fd393c2bc89619c0abed17e36b30730f55422761
[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.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
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             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     alignEl : false, // when show is called with an element - this get's stored.
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         this.alignEl = Roo.get( on_el );
19881
19882         if (!this.el) {
19883             this.render(document.body);
19884         }
19885         
19886         
19887          
19888         
19889         if (this.title === false) {
19890             this.headerEl.hide();
19891         }
19892         
19893        
19894         this.el.show();
19895         this.el.dom.style.display = 'block';
19896          
19897  
19898         if (this.alignEl) {
19899             this.updatePosition(this.placement, true);
19900              
19901         } else {
19902             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19903             var es = this.el.getSize();
19904             var x = Roo.lib.Dom.getViewWidth()/2;
19905             var y = Roo.lib.Dom.getViewHeight()/2;
19906             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19907             
19908         }
19909
19910         
19911         //var arrow = this.el.select('.arrow',true).first();
19912         //arrow.set(align[2], 
19913         
19914         this.el.addClass('in');
19915         
19916          
19917         
19918         this.hoverState = 'in';
19919         
19920         if (this.modal) {
19921             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19922             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19923             this.maskEl.dom.style.display = 'block';
19924             this.maskEl.addClass('show');
19925         }
19926         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19927  
19928         this.fireEvent('show', this);
19929         
19930     },
19931     /**
19932      * fire this manually after loading a grid in the table for example
19933      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19934      * @param {Boolean} try and move it if we cant get right position.
19935      */
19936     updatePosition : function(placement, try_move)
19937     {
19938         // allow for calling with no parameters
19939         placement = placement   ? placement :  this.placement;
19940         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19941         
19942         this.el.removeClass([
19943             'fade','top','bottom', 'left', 'right','in',
19944             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19945         ]);
19946         this.el.addClass(placement + ' bs-popover-' + placement);
19947         
19948         if (!this.alignEl ) {
19949             return false;
19950         }
19951         
19952         switch (placement) {
19953             case 'right':
19954                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19955                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19956                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19957                     //normal display... or moved up/down.
19958                     this.el.setXY(offset);
19959                     var xy = this.alignEl.getAnchorXY('tr', false);
19960                     xy[0]+=2;xy[1]+=5;
19961                     this.arrowEl.setXY(xy);
19962                     return true;
19963                 }
19964                 // continue through...
19965                 return this.updatePosition('left', false);
19966                 
19967             
19968             case 'left':
19969                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19970                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19971                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19972                     //normal display... or moved up/down.
19973                     this.el.setXY(offset);
19974                     var xy = this.alignEl.getAnchorXY('tl', false);
19975                     xy[0]-=10;xy[1]+=5; // << fix me
19976                     this.arrowEl.setXY(xy);
19977                     return true;
19978                 }
19979                 // call self...
19980                 return this.updatePosition('right', false);
19981             
19982             case 'top':
19983                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
19984                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
19985                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19986                     //normal display... or moved up/down.
19987                     this.el.setXY(offset);
19988                     var xy = this.alignEl.getAnchorXY('t', false);
19989                     xy[1]-=10; // << fix me
19990                     this.arrowEl.setXY(xy);
19991                     return true;
19992                 }
19993                 // fall through
19994                return this.updatePosition('bottom', false);
19995             
19996             case 'bottom':
19997                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
19998                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
19999                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20000                     //normal display... or moved up/down.
20001                     this.el.setXY(offset);
20002                     var xy = this.alignEl.getAnchorXY('b', false);
20003                      xy[1]+=2; // << fix me
20004                     this.arrowEl.setXY(xy);
20005                     return true;
20006                 }
20007                 // fall through
20008                 return this.updatePosition('top', false);
20009                 
20010             
20011         }
20012         
20013         
20014         return false;
20015     },
20016     
20017     hide : function()
20018     {
20019         this.el.setXY([0,0]);
20020         this.el.removeClass('in');
20021         this.el.hide();
20022         this.hoverState = null;
20023         this.maskEl.hide(); // always..
20024         this.fireEvent('hide', this);
20025     }
20026     
20027 });
20028
20029
20030 Roo.apply(Roo.bootstrap.Popover, {
20031
20032     alignment : {
20033         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20034         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20035         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20036         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20037     },
20038     
20039     zIndex : 20001,
20040
20041     clickHander : false,
20042     
20043
20044     onMouseDown : function(e)
20045     {
20046         if (!e.getTarget(".roo-popover")) {
20047             this.hideAll();
20048         }
20049          
20050     },
20051     
20052     popups : [],
20053     
20054     register : function(popup)
20055     {
20056         if (!Roo.bootstrap.Popover.clickHandler) {
20057             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20058         }
20059         // hide other popups.
20060         this.hideAll();
20061         this.popups.push(popup);
20062     },
20063     hideAll : function()
20064     {
20065         this.popups.forEach(function(p) {
20066             p.hide();
20067         });
20068     }
20069
20070 });/*
20071  * - LGPL
20072  *
20073  * Card header - holder for the card header elements.
20074  * 
20075  */
20076
20077 /**
20078  * @class Roo.bootstrap.PopoverNav
20079  * @extends Roo.bootstrap.NavGroup
20080  * Bootstrap Popover header navigation class
20081  * @constructor
20082  * Create a new Popover Header Navigation 
20083  * @param {Object} config The config object
20084  */
20085
20086 Roo.bootstrap.PopoverNav = function(config){
20087     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20088 };
20089
20090 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20091     
20092     
20093     container_method : 'getPopoverHeader' 
20094     
20095      
20096     
20097     
20098    
20099 });
20100
20101  
20102
20103  /*
20104  * - LGPL
20105  *
20106  * Progress
20107  * 
20108  */
20109
20110 /**
20111  * @class Roo.bootstrap.Progress
20112  * @extends Roo.bootstrap.Component
20113  * Bootstrap Progress class
20114  * @cfg {Boolean} striped striped of the progress bar
20115  * @cfg {Boolean} active animated of the progress bar
20116  * 
20117  * 
20118  * @constructor
20119  * Create a new Progress
20120  * @param {Object} config The config object
20121  */
20122
20123 Roo.bootstrap.Progress = function(config){
20124     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20125 };
20126
20127 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20128     
20129     striped : false,
20130     active: false,
20131     
20132     getAutoCreate : function(){
20133         var cfg = {
20134             tag: 'div',
20135             cls: 'progress'
20136         };
20137         
20138         
20139         if(this.striped){
20140             cfg.cls += ' progress-striped';
20141         }
20142       
20143         if(this.active){
20144             cfg.cls += ' active';
20145         }
20146         
20147         
20148         return cfg;
20149     }
20150    
20151 });
20152
20153  
20154
20155  /*
20156  * - LGPL
20157  *
20158  * ProgressBar
20159  * 
20160  */
20161
20162 /**
20163  * @class Roo.bootstrap.ProgressBar
20164  * @extends Roo.bootstrap.Component
20165  * Bootstrap ProgressBar class
20166  * @cfg {Number} aria_valuenow aria-value now
20167  * @cfg {Number} aria_valuemin aria-value min
20168  * @cfg {Number} aria_valuemax aria-value max
20169  * @cfg {String} label label for the progress bar
20170  * @cfg {String} panel (success | info | warning | danger )
20171  * @cfg {String} role role of the progress bar
20172  * @cfg {String} sr_only text
20173  * 
20174  * 
20175  * @constructor
20176  * Create a new ProgressBar
20177  * @param {Object} config The config object
20178  */
20179
20180 Roo.bootstrap.ProgressBar = function(config){
20181     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20182 };
20183
20184 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20185     
20186     aria_valuenow : 0,
20187     aria_valuemin : 0,
20188     aria_valuemax : 100,
20189     label : false,
20190     panel : false,
20191     role : false,
20192     sr_only: false,
20193     
20194     getAutoCreate : function()
20195     {
20196         
20197         var cfg = {
20198             tag: 'div',
20199             cls: 'progress-bar',
20200             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20201         };
20202         
20203         if(this.sr_only){
20204             cfg.cn = {
20205                 tag: 'span',
20206                 cls: 'sr-only',
20207                 html: this.sr_only
20208             }
20209         }
20210         
20211         if(this.role){
20212             cfg.role = this.role;
20213         }
20214         
20215         if(this.aria_valuenow){
20216             cfg['aria-valuenow'] = this.aria_valuenow;
20217         }
20218         
20219         if(this.aria_valuemin){
20220             cfg['aria-valuemin'] = this.aria_valuemin;
20221         }
20222         
20223         if(this.aria_valuemax){
20224             cfg['aria-valuemax'] = this.aria_valuemax;
20225         }
20226         
20227         if(this.label && !this.sr_only){
20228             cfg.html = this.label;
20229         }
20230         
20231         if(this.panel){
20232             cfg.cls += ' progress-bar-' + this.panel;
20233         }
20234         
20235         return cfg;
20236     },
20237     
20238     update : function(aria_valuenow)
20239     {
20240         this.aria_valuenow = aria_valuenow;
20241         
20242         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20243     }
20244    
20245 });
20246
20247  
20248
20249  /*
20250  * - LGPL
20251  *
20252  * column
20253  * 
20254  */
20255
20256 /**
20257  * @class Roo.bootstrap.TabGroup
20258  * @extends Roo.bootstrap.Column
20259  * Bootstrap Column class
20260  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20261  * @cfg {Boolean} carousel true to make the group behave like a carousel
20262  * @cfg {Boolean} bullets show bullets for the panels
20263  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20264  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20265  * @cfg {Boolean} showarrow (true|false) show arrow default true
20266  * 
20267  * @constructor
20268  * Create a new TabGroup
20269  * @param {Object} config The config object
20270  */
20271
20272 Roo.bootstrap.TabGroup = function(config){
20273     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20274     if (!this.navId) {
20275         this.navId = Roo.id();
20276     }
20277     this.tabs = [];
20278     Roo.bootstrap.TabGroup.register(this);
20279     
20280 };
20281
20282 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20283     
20284     carousel : false,
20285     transition : false,
20286     bullets : 0,
20287     timer : 0,
20288     autoslide : false,
20289     slideFn : false,
20290     slideOnTouch : false,
20291     showarrow : true,
20292     
20293     getAutoCreate : function()
20294     {
20295         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20296         
20297         cfg.cls += ' tab-content';
20298         
20299         if (this.carousel) {
20300             cfg.cls += ' carousel slide';
20301             
20302             cfg.cn = [{
20303                cls : 'carousel-inner',
20304                cn : []
20305             }];
20306         
20307             if(this.bullets  && !Roo.isTouch){
20308                 
20309                 var bullets = {
20310                     cls : 'carousel-bullets',
20311                     cn : []
20312                 };
20313                
20314                 if(this.bullets_cls){
20315                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20316                 }
20317                 
20318                 bullets.cn.push({
20319                     cls : 'clear'
20320                 });
20321                 
20322                 cfg.cn[0].cn.push(bullets);
20323             }
20324             
20325             if(this.showarrow){
20326                 cfg.cn[0].cn.push({
20327                     tag : 'div',
20328                     class : 'carousel-arrow',
20329                     cn : [
20330                         {
20331                             tag : 'div',
20332                             class : 'carousel-prev',
20333                             cn : [
20334                                 {
20335                                     tag : 'i',
20336                                     class : 'fa fa-chevron-left'
20337                                 }
20338                             ]
20339                         },
20340                         {
20341                             tag : 'div',
20342                             class : 'carousel-next',
20343                             cn : [
20344                                 {
20345                                     tag : 'i',
20346                                     class : 'fa fa-chevron-right'
20347                                 }
20348                             ]
20349                         }
20350                     ]
20351                 });
20352             }
20353             
20354         }
20355         
20356         return cfg;
20357     },
20358     
20359     initEvents:  function()
20360     {
20361 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20362 //            this.el.on("touchstart", this.onTouchStart, this);
20363 //        }
20364         
20365         if(this.autoslide){
20366             var _this = this;
20367             
20368             this.slideFn = window.setInterval(function() {
20369                 _this.showPanelNext();
20370             }, this.timer);
20371         }
20372         
20373         if(this.showarrow){
20374             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20375             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20376         }
20377         
20378         
20379     },
20380     
20381 //    onTouchStart : function(e, el, o)
20382 //    {
20383 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20384 //            return;
20385 //        }
20386 //        
20387 //        this.showPanelNext();
20388 //    },
20389     
20390     
20391     getChildContainer : function()
20392     {
20393         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20394     },
20395     
20396     /**
20397     * register a Navigation item
20398     * @param {Roo.bootstrap.NavItem} the navitem to add
20399     */
20400     register : function(item)
20401     {
20402         this.tabs.push( item);
20403         item.navId = this.navId; // not really needed..
20404         this.addBullet();
20405     
20406     },
20407     
20408     getActivePanel : function()
20409     {
20410         var r = false;
20411         Roo.each(this.tabs, function(t) {
20412             if (t.active) {
20413                 r = t;
20414                 return false;
20415             }
20416             return null;
20417         });
20418         return r;
20419         
20420     },
20421     getPanelByName : function(n)
20422     {
20423         var r = false;
20424         Roo.each(this.tabs, function(t) {
20425             if (t.tabId == n) {
20426                 r = t;
20427                 return false;
20428             }
20429             return null;
20430         });
20431         return r;
20432     },
20433     indexOfPanel : function(p)
20434     {
20435         var r = false;
20436         Roo.each(this.tabs, function(t,i) {
20437             if (t.tabId == p.tabId) {
20438                 r = i;
20439                 return false;
20440             }
20441             return null;
20442         });
20443         return r;
20444     },
20445     /**
20446      * show a specific panel
20447      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20448      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20449      */
20450     showPanel : function (pan)
20451     {
20452         if(this.transition || typeof(pan) == 'undefined'){
20453             Roo.log("waiting for the transitionend");
20454             return false;
20455         }
20456         
20457         if (typeof(pan) == 'number') {
20458             pan = this.tabs[pan];
20459         }
20460         
20461         if (typeof(pan) == 'string') {
20462             pan = this.getPanelByName(pan);
20463         }
20464         
20465         var cur = this.getActivePanel();
20466         
20467         if(!pan || !cur){
20468             Roo.log('pan or acitve pan is undefined');
20469             return false;
20470         }
20471         
20472         if (pan.tabId == this.getActivePanel().tabId) {
20473             return true;
20474         }
20475         
20476         if (false === cur.fireEvent('beforedeactivate')) {
20477             return false;
20478         }
20479         
20480         if(this.bullets > 0 && !Roo.isTouch){
20481             this.setActiveBullet(this.indexOfPanel(pan));
20482         }
20483         
20484         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20485             
20486             //class="carousel-item carousel-item-next carousel-item-left"
20487             
20488             this.transition = true;
20489             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20490             var lr = dir == 'next' ? 'left' : 'right';
20491             pan.el.addClass(dir); // or prev
20492             pan.el.addClass('carousel-item-' + dir); // or prev
20493             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20494             cur.el.addClass(lr); // or right
20495             pan.el.addClass(lr);
20496             cur.el.addClass('carousel-item-' +lr); // or right
20497             pan.el.addClass('carousel-item-' +lr);
20498             
20499             
20500             var _this = this;
20501             cur.el.on('transitionend', function() {
20502                 Roo.log("trans end?");
20503                 
20504                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20505                 pan.setActive(true);
20506                 
20507                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20508                 cur.setActive(false);
20509                 
20510                 _this.transition = false;
20511                 
20512             }, this, { single:  true } );
20513             
20514             return true;
20515         }
20516         
20517         cur.setActive(false);
20518         pan.setActive(true);
20519         
20520         return true;
20521         
20522     },
20523     showPanelNext : function()
20524     {
20525         var i = this.indexOfPanel(this.getActivePanel());
20526         
20527         if (i >= this.tabs.length - 1 && !this.autoslide) {
20528             return;
20529         }
20530         
20531         if (i >= this.tabs.length - 1 && this.autoslide) {
20532             i = -1;
20533         }
20534         
20535         this.showPanel(this.tabs[i+1]);
20536     },
20537     
20538     showPanelPrev : function()
20539     {
20540         var i = this.indexOfPanel(this.getActivePanel());
20541         
20542         if (i  < 1 && !this.autoslide) {
20543             return;
20544         }
20545         
20546         if (i < 1 && this.autoslide) {
20547             i = this.tabs.length;
20548         }
20549         
20550         this.showPanel(this.tabs[i-1]);
20551     },
20552     
20553     
20554     addBullet: function()
20555     {
20556         if(!this.bullets || Roo.isTouch){
20557             return;
20558         }
20559         var ctr = this.el.select('.carousel-bullets',true).first();
20560         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20561         var bullet = ctr.createChild({
20562             cls : 'bullet bullet-' + i
20563         },ctr.dom.lastChild);
20564         
20565         
20566         var _this = this;
20567         
20568         bullet.on('click', (function(e, el, o, ii, t){
20569
20570             e.preventDefault();
20571
20572             this.showPanel(ii);
20573
20574             if(this.autoslide && this.slideFn){
20575                 clearInterval(this.slideFn);
20576                 this.slideFn = window.setInterval(function() {
20577                     _this.showPanelNext();
20578                 }, this.timer);
20579             }
20580
20581         }).createDelegate(this, [i, bullet], true));
20582                 
20583         
20584     },
20585      
20586     setActiveBullet : function(i)
20587     {
20588         if(Roo.isTouch){
20589             return;
20590         }
20591         
20592         Roo.each(this.el.select('.bullet', true).elements, function(el){
20593             el.removeClass('selected');
20594         });
20595
20596         var bullet = this.el.select('.bullet-' + i, true).first();
20597         
20598         if(!bullet){
20599             return;
20600         }
20601         
20602         bullet.addClass('selected');
20603     }
20604     
20605     
20606   
20607 });
20608
20609  
20610
20611  
20612  
20613 Roo.apply(Roo.bootstrap.TabGroup, {
20614     
20615     groups: {},
20616      /**
20617     * register a Navigation Group
20618     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20619     */
20620     register : function(navgrp)
20621     {
20622         this.groups[navgrp.navId] = navgrp;
20623         
20624     },
20625     /**
20626     * fetch a Navigation Group based on the navigation ID
20627     * if one does not exist , it will get created.
20628     * @param {string} the navgroup to add
20629     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20630     */
20631     get: function(navId) {
20632         if (typeof(this.groups[navId]) == 'undefined') {
20633             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20634         }
20635         return this.groups[navId] ;
20636     }
20637     
20638     
20639     
20640 });
20641
20642  /*
20643  * - LGPL
20644  *
20645  * TabPanel
20646  * 
20647  */
20648
20649 /**
20650  * @class Roo.bootstrap.TabPanel
20651  * @extends Roo.bootstrap.Component
20652  * Bootstrap TabPanel class
20653  * @cfg {Boolean} active panel active
20654  * @cfg {String} html panel content
20655  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20656  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20657  * @cfg {String} href click to link..
20658  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20659  * 
20660  * 
20661  * @constructor
20662  * Create a new TabPanel
20663  * @param {Object} config The config object
20664  */
20665
20666 Roo.bootstrap.TabPanel = function(config){
20667     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20668     this.addEvents({
20669         /**
20670              * @event changed
20671              * Fires when the active status changes
20672              * @param {Roo.bootstrap.TabPanel} this
20673              * @param {Boolean} state the new state
20674             
20675          */
20676         'changed': true,
20677         /**
20678              * @event beforedeactivate
20679              * Fires before a tab is de-activated - can be used to do validation on a form.
20680              * @param {Roo.bootstrap.TabPanel} this
20681              * @return {Boolean} false if there is an error
20682             
20683          */
20684         'beforedeactivate': true
20685      });
20686     
20687     this.tabId = this.tabId || Roo.id();
20688   
20689 };
20690
20691 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20692     
20693     active: false,
20694     html: false,
20695     tabId: false,
20696     navId : false,
20697     href : '',
20698     touchSlide : false,
20699     getAutoCreate : function(){
20700         
20701         
20702         var cfg = {
20703             tag: 'div',
20704             // item is needed for carousel - not sure if it has any effect otherwise
20705             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20706             html: this.html || ''
20707         };
20708         
20709         if(this.active){
20710             cfg.cls += ' active';
20711         }
20712         
20713         if(this.tabId){
20714             cfg.tabId = this.tabId;
20715         }
20716         
20717         
20718         
20719         return cfg;
20720     },
20721     
20722     initEvents:  function()
20723     {
20724         var p = this.parent();
20725         
20726         this.navId = this.navId || p.navId;
20727         
20728         if (typeof(this.navId) != 'undefined') {
20729             // not really needed.. but just in case.. parent should be a NavGroup.
20730             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20731             
20732             tg.register(this);
20733             
20734             var i = tg.tabs.length - 1;
20735             
20736             if(this.active && tg.bullets > 0 && i < tg.bullets){
20737                 tg.setActiveBullet(i);
20738             }
20739         }
20740         
20741         this.el.on('click', this.onClick, this);
20742         
20743         if(Roo.isTouch && this.touchSlide){
20744             this.el.on("touchstart", this.onTouchStart, this);
20745             this.el.on("touchmove", this.onTouchMove, this);
20746             this.el.on("touchend", this.onTouchEnd, this);
20747         }
20748         
20749     },
20750     
20751     onRender : function(ct, position)
20752     {
20753         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20754     },
20755     
20756     setActive : function(state)
20757     {
20758         Roo.log("panel - set active " + this.tabId + "=" + state);
20759         
20760         this.active = state;
20761         if (!state) {
20762             this.el.removeClass('active');
20763             
20764         } else  if (!this.el.hasClass('active')) {
20765             this.el.addClass('active');
20766         }
20767         
20768         this.fireEvent('changed', this, state);
20769     },
20770     
20771     onClick : function(e)
20772     {
20773         e.preventDefault();
20774         
20775         if(!this.href.length){
20776             return;
20777         }
20778         
20779         window.location.href = this.href;
20780     },
20781     
20782     startX : 0,
20783     startY : 0,
20784     endX : 0,
20785     endY : 0,
20786     swiping : false,
20787     
20788     onTouchStart : function(e)
20789     {
20790         this.swiping = false;
20791         
20792         this.startX = e.browserEvent.touches[0].clientX;
20793         this.startY = e.browserEvent.touches[0].clientY;
20794     },
20795     
20796     onTouchMove : function(e)
20797     {
20798         this.swiping = true;
20799         
20800         this.endX = e.browserEvent.touches[0].clientX;
20801         this.endY = e.browserEvent.touches[0].clientY;
20802     },
20803     
20804     onTouchEnd : function(e)
20805     {
20806         if(!this.swiping){
20807             this.onClick(e);
20808             return;
20809         }
20810         
20811         var tabGroup = this.parent();
20812         
20813         if(this.endX > this.startX){ // swiping right
20814             tabGroup.showPanelPrev();
20815             return;
20816         }
20817         
20818         if(this.startX > this.endX){ // swiping left
20819             tabGroup.showPanelNext();
20820             return;
20821         }
20822     }
20823     
20824     
20825 });
20826  
20827
20828  
20829
20830  /*
20831  * - LGPL
20832  *
20833  * DateField
20834  * 
20835  */
20836
20837 /**
20838  * @class Roo.bootstrap.DateField
20839  * @extends Roo.bootstrap.Input
20840  * Bootstrap DateField class
20841  * @cfg {Number} weekStart default 0
20842  * @cfg {String} viewMode default empty, (months|years)
20843  * @cfg {String} minViewMode default empty, (months|years)
20844  * @cfg {Number} startDate default -Infinity
20845  * @cfg {Number} endDate default Infinity
20846  * @cfg {Boolean} todayHighlight default false
20847  * @cfg {Boolean} todayBtn default false
20848  * @cfg {Boolean} calendarWeeks default false
20849  * @cfg {Object} daysOfWeekDisabled default empty
20850  * @cfg {Boolean} singleMode default false (true | false)
20851  * 
20852  * @cfg {Boolean} keyboardNavigation default true
20853  * @cfg {String} language default en
20854  * 
20855  * @constructor
20856  * Create a new DateField
20857  * @param {Object} config The config object
20858  */
20859
20860 Roo.bootstrap.DateField = function(config){
20861     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20862      this.addEvents({
20863             /**
20864              * @event show
20865              * Fires when this field show.
20866              * @param {Roo.bootstrap.DateField} this
20867              * @param {Mixed} date The date value
20868              */
20869             show : true,
20870             /**
20871              * @event show
20872              * Fires when this field hide.
20873              * @param {Roo.bootstrap.DateField} this
20874              * @param {Mixed} date The date value
20875              */
20876             hide : true,
20877             /**
20878              * @event select
20879              * Fires when select a date.
20880              * @param {Roo.bootstrap.DateField} this
20881              * @param {Mixed} date The date value
20882              */
20883             select : true,
20884             /**
20885              * @event beforeselect
20886              * Fires when before select a date.
20887              * @param {Roo.bootstrap.DateField} this
20888              * @param {Mixed} date The date value
20889              */
20890             beforeselect : true
20891         });
20892 };
20893
20894 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20895     
20896     /**
20897      * @cfg {String} format
20898      * The default date format string which can be overriden for localization support.  The format must be
20899      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20900      */
20901     format : "m/d/y",
20902     /**
20903      * @cfg {String} altFormats
20904      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20905      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20906      */
20907     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20908     
20909     weekStart : 0,
20910     
20911     viewMode : '',
20912     
20913     minViewMode : '',
20914     
20915     todayHighlight : false,
20916     
20917     todayBtn: false,
20918     
20919     language: 'en',
20920     
20921     keyboardNavigation: true,
20922     
20923     calendarWeeks: false,
20924     
20925     startDate: -Infinity,
20926     
20927     endDate: Infinity,
20928     
20929     daysOfWeekDisabled: [],
20930     
20931     _events: [],
20932     
20933     singleMode : false,
20934     
20935     UTCDate: function()
20936     {
20937         return new Date(Date.UTC.apply(Date, arguments));
20938     },
20939     
20940     UTCToday: function()
20941     {
20942         var today = new Date();
20943         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20944     },
20945     
20946     getDate: function() {
20947             var d = this.getUTCDate();
20948             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20949     },
20950     
20951     getUTCDate: function() {
20952             return this.date;
20953     },
20954     
20955     setDate: function(d) {
20956             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20957     },
20958     
20959     setUTCDate: function(d) {
20960             this.date = d;
20961             this.setValue(this.formatDate(this.date));
20962     },
20963         
20964     onRender: function(ct, position)
20965     {
20966         
20967         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20968         
20969         this.language = this.language || 'en';
20970         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20971         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20972         
20973         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20974         this.format = this.format || 'm/d/y';
20975         this.isInline = false;
20976         this.isInput = true;
20977         this.component = this.el.select('.add-on', true).first() || false;
20978         this.component = (this.component && this.component.length === 0) ? false : this.component;
20979         this.hasInput = this.component && this.inputEl().length;
20980         
20981         if (typeof(this.minViewMode === 'string')) {
20982             switch (this.minViewMode) {
20983                 case 'months':
20984                     this.minViewMode = 1;
20985                     break;
20986                 case 'years':
20987                     this.minViewMode = 2;
20988                     break;
20989                 default:
20990                     this.minViewMode = 0;
20991                     break;
20992             }
20993         }
20994         
20995         if (typeof(this.viewMode === 'string')) {
20996             switch (this.viewMode) {
20997                 case 'months':
20998                     this.viewMode = 1;
20999                     break;
21000                 case 'years':
21001                     this.viewMode = 2;
21002                     break;
21003                 default:
21004                     this.viewMode = 0;
21005                     break;
21006             }
21007         }
21008                 
21009         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21010         
21011 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21012         
21013         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21014         
21015         this.picker().on('mousedown', this.onMousedown, this);
21016         this.picker().on('click', this.onClick, this);
21017         
21018         this.picker().addClass('datepicker-dropdown');
21019         
21020         this.startViewMode = this.viewMode;
21021         
21022         if(this.singleMode){
21023             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21024                 v.setVisibilityMode(Roo.Element.DISPLAY);
21025                 v.hide();
21026             });
21027             
21028             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21029                 v.setStyle('width', '189px');
21030             });
21031         }
21032         
21033         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21034             if(!this.calendarWeeks){
21035                 v.remove();
21036                 return;
21037             }
21038             
21039             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21040             v.attr('colspan', function(i, val){
21041                 return parseInt(val) + 1;
21042             });
21043         });
21044                         
21045         
21046         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21047         
21048         this.setStartDate(this.startDate);
21049         this.setEndDate(this.endDate);
21050         
21051         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21052         
21053         this.fillDow();
21054         this.fillMonths();
21055         this.update();
21056         this.showMode();
21057         
21058         if(this.isInline) {
21059             this.showPopup();
21060         }
21061     },
21062     
21063     picker : function()
21064     {
21065         return this.pickerEl;
21066 //        return this.el.select('.datepicker', true).first();
21067     },
21068     
21069     fillDow: function()
21070     {
21071         var dowCnt = this.weekStart;
21072         
21073         var dow = {
21074             tag: 'tr',
21075             cn: [
21076                 
21077             ]
21078         };
21079         
21080         if(this.calendarWeeks){
21081             dow.cn.push({
21082                 tag: 'th',
21083                 cls: 'cw',
21084                 html: '&nbsp;'
21085             })
21086         }
21087         
21088         while (dowCnt < this.weekStart + 7) {
21089             dow.cn.push({
21090                 tag: 'th',
21091                 cls: 'dow',
21092                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21093             });
21094         }
21095         
21096         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21097     },
21098     
21099     fillMonths: function()
21100     {    
21101         var i = 0;
21102         var months = this.picker().select('>.datepicker-months td', true).first();
21103         
21104         months.dom.innerHTML = '';
21105         
21106         while (i < 12) {
21107             var month = {
21108                 tag: 'span',
21109                 cls: 'month',
21110                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21111             };
21112             
21113             months.createChild(month);
21114         }
21115         
21116     },
21117     
21118     update: function()
21119     {
21120         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;
21121         
21122         if (this.date < this.startDate) {
21123             this.viewDate = new Date(this.startDate);
21124         } else if (this.date > this.endDate) {
21125             this.viewDate = new Date(this.endDate);
21126         } else {
21127             this.viewDate = new Date(this.date);
21128         }
21129         
21130         this.fill();
21131     },
21132     
21133     fill: function() 
21134     {
21135         var d = new Date(this.viewDate),
21136                 year = d.getUTCFullYear(),
21137                 month = d.getUTCMonth(),
21138                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21139                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21140                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21141                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21142                 currentDate = this.date && this.date.valueOf(),
21143                 today = this.UTCToday();
21144         
21145         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21146         
21147 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21148         
21149 //        this.picker.select('>tfoot th.today').
21150 //                                              .text(dates[this.language].today)
21151 //                                              .toggle(this.todayBtn !== false);
21152     
21153         this.updateNavArrows();
21154         this.fillMonths();
21155                                                 
21156         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21157         
21158         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21159          
21160         prevMonth.setUTCDate(day);
21161         
21162         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21163         
21164         var nextMonth = new Date(prevMonth);
21165         
21166         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21167         
21168         nextMonth = nextMonth.valueOf();
21169         
21170         var fillMonths = false;
21171         
21172         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21173         
21174         while(prevMonth.valueOf() <= nextMonth) {
21175             var clsName = '';
21176             
21177             if (prevMonth.getUTCDay() === this.weekStart) {
21178                 if(fillMonths){
21179                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21180                 }
21181                     
21182                 fillMonths = {
21183                     tag: 'tr',
21184                     cn: []
21185                 };
21186                 
21187                 if(this.calendarWeeks){
21188                     // ISO 8601: First week contains first thursday.
21189                     // ISO also states week starts on Monday, but we can be more abstract here.
21190                     var
21191                     // Start of current week: based on weekstart/current date
21192                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21193                     // Thursday of this week
21194                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21195                     // First Thursday of year, year from thursday
21196                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21197                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21198                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21199                     
21200                     fillMonths.cn.push({
21201                         tag: 'td',
21202                         cls: 'cw',
21203                         html: calWeek
21204                     });
21205                 }
21206             }
21207             
21208             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21209                 clsName += ' old';
21210             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21211                 clsName += ' new';
21212             }
21213             if (this.todayHighlight &&
21214                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21215                 prevMonth.getUTCMonth() == today.getMonth() &&
21216                 prevMonth.getUTCDate() == today.getDate()) {
21217                 clsName += ' today';
21218             }
21219             
21220             if (currentDate && prevMonth.valueOf() === currentDate) {
21221                 clsName += ' active';
21222             }
21223             
21224             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21225                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21226                     clsName += ' disabled';
21227             }
21228             
21229             fillMonths.cn.push({
21230                 tag: 'td',
21231                 cls: 'day ' + clsName,
21232                 html: prevMonth.getDate()
21233             });
21234             
21235             prevMonth.setDate(prevMonth.getDate()+1);
21236         }
21237           
21238         var currentYear = this.date && this.date.getUTCFullYear();
21239         var currentMonth = this.date && this.date.getUTCMonth();
21240         
21241         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21242         
21243         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21244             v.removeClass('active');
21245             
21246             if(currentYear === year && k === currentMonth){
21247                 v.addClass('active');
21248             }
21249             
21250             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21251                 v.addClass('disabled');
21252             }
21253             
21254         });
21255         
21256         
21257         year = parseInt(year/10, 10) * 10;
21258         
21259         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21260         
21261         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21262         
21263         year -= 1;
21264         for (var i = -1; i < 11; i++) {
21265             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21266                 tag: 'span',
21267                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21268                 html: year
21269             });
21270             
21271             year += 1;
21272         }
21273     },
21274     
21275     showMode: function(dir) 
21276     {
21277         if (dir) {
21278             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21279         }
21280         
21281         Roo.each(this.picker().select('>div',true).elements, function(v){
21282             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21283             v.hide();
21284         });
21285         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21286     },
21287     
21288     place: function()
21289     {
21290         if(this.isInline) {
21291             return;
21292         }
21293         
21294         this.picker().removeClass(['bottom', 'top']);
21295         
21296         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21297             /*
21298              * place to the top of element!
21299              *
21300              */
21301             
21302             this.picker().addClass('top');
21303             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21304             
21305             return;
21306         }
21307         
21308         this.picker().addClass('bottom');
21309         
21310         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21311     },
21312     
21313     parseDate : function(value)
21314     {
21315         if(!value || value instanceof Date){
21316             return value;
21317         }
21318         var v = Date.parseDate(value, this.format);
21319         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21320             v = Date.parseDate(value, 'Y-m-d');
21321         }
21322         if(!v && this.altFormats){
21323             if(!this.altFormatsArray){
21324                 this.altFormatsArray = this.altFormats.split("|");
21325             }
21326             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21327                 v = Date.parseDate(value, this.altFormatsArray[i]);
21328             }
21329         }
21330         return v;
21331     },
21332     
21333     formatDate : function(date, fmt)
21334     {   
21335         return (!date || !(date instanceof Date)) ?
21336         date : date.dateFormat(fmt || this.format);
21337     },
21338     
21339     onFocus : function()
21340     {
21341         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21342         this.showPopup();
21343     },
21344     
21345     onBlur : function()
21346     {
21347         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21348         
21349         var d = this.inputEl().getValue();
21350         
21351         this.setValue(d);
21352                 
21353         this.hidePopup();
21354     },
21355     
21356     showPopup : function()
21357     {
21358         this.picker().show();
21359         this.update();
21360         this.place();
21361         
21362         this.fireEvent('showpopup', this, this.date);
21363     },
21364     
21365     hidePopup : function()
21366     {
21367         if(this.isInline) {
21368             return;
21369         }
21370         this.picker().hide();
21371         this.viewMode = this.startViewMode;
21372         this.showMode();
21373         
21374         this.fireEvent('hidepopup', this, this.date);
21375         
21376     },
21377     
21378     onMousedown: function(e)
21379     {
21380         e.stopPropagation();
21381         e.preventDefault();
21382     },
21383     
21384     keyup: function(e)
21385     {
21386         Roo.bootstrap.DateField.superclass.keyup.call(this);
21387         this.update();
21388     },
21389
21390     setValue: function(v)
21391     {
21392         if(this.fireEvent('beforeselect', this, v) !== false){
21393             var d = new Date(this.parseDate(v) ).clearTime();
21394         
21395             if(isNaN(d.getTime())){
21396                 this.date = this.viewDate = '';
21397                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21398                 return;
21399             }
21400
21401             v = this.formatDate(d);
21402
21403             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21404
21405             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21406
21407             this.update();
21408
21409             this.fireEvent('select', this, this.date);
21410         }
21411     },
21412     
21413     getValue: function()
21414     {
21415         return this.formatDate(this.date);
21416     },
21417     
21418     fireKey: function(e)
21419     {
21420         if (!this.picker().isVisible()){
21421             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21422                 this.showPopup();
21423             }
21424             return;
21425         }
21426         
21427         var dateChanged = false,
21428         dir, day, month,
21429         newDate, newViewDate;
21430         
21431         switch(e.keyCode){
21432             case 27: // escape
21433                 this.hidePopup();
21434                 e.preventDefault();
21435                 break;
21436             case 37: // left
21437             case 39: // right
21438                 if (!this.keyboardNavigation) {
21439                     break;
21440                 }
21441                 dir = e.keyCode == 37 ? -1 : 1;
21442                 
21443                 if (e.ctrlKey){
21444                     newDate = this.moveYear(this.date, dir);
21445                     newViewDate = this.moveYear(this.viewDate, dir);
21446                 } else if (e.shiftKey){
21447                     newDate = this.moveMonth(this.date, dir);
21448                     newViewDate = this.moveMonth(this.viewDate, dir);
21449                 } else {
21450                     newDate = new Date(this.date);
21451                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21452                     newViewDate = new Date(this.viewDate);
21453                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21454                 }
21455                 if (this.dateWithinRange(newDate)){
21456                     this.date = newDate;
21457                     this.viewDate = newViewDate;
21458                     this.setValue(this.formatDate(this.date));
21459 //                    this.update();
21460                     e.preventDefault();
21461                     dateChanged = true;
21462                 }
21463                 break;
21464             case 38: // up
21465             case 40: // down
21466                 if (!this.keyboardNavigation) {
21467                     break;
21468                 }
21469                 dir = e.keyCode == 38 ? -1 : 1;
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 * 7);
21479                     newViewDate = new Date(this.viewDate);
21480                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
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 13: // enter
21492                 this.setValue(this.formatDate(this.date));
21493                 this.hidePopup();
21494                 e.preventDefault();
21495                 break;
21496             case 9: // tab
21497                 this.setValue(this.formatDate(this.date));
21498                 this.hidePopup();
21499                 break;
21500             case 16: // shift
21501             case 17: // ctrl
21502             case 18: // alt
21503                 break;
21504             default :
21505                 this.hidePopup();
21506                 
21507         }
21508     },
21509     
21510     
21511     onClick: function(e) 
21512     {
21513         e.stopPropagation();
21514         e.preventDefault();
21515         
21516         var target = e.getTarget();
21517         
21518         if(target.nodeName.toLowerCase() === 'i'){
21519             target = Roo.get(target).dom.parentNode;
21520         }
21521         
21522         var nodeName = target.nodeName;
21523         var className = target.className;
21524         var html = target.innerHTML;
21525         //Roo.log(nodeName);
21526         
21527         switch(nodeName.toLowerCase()) {
21528             case 'th':
21529                 switch(className) {
21530                     case 'switch':
21531                         this.showMode(1);
21532                         break;
21533                     case 'prev':
21534                     case 'next':
21535                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21536                         switch(this.viewMode){
21537                                 case 0:
21538                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21539                                         break;
21540                                 case 1:
21541                                 case 2:
21542                                         this.viewDate = this.moveYear(this.viewDate, dir);
21543                                         break;
21544                         }
21545                         this.fill();
21546                         break;
21547                     case 'today':
21548                         var date = new Date();
21549                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21550 //                        this.fill()
21551                         this.setValue(this.formatDate(this.date));
21552                         
21553                         this.hidePopup();
21554                         break;
21555                 }
21556                 break;
21557             case 'span':
21558                 if (className.indexOf('disabled') < 0) {
21559                     this.viewDate.setUTCDate(1);
21560                     if (className.indexOf('month') > -1) {
21561                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21562                     } else {
21563                         var year = parseInt(html, 10) || 0;
21564                         this.viewDate.setUTCFullYear(year);
21565                         
21566                     }
21567                     
21568                     if(this.singleMode){
21569                         this.setValue(this.formatDate(this.viewDate));
21570                         this.hidePopup();
21571                         return;
21572                     }
21573                     
21574                     this.showMode(-1);
21575                     this.fill();
21576                 }
21577                 break;
21578                 
21579             case 'td':
21580                 //Roo.log(className);
21581                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21582                     var day = parseInt(html, 10) || 1;
21583                     var year = this.viewDate.getUTCFullYear(),
21584                         month = this.viewDate.getUTCMonth();
21585
21586                     if (className.indexOf('old') > -1) {
21587                         if(month === 0 ){
21588                             month = 11;
21589                             year -= 1;
21590                         }else{
21591                             month -= 1;
21592                         }
21593                     } else if (className.indexOf('new') > -1) {
21594                         if (month == 11) {
21595                             month = 0;
21596                             year += 1;
21597                         } else {
21598                             month += 1;
21599                         }
21600                     }
21601                     //Roo.log([year,month,day]);
21602                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21603                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21604 //                    this.fill();
21605                     //Roo.log(this.formatDate(this.date));
21606                     this.setValue(this.formatDate(this.date));
21607                     this.hidePopup();
21608                 }
21609                 break;
21610         }
21611     },
21612     
21613     setStartDate: function(startDate)
21614     {
21615         this.startDate = startDate || -Infinity;
21616         if (this.startDate !== -Infinity) {
21617             this.startDate = this.parseDate(this.startDate);
21618         }
21619         this.update();
21620         this.updateNavArrows();
21621     },
21622
21623     setEndDate: function(endDate)
21624     {
21625         this.endDate = endDate || Infinity;
21626         if (this.endDate !== Infinity) {
21627             this.endDate = this.parseDate(this.endDate);
21628         }
21629         this.update();
21630         this.updateNavArrows();
21631     },
21632     
21633     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21634     {
21635         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21636         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21637             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21638         }
21639         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21640             return parseInt(d, 10);
21641         });
21642         this.update();
21643         this.updateNavArrows();
21644     },
21645     
21646     updateNavArrows: function() 
21647     {
21648         if(this.singleMode){
21649             return;
21650         }
21651         
21652         var d = new Date(this.viewDate),
21653         year = d.getUTCFullYear(),
21654         month = d.getUTCMonth();
21655         
21656         Roo.each(this.picker().select('.prev', true).elements, function(v){
21657             v.show();
21658             switch (this.viewMode) {
21659                 case 0:
21660
21661                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21662                         v.hide();
21663                     }
21664                     break;
21665                 case 1:
21666                 case 2:
21667                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21668                         v.hide();
21669                     }
21670                     break;
21671             }
21672         });
21673         
21674         Roo.each(this.picker().select('.next', true).elements, function(v){
21675             v.show();
21676             switch (this.viewMode) {
21677                 case 0:
21678
21679                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21680                         v.hide();
21681                     }
21682                     break;
21683                 case 1:
21684                 case 2:
21685                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21686                         v.hide();
21687                     }
21688                     break;
21689             }
21690         })
21691     },
21692     
21693     moveMonth: function(date, dir)
21694     {
21695         if (!dir) {
21696             return date;
21697         }
21698         var new_date = new Date(date.valueOf()),
21699         day = new_date.getUTCDate(),
21700         month = new_date.getUTCMonth(),
21701         mag = Math.abs(dir),
21702         new_month, test;
21703         dir = dir > 0 ? 1 : -1;
21704         if (mag == 1){
21705             test = dir == -1
21706             // If going back one month, make sure month is not current month
21707             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21708             ? function(){
21709                 return new_date.getUTCMonth() == month;
21710             }
21711             // If going forward one month, make sure month is as expected
21712             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21713             : function(){
21714                 return new_date.getUTCMonth() != new_month;
21715             };
21716             new_month = month + dir;
21717             new_date.setUTCMonth(new_month);
21718             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21719             if (new_month < 0 || new_month > 11) {
21720                 new_month = (new_month + 12) % 12;
21721             }
21722         } else {
21723             // For magnitudes >1, move one month at a time...
21724             for (var i=0; i<mag; i++) {
21725                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21726                 new_date = this.moveMonth(new_date, dir);
21727             }
21728             // ...then reset the day, keeping it in the new month
21729             new_month = new_date.getUTCMonth();
21730             new_date.setUTCDate(day);
21731             test = function(){
21732                 return new_month != new_date.getUTCMonth();
21733             };
21734         }
21735         // Common date-resetting loop -- if date is beyond end of month, make it
21736         // end of month
21737         while (test()){
21738             new_date.setUTCDate(--day);
21739             new_date.setUTCMonth(new_month);
21740         }
21741         return new_date;
21742     },
21743
21744     moveYear: function(date, dir)
21745     {
21746         return this.moveMonth(date, dir*12);
21747     },
21748
21749     dateWithinRange: function(date)
21750     {
21751         return date >= this.startDate && date <= this.endDate;
21752     },
21753
21754     
21755     remove: function() 
21756     {
21757         this.picker().remove();
21758     },
21759     
21760     validateValue : function(value)
21761     {
21762         if(this.getVisibilityEl().hasClass('hidden')){
21763             return true;
21764         }
21765         
21766         if(value.length < 1)  {
21767             if(this.allowBlank){
21768                 return true;
21769             }
21770             return false;
21771         }
21772         
21773         if(value.length < this.minLength){
21774             return false;
21775         }
21776         if(value.length > this.maxLength){
21777             return false;
21778         }
21779         if(this.vtype){
21780             var vt = Roo.form.VTypes;
21781             if(!vt[this.vtype](value, this)){
21782                 return false;
21783             }
21784         }
21785         if(typeof this.validator == "function"){
21786             var msg = this.validator(value);
21787             if(msg !== true){
21788                 return false;
21789             }
21790         }
21791         
21792         if(this.regex && !this.regex.test(value)){
21793             return false;
21794         }
21795         
21796         if(typeof(this.parseDate(value)) == 'undefined'){
21797             return false;
21798         }
21799         
21800         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21801             return false;
21802         }      
21803         
21804         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21805             return false;
21806         } 
21807         
21808         
21809         return true;
21810     },
21811     
21812     reset : function()
21813     {
21814         this.date = this.viewDate = '';
21815         
21816         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21817     }
21818    
21819 });
21820
21821 Roo.apply(Roo.bootstrap.DateField,  {
21822     
21823     head : {
21824         tag: 'thead',
21825         cn: [
21826         {
21827             tag: 'tr',
21828             cn: [
21829             {
21830                 tag: 'th',
21831                 cls: 'prev',
21832                 html: '<i class="fa fa-arrow-left"/>'
21833             },
21834             {
21835                 tag: 'th',
21836                 cls: 'switch',
21837                 colspan: '5'
21838             },
21839             {
21840                 tag: 'th',
21841                 cls: 'next',
21842                 html: '<i class="fa fa-arrow-right"/>'
21843             }
21844
21845             ]
21846         }
21847         ]
21848     },
21849     
21850     content : {
21851         tag: 'tbody',
21852         cn: [
21853         {
21854             tag: 'tr',
21855             cn: [
21856             {
21857                 tag: 'td',
21858                 colspan: '7'
21859             }
21860             ]
21861         }
21862         ]
21863     },
21864     
21865     footer : {
21866         tag: 'tfoot',
21867         cn: [
21868         {
21869             tag: 'tr',
21870             cn: [
21871             {
21872                 tag: 'th',
21873                 colspan: '7',
21874                 cls: 'today'
21875             }
21876                     
21877             ]
21878         }
21879         ]
21880     },
21881     
21882     dates:{
21883         en: {
21884             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21885             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21886             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21887             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21888             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21889             today: "Today"
21890         }
21891     },
21892     
21893     modes: [
21894     {
21895         clsName: 'days',
21896         navFnc: 'Month',
21897         navStep: 1
21898     },
21899     {
21900         clsName: 'months',
21901         navFnc: 'FullYear',
21902         navStep: 1
21903     },
21904     {
21905         clsName: 'years',
21906         navFnc: 'FullYear',
21907         navStep: 10
21908     }]
21909 });
21910
21911 Roo.apply(Roo.bootstrap.DateField,  {
21912   
21913     template : {
21914         tag: 'div',
21915         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21916         cn: [
21917         {
21918             tag: 'div',
21919             cls: 'datepicker-days',
21920             cn: [
21921             {
21922                 tag: 'table',
21923                 cls: 'table-condensed',
21924                 cn:[
21925                 Roo.bootstrap.DateField.head,
21926                 {
21927                     tag: 'tbody'
21928                 },
21929                 Roo.bootstrap.DateField.footer
21930                 ]
21931             }
21932             ]
21933         },
21934         {
21935             tag: 'div',
21936             cls: 'datepicker-months',
21937             cn: [
21938             {
21939                 tag: 'table',
21940                 cls: 'table-condensed',
21941                 cn:[
21942                 Roo.bootstrap.DateField.head,
21943                 Roo.bootstrap.DateField.content,
21944                 Roo.bootstrap.DateField.footer
21945                 ]
21946             }
21947             ]
21948         },
21949         {
21950             tag: 'div',
21951             cls: 'datepicker-years',
21952             cn: [
21953             {
21954                 tag: 'table',
21955                 cls: 'table-condensed',
21956                 cn:[
21957                 Roo.bootstrap.DateField.head,
21958                 Roo.bootstrap.DateField.content,
21959                 Roo.bootstrap.DateField.footer
21960                 ]
21961             }
21962             ]
21963         }
21964         ]
21965     }
21966 });
21967
21968  
21969
21970  /*
21971  * - LGPL
21972  *
21973  * TimeField
21974  * 
21975  */
21976
21977 /**
21978  * @class Roo.bootstrap.TimeField
21979  * @extends Roo.bootstrap.Input
21980  * Bootstrap DateField class
21981  * 
21982  * 
21983  * @constructor
21984  * Create a new TimeField
21985  * @param {Object} config The config object
21986  */
21987
21988 Roo.bootstrap.TimeField = function(config){
21989     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21990     this.addEvents({
21991             /**
21992              * @event show
21993              * Fires when this field show.
21994              * @param {Roo.bootstrap.DateField} thisthis
21995              * @param {Mixed} date The date value
21996              */
21997             show : true,
21998             /**
21999              * @event show
22000              * Fires when this field hide.
22001              * @param {Roo.bootstrap.DateField} this
22002              * @param {Mixed} date The date value
22003              */
22004             hide : true,
22005             /**
22006              * @event select
22007              * Fires when select a date.
22008              * @param {Roo.bootstrap.DateField} this
22009              * @param {Mixed} date The date value
22010              */
22011             select : true
22012         });
22013 };
22014
22015 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22016     
22017     /**
22018      * @cfg {String} format
22019      * The default time format string which can be overriden for localization support.  The format must be
22020      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22021      */
22022     format : "H:i",
22023
22024     getAutoCreate : function()
22025     {
22026         this.after = '<i class="fa far fa-clock"></i>';
22027         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22028         
22029          
22030     },
22031     onRender: function(ct, position)
22032     {
22033         
22034         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22035                 
22036         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22037         
22038         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22039         
22040         this.pop = this.picker().select('>.datepicker-time',true).first();
22041         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22042         
22043         this.picker().on('mousedown', this.onMousedown, this);
22044         this.picker().on('click', this.onClick, this);
22045         
22046         this.picker().addClass('datepicker-dropdown');
22047     
22048         this.fillTime();
22049         this.update();
22050             
22051         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22052         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22053         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22054         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22055         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22056         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22057
22058     },
22059     
22060     fireKey: function(e){
22061         if (!this.picker().isVisible()){
22062             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22063                 this.show();
22064             }
22065             return;
22066         }
22067
22068         e.preventDefault();
22069         
22070         switch(e.keyCode){
22071             case 27: // escape
22072                 this.hide();
22073                 break;
22074             case 37: // left
22075             case 39: // right
22076                 this.onTogglePeriod();
22077                 break;
22078             case 38: // up
22079                 this.onIncrementMinutes();
22080                 break;
22081             case 40: // down
22082                 this.onDecrementMinutes();
22083                 break;
22084             case 13: // enter
22085             case 9: // tab
22086                 this.setTime();
22087                 break;
22088         }
22089     },
22090     
22091     onClick: function(e) {
22092         e.stopPropagation();
22093         e.preventDefault();
22094     },
22095     
22096     picker : function()
22097     {
22098         return this.pickerEl;
22099     },
22100     
22101     fillTime: function()
22102     {    
22103         var time = this.pop.select('tbody', true).first();
22104         
22105         time.dom.innerHTML = '';
22106         
22107         time.createChild({
22108             tag: 'tr',
22109             cn: [
22110                 {
22111                     tag: 'td',
22112                     cn: [
22113                         {
22114                             tag: 'a',
22115                             href: '#',
22116                             cls: 'btn',
22117                             cn: [
22118                                 {
22119                                     tag: 'i',
22120                                     cls: 'hours-up fa fas fa-chevron-up'
22121                                 }
22122                             ]
22123                         } 
22124                     ]
22125                 },
22126                 {
22127                     tag: 'td',
22128                     cls: 'separator'
22129                 },
22130                 {
22131                     tag: 'td',
22132                     cn: [
22133                         {
22134                             tag: 'a',
22135                             href: '#',
22136                             cls: 'btn',
22137                             cn: [
22138                                 {
22139                                     tag: 'i',
22140                                     cls: 'minutes-up fa fas fa-chevron-up'
22141                                 }
22142                             ]
22143                         }
22144                     ]
22145                 },
22146                 {
22147                     tag: 'td',
22148                     cls: 'separator'
22149                 }
22150             ]
22151         });
22152         
22153         time.createChild({
22154             tag: 'tr',
22155             cn: [
22156                 {
22157                     tag: 'td',
22158                     cn: [
22159                         {
22160                             tag: 'span',
22161                             cls: 'timepicker-hour',
22162                             html: '00'
22163                         }  
22164                     ]
22165                 },
22166                 {
22167                     tag: 'td',
22168                     cls: 'separator',
22169                     html: ':'
22170                 },
22171                 {
22172                     tag: 'td',
22173                     cn: [
22174                         {
22175                             tag: 'span',
22176                             cls: 'timepicker-minute',
22177                             html: '00'
22178                         }  
22179                     ]
22180                 },
22181                 {
22182                     tag: 'td',
22183                     cls: 'separator'
22184                 },
22185                 {
22186                     tag: 'td',
22187                     cn: [
22188                         {
22189                             tag: 'button',
22190                             type: 'button',
22191                             cls: 'btn btn-primary period',
22192                             html: 'AM'
22193                             
22194                         }
22195                     ]
22196                 }
22197             ]
22198         });
22199         
22200         time.createChild({
22201             tag: 'tr',
22202             cn: [
22203                 {
22204                     tag: 'td',
22205                     cn: [
22206                         {
22207                             tag: 'a',
22208                             href: '#',
22209                             cls: 'btn',
22210                             cn: [
22211                                 {
22212                                     tag: 'span',
22213                                     cls: 'hours-down fa fas fa-chevron-down'
22214                                 }
22215                             ]
22216                         }
22217                     ]
22218                 },
22219                 {
22220                     tag: 'td',
22221                     cls: 'separator'
22222                 },
22223                 {
22224                     tag: 'td',
22225                     cn: [
22226                         {
22227                             tag: 'a',
22228                             href: '#',
22229                             cls: 'btn',
22230                             cn: [
22231                                 {
22232                                     tag: 'span',
22233                                     cls: 'minutes-down fa fas fa-chevron-down'
22234                                 }
22235                             ]
22236                         }
22237                     ]
22238                 },
22239                 {
22240                     tag: 'td',
22241                     cls: 'separator'
22242                 }
22243             ]
22244         });
22245         
22246     },
22247     
22248     update: function()
22249     {
22250         
22251         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22252         
22253         this.fill();
22254     },
22255     
22256     fill: function() 
22257     {
22258         var hours = this.time.getHours();
22259         var minutes = this.time.getMinutes();
22260         var period = 'AM';
22261         
22262         if(hours > 11){
22263             period = 'PM';
22264         }
22265         
22266         if(hours == 0){
22267             hours = 12;
22268         }
22269         
22270         
22271         if(hours > 12){
22272             hours = hours - 12;
22273         }
22274         
22275         if(hours < 10){
22276             hours = '0' + hours;
22277         }
22278         
22279         if(minutes < 10){
22280             minutes = '0' + minutes;
22281         }
22282         
22283         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22284         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22285         this.pop.select('button', true).first().dom.innerHTML = period;
22286         
22287     },
22288     
22289     place: function()
22290     {   
22291         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22292         
22293         var cls = ['bottom'];
22294         
22295         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22296             cls.pop();
22297             cls.push('top');
22298         }
22299         
22300         cls.push('right');
22301         
22302         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22303             cls.pop();
22304             cls.push('left');
22305         }
22306         //this.picker().setXY(20000,20000);
22307         this.picker().addClass(cls.join('-'));
22308         
22309         var _this = this;
22310         
22311         Roo.each(cls, function(c){
22312             if(c == 'bottom'){
22313                 (function() {
22314                  //  
22315                 }).defer(200);
22316                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22317                 //_this.picker().setTop(_this.inputEl().getHeight());
22318                 return;
22319             }
22320             if(c == 'top'){
22321                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22322                 
22323                 //_this.picker().setTop(0 - _this.picker().getHeight());
22324                 return;
22325             }
22326             /*
22327             if(c == 'left'){
22328                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22329                 return;
22330             }
22331             if(c == 'right'){
22332                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22333                 return;
22334             }
22335             */
22336         });
22337         
22338     },
22339   
22340     onFocus : function()
22341     {
22342         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22343         this.show();
22344     },
22345     
22346     onBlur : function()
22347     {
22348         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22349         this.hide();
22350     },
22351     
22352     show : function()
22353     {
22354         this.picker().show();
22355         this.pop.show();
22356         this.update();
22357         this.place();
22358         
22359         this.fireEvent('show', this, this.date);
22360     },
22361     
22362     hide : function()
22363     {
22364         this.picker().hide();
22365         this.pop.hide();
22366         
22367         this.fireEvent('hide', this, this.date);
22368     },
22369     
22370     setTime : function()
22371     {
22372         this.hide();
22373         this.setValue(this.time.format(this.format));
22374         
22375         this.fireEvent('select', this, this.date);
22376         
22377         
22378     },
22379     
22380     onMousedown: function(e){
22381         e.stopPropagation();
22382         e.preventDefault();
22383     },
22384     
22385     onIncrementHours: function()
22386     {
22387         Roo.log('onIncrementHours');
22388         this.time = this.time.add(Date.HOUR, 1);
22389         this.update();
22390         
22391     },
22392     
22393     onDecrementHours: function()
22394     {
22395         Roo.log('onDecrementHours');
22396         this.time = this.time.add(Date.HOUR, -1);
22397         this.update();
22398     },
22399     
22400     onIncrementMinutes: function()
22401     {
22402         Roo.log('onIncrementMinutes');
22403         this.time = this.time.add(Date.MINUTE, 1);
22404         this.update();
22405     },
22406     
22407     onDecrementMinutes: function()
22408     {
22409         Roo.log('onDecrementMinutes');
22410         this.time = this.time.add(Date.MINUTE, -1);
22411         this.update();
22412     },
22413     
22414     onTogglePeriod: function()
22415     {
22416         Roo.log('onTogglePeriod');
22417         this.time = this.time.add(Date.HOUR, 12);
22418         this.update();
22419     }
22420     
22421    
22422 });
22423  
22424
22425 Roo.apply(Roo.bootstrap.TimeField,  {
22426   
22427     template : {
22428         tag: 'div',
22429         cls: 'datepicker dropdown-menu',
22430         cn: [
22431             {
22432                 tag: 'div',
22433                 cls: 'datepicker-time',
22434                 cn: [
22435                 {
22436                     tag: 'table',
22437                     cls: 'table-condensed',
22438                     cn:[
22439                         {
22440                             tag: 'tbody',
22441                             cn: [
22442                                 {
22443                                     tag: 'tr',
22444                                     cn: [
22445                                     {
22446                                         tag: 'td',
22447                                         colspan: '7'
22448                                     }
22449                                     ]
22450                                 }
22451                             ]
22452                         },
22453                         {
22454                             tag: 'tfoot',
22455                             cn: [
22456                                 {
22457                                     tag: 'tr',
22458                                     cn: [
22459                                     {
22460                                         tag: 'th',
22461                                         colspan: '7',
22462                                         cls: '',
22463                                         cn: [
22464                                             {
22465                                                 tag: 'button',
22466                                                 cls: 'btn btn-info ok',
22467                                                 html: 'OK'
22468                                             }
22469                                         ]
22470                                     }
22471                     
22472                                     ]
22473                                 }
22474                             ]
22475                         }
22476                     ]
22477                 }
22478                 ]
22479             }
22480         ]
22481     }
22482 });
22483
22484  
22485
22486  /*
22487  * - LGPL
22488  *
22489  * MonthField
22490  * 
22491  */
22492
22493 /**
22494  * @class Roo.bootstrap.MonthField
22495  * @extends Roo.bootstrap.Input
22496  * Bootstrap MonthField class
22497  * 
22498  * @cfg {String} language default en
22499  * 
22500  * @constructor
22501  * Create a new MonthField
22502  * @param {Object} config The config object
22503  */
22504
22505 Roo.bootstrap.MonthField = function(config){
22506     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22507     
22508     this.addEvents({
22509         /**
22510          * @event show
22511          * Fires when this field show.
22512          * @param {Roo.bootstrap.MonthField} this
22513          * @param {Mixed} date The date value
22514          */
22515         show : true,
22516         /**
22517          * @event show
22518          * Fires when this field hide.
22519          * @param {Roo.bootstrap.MonthField} this
22520          * @param {Mixed} date The date value
22521          */
22522         hide : true,
22523         /**
22524          * @event select
22525          * Fires when select a date.
22526          * @param {Roo.bootstrap.MonthField} this
22527          * @param {String} oldvalue The old value
22528          * @param {String} newvalue The new value
22529          */
22530         select : true
22531     });
22532 };
22533
22534 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22535     
22536     onRender: function(ct, position)
22537     {
22538         
22539         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22540         
22541         this.language = this.language || 'en';
22542         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22543         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22544         
22545         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22546         this.isInline = false;
22547         this.isInput = true;
22548         this.component = this.el.select('.add-on', true).first() || false;
22549         this.component = (this.component && this.component.length === 0) ? false : this.component;
22550         this.hasInput = this.component && this.inputEL().length;
22551         
22552         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22553         
22554         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22555         
22556         this.picker().on('mousedown', this.onMousedown, this);
22557         this.picker().on('click', this.onClick, this);
22558         
22559         this.picker().addClass('datepicker-dropdown');
22560         
22561         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22562             v.setStyle('width', '189px');
22563         });
22564         
22565         this.fillMonths();
22566         
22567         this.update();
22568         
22569         if(this.isInline) {
22570             this.show();
22571         }
22572         
22573     },
22574     
22575     setValue: function(v, suppressEvent)
22576     {   
22577         var o = this.getValue();
22578         
22579         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22580         
22581         this.update();
22582
22583         if(suppressEvent !== true){
22584             this.fireEvent('select', this, o, v);
22585         }
22586         
22587     },
22588     
22589     getValue: function()
22590     {
22591         return this.value;
22592     },
22593     
22594     onClick: function(e) 
22595     {
22596         e.stopPropagation();
22597         e.preventDefault();
22598         
22599         var target = e.getTarget();
22600         
22601         if(target.nodeName.toLowerCase() === 'i'){
22602             target = Roo.get(target).dom.parentNode;
22603         }
22604         
22605         var nodeName = target.nodeName;
22606         var className = target.className;
22607         var html = target.innerHTML;
22608         
22609         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22610             return;
22611         }
22612         
22613         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22614         
22615         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22616         
22617         this.hide();
22618                         
22619     },
22620     
22621     picker : function()
22622     {
22623         return this.pickerEl;
22624     },
22625     
22626     fillMonths: function()
22627     {    
22628         var i = 0;
22629         var months = this.picker().select('>.datepicker-months td', true).first();
22630         
22631         months.dom.innerHTML = '';
22632         
22633         while (i < 12) {
22634             var month = {
22635                 tag: 'span',
22636                 cls: 'month',
22637                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22638             };
22639             
22640             months.createChild(month);
22641         }
22642         
22643     },
22644     
22645     update: function()
22646     {
22647         var _this = this;
22648         
22649         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22650             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22651         }
22652         
22653         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22654             e.removeClass('active');
22655             
22656             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22657                 e.addClass('active');
22658             }
22659         })
22660     },
22661     
22662     place: function()
22663     {
22664         if(this.isInline) {
22665             return;
22666         }
22667         
22668         this.picker().removeClass(['bottom', 'top']);
22669         
22670         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22671             /*
22672              * place to the top of element!
22673              *
22674              */
22675             
22676             this.picker().addClass('top');
22677             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22678             
22679             return;
22680         }
22681         
22682         this.picker().addClass('bottom');
22683         
22684         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22685     },
22686     
22687     onFocus : function()
22688     {
22689         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22690         this.show();
22691     },
22692     
22693     onBlur : function()
22694     {
22695         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22696         
22697         var d = this.inputEl().getValue();
22698         
22699         this.setValue(d);
22700                 
22701         this.hide();
22702     },
22703     
22704     show : function()
22705     {
22706         this.picker().show();
22707         this.picker().select('>.datepicker-months', true).first().show();
22708         this.update();
22709         this.place();
22710         
22711         this.fireEvent('show', this, this.date);
22712     },
22713     
22714     hide : function()
22715     {
22716         if(this.isInline) {
22717             return;
22718         }
22719         this.picker().hide();
22720         this.fireEvent('hide', this, this.date);
22721         
22722     },
22723     
22724     onMousedown: function(e)
22725     {
22726         e.stopPropagation();
22727         e.preventDefault();
22728     },
22729     
22730     keyup: function(e)
22731     {
22732         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22733         this.update();
22734     },
22735
22736     fireKey: function(e)
22737     {
22738         if (!this.picker().isVisible()){
22739             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22740                 this.show();
22741             }
22742             return;
22743         }
22744         
22745         var dir;
22746         
22747         switch(e.keyCode){
22748             case 27: // escape
22749                 this.hide();
22750                 e.preventDefault();
22751                 break;
22752             case 37: // left
22753             case 39: // right
22754                 dir = e.keyCode == 37 ? -1 : 1;
22755                 
22756                 this.vIndex = this.vIndex + dir;
22757                 
22758                 if(this.vIndex < 0){
22759                     this.vIndex = 0;
22760                 }
22761                 
22762                 if(this.vIndex > 11){
22763                     this.vIndex = 11;
22764                 }
22765                 
22766                 if(isNaN(this.vIndex)){
22767                     this.vIndex = 0;
22768                 }
22769                 
22770                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22771                 
22772                 break;
22773             case 38: // up
22774             case 40: // down
22775                 
22776                 dir = e.keyCode == 38 ? -1 : 1;
22777                 
22778                 this.vIndex = this.vIndex + dir * 4;
22779                 
22780                 if(this.vIndex < 0){
22781                     this.vIndex = 0;
22782                 }
22783                 
22784                 if(this.vIndex > 11){
22785                     this.vIndex = 11;
22786                 }
22787                 
22788                 if(isNaN(this.vIndex)){
22789                     this.vIndex = 0;
22790                 }
22791                 
22792                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22793                 break;
22794                 
22795             case 13: // enter
22796                 
22797                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22798                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22799                 }
22800                 
22801                 this.hide();
22802                 e.preventDefault();
22803                 break;
22804             case 9: // tab
22805                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22806                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22807                 }
22808                 this.hide();
22809                 break;
22810             case 16: // shift
22811             case 17: // ctrl
22812             case 18: // alt
22813                 break;
22814             default :
22815                 this.hide();
22816                 
22817         }
22818     },
22819     
22820     remove: function() 
22821     {
22822         this.picker().remove();
22823     }
22824    
22825 });
22826
22827 Roo.apply(Roo.bootstrap.MonthField,  {
22828     
22829     content : {
22830         tag: 'tbody',
22831         cn: [
22832         {
22833             tag: 'tr',
22834             cn: [
22835             {
22836                 tag: 'td',
22837                 colspan: '7'
22838             }
22839             ]
22840         }
22841         ]
22842     },
22843     
22844     dates:{
22845         en: {
22846             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22847             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22848         }
22849     }
22850 });
22851
22852 Roo.apply(Roo.bootstrap.MonthField,  {
22853   
22854     template : {
22855         tag: 'div',
22856         cls: 'datepicker dropdown-menu roo-dynamic',
22857         cn: [
22858             {
22859                 tag: 'div',
22860                 cls: 'datepicker-months',
22861                 cn: [
22862                 {
22863                     tag: 'table',
22864                     cls: 'table-condensed',
22865                     cn:[
22866                         Roo.bootstrap.DateField.content
22867                     ]
22868                 }
22869                 ]
22870             }
22871         ]
22872     }
22873 });
22874
22875  
22876
22877  
22878  /*
22879  * - LGPL
22880  *
22881  * CheckBox
22882  * 
22883  */
22884
22885 /**
22886  * @class Roo.bootstrap.CheckBox
22887  * @extends Roo.bootstrap.Input
22888  * Bootstrap CheckBox class
22889  * 
22890  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22891  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22892  * @cfg {String} boxLabel The text that appears beside the checkbox
22893  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22894  * @cfg {Boolean} checked initnal the element
22895  * @cfg {Boolean} inline inline the element (default false)
22896  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22897  * @cfg {String} tooltip label tooltip
22898  * 
22899  * @constructor
22900  * Create a new CheckBox
22901  * @param {Object} config The config object
22902  */
22903
22904 Roo.bootstrap.CheckBox = function(config){
22905     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22906    
22907     this.addEvents({
22908         /**
22909         * @event check
22910         * Fires when the element is checked or unchecked.
22911         * @param {Roo.bootstrap.CheckBox} this This input
22912         * @param {Boolean} checked The new checked value
22913         */
22914        check : true,
22915        /**
22916         * @event click
22917         * Fires when the element is click.
22918         * @param {Roo.bootstrap.CheckBox} this This input
22919         */
22920        click : true
22921     });
22922     
22923 };
22924
22925 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22926   
22927     inputType: 'checkbox',
22928     inputValue: 1,
22929     valueOff: 0,
22930     boxLabel: false,
22931     checked: false,
22932     weight : false,
22933     inline: false,
22934     tooltip : '',
22935     
22936     // checkbox success does not make any sense really.. 
22937     invalidClass : "",
22938     validClass : "",
22939     
22940     
22941     getAutoCreate : function()
22942     {
22943         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22944         
22945         var id = Roo.id();
22946         
22947         var cfg = {};
22948         
22949         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22950         
22951         if(this.inline){
22952             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22953         }
22954         
22955         var input =  {
22956             tag: 'input',
22957             id : id,
22958             type : this.inputType,
22959             value : this.inputValue,
22960             cls : 'roo-' + this.inputType, //'form-box',
22961             placeholder : this.placeholder || ''
22962             
22963         };
22964         
22965         if(this.inputType != 'radio'){
22966             var hidden =  {
22967                 tag: 'input',
22968                 type : 'hidden',
22969                 cls : 'roo-hidden-value',
22970                 value : this.checked ? this.inputValue : this.valueOff
22971             };
22972         }
22973         
22974             
22975         if (this.weight) { // Validity check?
22976             cfg.cls += " " + this.inputType + "-" + this.weight;
22977         }
22978         
22979         if (this.disabled) {
22980             input.disabled=true;
22981         }
22982         
22983         if(this.checked){
22984             input.checked = this.checked;
22985         }
22986         
22987         if (this.name) {
22988             
22989             input.name = this.name;
22990             
22991             if(this.inputType != 'radio'){
22992                 hidden.name = this.name;
22993                 input.name = '_hidden_' + this.name;
22994             }
22995         }
22996         
22997         if (this.size) {
22998             input.cls += ' input-' + this.size;
22999         }
23000         
23001         var settings=this;
23002         
23003         ['xs','sm','md','lg'].map(function(size){
23004             if (settings[size]) {
23005                 cfg.cls += ' col-' + size + '-' + settings[size];
23006             }
23007         });
23008         
23009         var inputblock = input;
23010          
23011         if (this.before || this.after) {
23012             
23013             inputblock = {
23014                 cls : 'input-group',
23015                 cn :  [] 
23016             };
23017             
23018             if (this.before) {
23019                 inputblock.cn.push({
23020                     tag :'span',
23021                     cls : 'input-group-addon',
23022                     html : this.before
23023                 });
23024             }
23025             
23026             inputblock.cn.push(input);
23027             
23028             if(this.inputType != 'radio'){
23029                 inputblock.cn.push(hidden);
23030             }
23031             
23032             if (this.after) {
23033                 inputblock.cn.push({
23034                     tag :'span',
23035                     cls : 'input-group-addon',
23036                     html : this.after
23037                 });
23038             }
23039             
23040         }
23041         var boxLabelCfg = false;
23042         
23043         if(this.boxLabel){
23044            
23045             boxLabelCfg = {
23046                 tag: 'label',
23047                 //'for': id, // box label is handled by onclick - so no for...
23048                 cls: 'box-label',
23049                 html: this.boxLabel
23050             };
23051             if(this.tooltip){
23052                 boxLabelCfg.tooltip = this.tooltip;
23053             }
23054              
23055         }
23056         
23057         
23058         if (align ==='left' && this.fieldLabel.length) {
23059 //                Roo.log("left and has label");
23060             cfg.cn = [
23061                 {
23062                     tag: 'label',
23063                     'for' :  id,
23064                     cls : 'control-label',
23065                     html : this.fieldLabel
23066                 },
23067                 {
23068                     cls : "", 
23069                     cn: [
23070                         inputblock
23071                     ]
23072                 }
23073             ];
23074             
23075             if (boxLabelCfg) {
23076                 cfg.cn[1].cn.push(boxLabelCfg);
23077             }
23078             
23079             if(this.labelWidth > 12){
23080                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23081             }
23082             
23083             if(this.labelWidth < 13 && this.labelmd == 0){
23084                 this.labelmd = this.labelWidth;
23085             }
23086             
23087             if(this.labellg > 0){
23088                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23089                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23090             }
23091             
23092             if(this.labelmd > 0){
23093                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23094                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23095             }
23096             
23097             if(this.labelsm > 0){
23098                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23099                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23100             }
23101             
23102             if(this.labelxs > 0){
23103                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23104                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23105             }
23106             
23107         } else if ( this.fieldLabel.length) {
23108 //                Roo.log(" label");
23109                 cfg.cn = [
23110                    
23111                     {
23112                         tag: this.boxLabel ? 'span' : 'label',
23113                         'for': id,
23114                         cls: 'control-label box-input-label',
23115                         //cls : 'input-group-addon',
23116                         html : this.fieldLabel
23117                     },
23118                     
23119                     inputblock
23120                     
23121                 ];
23122                 if (boxLabelCfg) {
23123                     cfg.cn.push(boxLabelCfg);
23124                 }
23125
23126         } else {
23127             
23128 //                Roo.log(" no label && no align");
23129                 cfg.cn = [  inputblock ] ;
23130                 if (boxLabelCfg) {
23131                     cfg.cn.push(boxLabelCfg);
23132                 }
23133
23134                 
23135         }
23136         
23137        
23138         
23139         if(this.inputType != 'radio'){
23140             cfg.cn.push(hidden);
23141         }
23142         
23143         return cfg;
23144         
23145     },
23146     
23147     /**
23148      * return the real input element.
23149      */
23150     inputEl: function ()
23151     {
23152         return this.el.select('input.roo-' + this.inputType,true).first();
23153     },
23154     hiddenEl: function ()
23155     {
23156         return this.el.select('input.roo-hidden-value',true).first();
23157     },
23158     
23159     labelEl: function()
23160     {
23161         return this.el.select('label.control-label',true).first();
23162     },
23163     /* depricated... */
23164     
23165     label: function()
23166     {
23167         return this.labelEl();
23168     },
23169     
23170     boxLabelEl: function()
23171     {
23172         return this.el.select('label.box-label',true).first();
23173     },
23174     
23175     initEvents : function()
23176     {
23177 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23178         
23179         this.inputEl().on('click', this.onClick,  this);
23180         
23181         if (this.boxLabel) { 
23182             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23183         }
23184         
23185         this.startValue = this.getValue();
23186         
23187         if(this.groupId){
23188             Roo.bootstrap.CheckBox.register(this);
23189         }
23190     },
23191     
23192     onClick : function(e)
23193     {   
23194         if(this.fireEvent('click', this, e) !== false){
23195             this.setChecked(!this.checked);
23196         }
23197         
23198     },
23199     
23200     setChecked : function(state,suppressEvent)
23201     {
23202         this.startValue = this.getValue();
23203
23204         if(this.inputType == 'radio'){
23205             
23206             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23207                 e.dom.checked = false;
23208             });
23209             
23210             this.inputEl().dom.checked = true;
23211             
23212             this.inputEl().dom.value = this.inputValue;
23213             
23214             if(suppressEvent !== true){
23215                 this.fireEvent('check', this, true);
23216             }
23217             
23218             this.validate();
23219             
23220             return;
23221         }
23222         
23223         this.checked = state;
23224         
23225         this.inputEl().dom.checked = state;
23226         
23227         
23228         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23229         
23230         if(suppressEvent !== true){
23231             this.fireEvent('check', this, state);
23232         }
23233         
23234         this.validate();
23235     },
23236     
23237     getValue : function()
23238     {
23239         if(this.inputType == 'radio'){
23240             return this.getGroupValue();
23241         }
23242         
23243         return this.hiddenEl().dom.value;
23244         
23245     },
23246     
23247     getGroupValue : function()
23248     {
23249         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23250             return '';
23251         }
23252         
23253         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23254     },
23255     
23256     setValue : function(v,suppressEvent)
23257     {
23258         if(this.inputType == 'radio'){
23259             this.setGroupValue(v, suppressEvent);
23260             return;
23261         }
23262         
23263         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23264         
23265         this.validate();
23266     },
23267     
23268     setGroupValue : function(v, suppressEvent)
23269     {
23270         this.startValue = this.getValue();
23271         
23272         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23273             e.dom.checked = false;
23274             
23275             if(e.dom.value == v){
23276                 e.dom.checked = true;
23277             }
23278         });
23279         
23280         if(suppressEvent !== true){
23281             this.fireEvent('check', this, true);
23282         }
23283
23284         this.validate();
23285         
23286         return;
23287     },
23288     
23289     validate : function()
23290     {
23291         if(this.getVisibilityEl().hasClass('hidden')){
23292             return true;
23293         }
23294         
23295         if(
23296                 this.disabled || 
23297                 (this.inputType == 'radio' && this.validateRadio()) ||
23298                 (this.inputType == 'checkbox' && this.validateCheckbox())
23299         ){
23300             this.markValid();
23301             return true;
23302         }
23303         
23304         this.markInvalid();
23305         return false;
23306     },
23307     
23308     validateRadio : function()
23309     {
23310         if(this.getVisibilityEl().hasClass('hidden')){
23311             return true;
23312         }
23313         
23314         if(this.allowBlank){
23315             return true;
23316         }
23317         
23318         var valid = false;
23319         
23320         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23321             if(!e.dom.checked){
23322                 return;
23323             }
23324             
23325             valid = true;
23326             
23327             return false;
23328         });
23329         
23330         return valid;
23331     },
23332     
23333     validateCheckbox : function()
23334     {
23335         if(!this.groupId){
23336             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23337             //return (this.getValue() == this.inputValue) ? true : false;
23338         }
23339         
23340         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23341         
23342         if(!group){
23343             return false;
23344         }
23345         
23346         var r = false;
23347         
23348         for(var i in group){
23349             if(group[i].el.isVisible(true)){
23350                 r = false;
23351                 break;
23352             }
23353             
23354             r = true;
23355         }
23356         
23357         for(var i in group){
23358             if(r){
23359                 break;
23360             }
23361             
23362             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23363         }
23364         
23365         return r;
23366     },
23367     
23368     /**
23369      * Mark this field as valid
23370      */
23371     markValid : function()
23372     {
23373         var _this = this;
23374         
23375         this.fireEvent('valid', this);
23376         
23377         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23378         
23379         if(this.groupId){
23380             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23381         }
23382         
23383         if(label){
23384             label.markValid();
23385         }
23386
23387         if(this.inputType == 'radio'){
23388             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23389                 var fg = e.findParent('.form-group', false, true);
23390                 if (Roo.bootstrap.version == 3) {
23391                     fg.removeClass([_this.invalidClass, _this.validClass]);
23392                     fg.addClass(_this.validClass);
23393                 } else {
23394                     fg.removeClass(['is-valid', 'is-invalid']);
23395                     fg.addClass('is-valid');
23396                 }
23397             });
23398             
23399             return;
23400         }
23401
23402         if(!this.groupId){
23403             var fg = this.el.findParent('.form-group', false, true);
23404             if (Roo.bootstrap.version == 3) {
23405                 fg.removeClass([this.invalidClass, this.validClass]);
23406                 fg.addClass(this.validClass);
23407             } else {
23408                 fg.removeClass(['is-valid', 'is-invalid']);
23409                 fg.addClass('is-valid');
23410             }
23411             return;
23412         }
23413         
23414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23415         
23416         if(!group){
23417             return;
23418         }
23419         
23420         for(var i in group){
23421             var fg = group[i].el.findParent('.form-group', false, true);
23422             if (Roo.bootstrap.version == 3) {
23423                 fg.removeClass([this.invalidClass, this.validClass]);
23424                 fg.addClass(this.validClass);
23425             } else {
23426                 fg.removeClass(['is-valid', 'is-invalid']);
23427                 fg.addClass('is-valid');
23428             }
23429         }
23430     },
23431     
23432      /**
23433      * Mark this field as invalid
23434      * @param {String} msg The validation message
23435      */
23436     markInvalid : function(msg)
23437     {
23438         if(this.allowBlank){
23439             return;
23440         }
23441         
23442         var _this = this;
23443         
23444         this.fireEvent('invalid', this, msg);
23445         
23446         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23447         
23448         if(this.groupId){
23449             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23450         }
23451         
23452         if(label){
23453             label.markInvalid();
23454         }
23455             
23456         if(this.inputType == 'radio'){
23457             
23458             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23459                 var fg = e.findParent('.form-group', false, true);
23460                 if (Roo.bootstrap.version == 3) {
23461                     fg.removeClass([_this.invalidClass, _this.validClass]);
23462                     fg.addClass(_this.invalidClass);
23463                 } else {
23464                     fg.removeClass(['is-invalid', 'is-valid']);
23465                     fg.addClass('is-invalid');
23466                 }
23467             });
23468             
23469             return;
23470         }
23471         
23472         if(!this.groupId){
23473             var fg = this.el.findParent('.form-group', false, true);
23474             if (Roo.bootstrap.version == 3) {
23475                 fg.removeClass([_this.invalidClass, _this.validClass]);
23476                 fg.addClass(_this.invalidClass);
23477             } else {
23478                 fg.removeClass(['is-invalid', 'is-valid']);
23479                 fg.addClass('is-invalid');
23480             }
23481             return;
23482         }
23483         
23484         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23485         
23486         if(!group){
23487             return;
23488         }
23489         
23490         for(var i in group){
23491             var fg = group[i].el.findParent('.form-group', false, true);
23492             if (Roo.bootstrap.version == 3) {
23493                 fg.removeClass([_this.invalidClass, _this.validClass]);
23494                 fg.addClass(_this.invalidClass);
23495             } else {
23496                 fg.removeClass(['is-invalid', 'is-valid']);
23497                 fg.addClass('is-invalid');
23498             }
23499         }
23500         
23501     },
23502     
23503     clearInvalid : function()
23504     {
23505         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23506         
23507         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23508         
23509         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23510         
23511         if (label && label.iconEl) {
23512             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23513             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23514         }
23515     },
23516     
23517     disable : function()
23518     {
23519         if(this.inputType != 'radio'){
23520             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23521             return;
23522         }
23523         
23524         var _this = this;
23525         
23526         if(this.rendered){
23527             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23528                 _this.getActionEl().addClass(this.disabledClass);
23529                 e.dom.disabled = true;
23530             });
23531         }
23532         
23533         this.disabled = true;
23534         this.fireEvent("disable", this);
23535         return this;
23536     },
23537
23538     enable : function()
23539     {
23540         if(this.inputType != 'radio'){
23541             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23542             return;
23543         }
23544         
23545         var _this = this;
23546         
23547         if(this.rendered){
23548             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23549                 _this.getActionEl().removeClass(this.disabledClass);
23550                 e.dom.disabled = false;
23551             });
23552         }
23553         
23554         this.disabled = false;
23555         this.fireEvent("enable", this);
23556         return this;
23557     },
23558     
23559     setBoxLabel : function(v)
23560     {
23561         this.boxLabel = v;
23562         
23563         if(this.rendered){
23564             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23565         }
23566     }
23567
23568 });
23569
23570 Roo.apply(Roo.bootstrap.CheckBox, {
23571     
23572     groups: {},
23573     
23574      /**
23575     * register a CheckBox Group
23576     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23577     */
23578     register : function(checkbox)
23579     {
23580         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23581             this.groups[checkbox.groupId] = {};
23582         }
23583         
23584         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23585             return;
23586         }
23587         
23588         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23589         
23590     },
23591     /**
23592     * fetch a CheckBox Group based on the group ID
23593     * @param {string} the group ID
23594     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23595     */
23596     get: function(groupId) {
23597         if (typeof(this.groups[groupId]) == 'undefined') {
23598             return false;
23599         }
23600         
23601         return this.groups[groupId] ;
23602     }
23603     
23604     
23605 });
23606 /*
23607  * - LGPL
23608  *
23609  * RadioItem
23610  * 
23611  */
23612
23613 /**
23614  * @class Roo.bootstrap.Radio
23615  * @extends Roo.bootstrap.Component
23616  * Bootstrap Radio class
23617  * @cfg {String} boxLabel - the label associated
23618  * @cfg {String} value - the value of radio
23619  * 
23620  * @constructor
23621  * Create a new Radio
23622  * @param {Object} config The config object
23623  */
23624 Roo.bootstrap.Radio = function(config){
23625     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23626     
23627 };
23628
23629 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23630     
23631     boxLabel : '',
23632     
23633     value : '',
23634     
23635     getAutoCreate : function()
23636     {
23637         var cfg = {
23638             tag : 'div',
23639             cls : 'form-group radio',
23640             cn : [
23641                 {
23642                     tag : 'label',
23643                     cls : 'box-label',
23644                     html : this.boxLabel
23645                 }
23646             ]
23647         };
23648         
23649         return cfg;
23650     },
23651     
23652     initEvents : function() 
23653     {
23654         this.parent().register(this);
23655         
23656         this.el.on('click', this.onClick, this);
23657         
23658     },
23659     
23660     onClick : function(e)
23661     {
23662         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23663             this.setChecked(true);
23664         }
23665     },
23666     
23667     setChecked : function(state, suppressEvent)
23668     {
23669         this.parent().setValue(this.value, suppressEvent);
23670         
23671     },
23672     
23673     setBoxLabel : function(v)
23674     {
23675         this.boxLabel = v;
23676         
23677         if(this.rendered){
23678             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23679         }
23680     }
23681     
23682 });
23683  
23684
23685  /*
23686  * - LGPL
23687  *
23688  * Input
23689  * 
23690  */
23691
23692 /**
23693  * @class Roo.bootstrap.SecurePass
23694  * @extends Roo.bootstrap.Input
23695  * Bootstrap SecurePass class
23696  *
23697  * 
23698  * @constructor
23699  * Create a new SecurePass
23700  * @param {Object} config The config object
23701  */
23702  
23703 Roo.bootstrap.SecurePass = function (config) {
23704     // these go here, so the translation tool can replace them..
23705     this.errors = {
23706         PwdEmpty: "Please type a password, and then retype it to confirm.",
23707         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23708         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23709         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23710         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23711         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23712         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23713         TooWeak: "Your password is Too Weak."
23714     },
23715     this.meterLabel = "Password strength:";
23716     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23717     this.meterClass = [
23718         "roo-password-meter-tooweak", 
23719         "roo-password-meter-weak", 
23720         "roo-password-meter-medium", 
23721         "roo-password-meter-strong", 
23722         "roo-password-meter-grey"
23723     ];
23724     
23725     this.errors = {};
23726     
23727     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23728 }
23729
23730 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23731     /**
23732      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23733      * {
23734      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23735      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23736      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23737      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23738      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23739      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23740      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23741      * })
23742      */
23743     // private
23744     
23745     meterWidth: 300,
23746     errorMsg :'',    
23747     errors: false,
23748     imageRoot: '/',
23749     /**
23750      * @cfg {String/Object} Label for the strength meter (defaults to
23751      * 'Password strength:')
23752      */
23753     // private
23754     meterLabel: '',
23755     /**
23756      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23757      * ['Weak', 'Medium', 'Strong'])
23758      */
23759     // private    
23760     pwdStrengths: false,    
23761     // private
23762     strength: 0,
23763     // private
23764     _lastPwd: null,
23765     // private
23766     kCapitalLetter: 0,
23767     kSmallLetter: 1,
23768     kDigit: 2,
23769     kPunctuation: 3,
23770     
23771     insecure: false,
23772     // private
23773     initEvents: function ()
23774     {
23775         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23776
23777         if (this.el.is('input[type=password]') && Roo.isSafari) {
23778             this.el.on('keydown', this.SafariOnKeyDown, this);
23779         }
23780
23781         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23782     },
23783     // private
23784     onRender: function (ct, position)
23785     {
23786         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23787         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23788         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23789
23790         this.trigger.createChild({
23791                    cn: [
23792                     {
23793                     //id: 'PwdMeter',
23794                     tag: 'div',
23795                     cls: 'roo-password-meter-grey col-xs-12',
23796                     style: {
23797                         //width: 0,
23798                         //width: this.meterWidth + 'px'                                                
23799                         }
23800                     },
23801                     {                            
23802                          cls: 'roo-password-meter-text'                          
23803                     }
23804                 ]            
23805         });
23806
23807          
23808         if (this.hideTrigger) {
23809             this.trigger.setDisplayed(false);
23810         }
23811         this.setSize(this.width || '', this.height || '');
23812     },
23813     // private
23814     onDestroy: function ()
23815     {
23816         if (this.trigger) {
23817             this.trigger.removeAllListeners();
23818             this.trigger.remove();
23819         }
23820         if (this.wrap) {
23821             this.wrap.remove();
23822         }
23823         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23824     },
23825     // private
23826     checkStrength: function ()
23827     {
23828         var pwd = this.inputEl().getValue();
23829         if (pwd == this._lastPwd) {
23830             return;
23831         }
23832
23833         var strength;
23834         if (this.ClientSideStrongPassword(pwd)) {
23835             strength = 3;
23836         } else if (this.ClientSideMediumPassword(pwd)) {
23837             strength = 2;
23838         } else if (this.ClientSideWeakPassword(pwd)) {
23839             strength = 1;
23840         } else {
23841             strength = 0;
23842         }
23843         
23844         Roo.log('strength1: ' + strength);
23845         
23846         //var pm = this.trigger.child('div/div/div').dom;
23847         var pm = this.trigger.child('div/div');
23848         pm.removeClass(this.meterClass);
23849         pm.addClass(this.meterClass[strength]);
23850                 
23851         
23852         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23853                 
23854         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23855         
23856         this._lastPwd = pwd;
23857     },
23858     reset: function ()
23859     {
23860         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23861         
23862         this._lastPwd = '';
23863         
23864         var pm = this.trigger.child('div/div');
23865         pm.removeClass(this.meterClass);
23866         pm.addClass('roo-password-meter-grey');        
23867         
23868         
23869         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23870         
23871         pt.innerHTML = '';
23872         this.inputEl().dom.type='password';
23873     },
23874     // private
23875     validateValue: function (value)
23876     {
23877         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23878             return false;
23879         }
23880         if (value.length == 0) {
23881             if (this.allowBlank) {
23882                 this.clearInvalid();
23883                 return true;
23884             }
23885
23886             this.markInvalid(this.errors.PwdEmpty);
23887             this.errorMsg = this.errors.PwdEmpty;
23888             return false;
23889         }
23890         
23891         if(this.insecure){
23892             return true;
23893         }
23894         
23895         if (!value.match(/[\x21-\x7e]+/)) {
23896             this.markInvalid(this.errors.PwdBadChar);
23897             this.errorMsg = this.errors.PwdBadChar;
23898             return false;
23899         }
23900         if (value.length < 6) {
23901             this.markInvalid(this.errors.PwdShort);
23902             this.errorMsg = this.errors.PwdShort;
23903             return false;
23904         }
23905         if (value.length > 16) {
23906             this.markInvalid(this.errors.PwdLong);
23907             this.errorMsg = this.errors.PwdLong;
23908             return false;
23909         }
23910         var strength;
23911         if (this.ClientSideStrongPassword(value)) {
23912             strength = 3;
23913         } else if (this.ClientSideMediumPassword(value)) {
23914             strength = 2;
23915         } else if (this.ClientSideWeakPassword(value)) {
23916             strength = 1;
23917         } else {
23918             strength = 0;
23919         }
23920
23921         
23922         if (strength < 2) {
23923             //this.markInvalid(this.errors.TooWeak);
23924             this.errorMsg = this.errors.TooWeak;
23925             //return false;
23926         }
23927         
23928         
23929         console.log('strength2: ' + strength);
23930         
23931         //var pm = this.trigger.child('div/div/div').dom;
23932         
23933         var pm = this.trigger.child('div/div');
23934         pm.removeClass(this.meterClass);
23935         pm.addClass(this.meterClass[strength]);
23936                 
23937         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23938                 
23939         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23940         
23941         this.errorMsg = ''; 
23942         return true;
23943     },
23944     // private
23945     CharacterSetChecks: function (type)
23946     {
23947         this.type = type;
23948         this.fResult = false;
23949     },
23950     // private
23951     isctype: function (character, type)
23952     {
23953         switch (type) {  
23954             case this.kCapitalLetter:
23955                 if (character >= 'A' && character <= 'Z') {
23956                     return true;
23957                 }
23958                 break;
23959             
23960             case this.kSmallLetter:
23961                 if (character >= 'a' && character <= 'z') {
23962                     return true;
23963                 }
23964                 break;
23965             
23966             case this.kDigit:
23967                 if (character >= '0' && character <= '9') {
23968                     return true;
23969                 }
23970                 break;
23971             
23972             case this.kPunctuation:
23973                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23974                     return true;
23975                 }
23976                 break;
23977             
23978             default:
23979                 return false;
23980         }
23981
23982     },
23983     // private
23984     IsLongEnough: function (pwd, size)
23985     {
23986         return !(pwd == null || isNaN(size) || pwd.length < size);
23987     },
23988     // private
23989     SpansEnoughCharacterSets: function (word, nb)
23990     {
23991         if (!this.IsLongEnough(word, nb))
23992         {
23993             return false;
23994         }
23995
23996         var characterSetChecks = new Array(
23997             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23998             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23999         );
24000         
24001         for (var index = 0; index < word.length; ++index) {
24002             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24003                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24004                     characterSetChecks[nCharSet].fResult = true;
24005                     break;
24006                 }
24007             }
24008         }
24009
24010         var nCharSets = 0;
24011         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24012             if (characterSetChecks[nCharSet].fResult) {
24013                 ++nCharSets;
24014             }
24015         }
24016
24017         if (nCharSets < nb) {
24018             return false;
24019         }
24020         return true;
24021     },
24022     // private
24023     ClientSideStrongPassword: function (pwd)
24024     {
24025         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24026     },
24027     // private
24028     ClientSideMediumPassword: function (pwd)
24029     {
24030         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24031     },
24032     // private
24033     ClientSideWeakPassword: function (pwd)
24034     {
24035         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24036     }
24037           
24038 })//<script type="text/javascript">
24039
24040 /*
24041  * Based  Ext JS Library 1.1.1
24042  * Copyright(c) 2006-2007, Ext JS, LLC.
24043  * LGPL
24044  *
24045  */
24046  
24047 /**
24048  * @class Roo.HtmlEditorCore
24049  * @extends Roo.Component
24050  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24051  *
24052  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24053  */
24054
24055 Roo.HtmlEditorCore = function(config){
24056     
24057     
24058     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24059     
24060     
24061     this.addEvents({
24062         /**
24063          * @event initialize
24064          * Fires when the editor is fully initialized (including the iframe)
24065          * @param {Roo.HtmlEditorCore} this
24066          */
24067         initialize: true,
24068         /**
24069          * @event activate
24070          * Fires when the editor is first receives the focus. Any insertion must wait
24071          * until after this event.
24072          * @param {Roo.HtmlEditorCore} this
24073          */
24074         activate: true,
24075          /**
24076          * @event beforesync
24077          * Fires before the textarea is updated with content from the editor iframe. Return false
24078          * to cancel the sync.
24079          * @param {Roo.HtmlEditorCore} this
24080          * @param {String} html
24081          */
24082         beforesync: true,
24083          /**
24084          * @event beforepush
24085          * Fires before the iframe editor is updated with content from the textarea. Return false
24086          * to cancel the push.
24087          * @param {Roo.HtmlEditorCore} this
24088          * @param {String} html
24089          */
24090         beforepush: true,
24091          /**
24092          * @event sync
24093          * Fires when the textarea is updated with content from the editor iframe.
24094          * @param {Roo.HtmlEditorCore} this
24095          * @param {String} html
24096          */
24097         sync: true,
24098          /**
24099          * @event push
24100          * Fires when the iframe editor is updated with content from the textarea.
24101          * @param {Roo.HtmlEditorCore} this
24102          * @param {String} html
24103          */
24104         push: true,
24105         
24106         /**
24107          * @event editorevent
24108          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24109          * @param {Roo.HtmlEditorCore} this
24110          */
24111         editorevent: true
24112         
24113     });
24114     
24115     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24116     
24117     // defaults : white / black...
24118     this.applyBlacklists();
24119     
24120     
24121     
24122 };
24123
24124
24125 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24126
24127
24128      /**
24129      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24130      */
24131     
24132     owner : false,
24133     
24134      /**
24135      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24136      *                        Roo.resizable.
24137      */
24138     resizable : false,
24139      /**
24140      * @cfg {Number} height (in pixels)
24141      */   
24142     height: 300,
24143    /**
24144      * @cfg {Number} width (in pixels)
24145      */   
24146     width: 500,
24147     
24148     /**
24149      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24150      * 
24151      */
24152     stylesheets: false,
24153     
24154     // id of frame..
24155     frameId: false,
24156     
24157     // private properties
24158     validationEvent : false,
24159     deferHeight: true,
24160     initialized : false,
24161     activated : false,
24162     sourceEditMode : false,
24163     onFocus : Roo.emptyFn,
24164     iframePad:3,
24165     hideMode:'offsets',
24166     
24167     clearUp: true,
24168     
24169     // blacklist + whitelisted elements..
24170     black: false,
24171     white: false,
24172      
24173     bodyCls : '',
24174
24175     /**
24176      * Protected method that will not generally be called directly. It
24177      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24178      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24179      */
24180     getDocMarkup : function(){
24181         // body styles..
24182         var st = '';
24183         
24184         // inherit styels from page...?? 
24185         if (this.stylesheets === false) {
24186             
24187             Roo.get(document.head).select('style').each(function(node) {
24188                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24189             });
24190             
24191             Roo.get(document.head).select('link').each(function(node) { 
24192                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24193             });
24194             
24195         } else if (!this.stylesheets.length) {
24196                 // simple..
24197                 st = '<style type="text/css">' +
24198                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24199                    '</style>';
24200         } else {
24201             for (var i in this.stylesheets) { 
24202                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24203             }
24204             
24205         }
24206         
24207         st +=  '<style type="text/css">' +
24208             'IMG { cursor: pointer } ' +
24209         '</style>';
24210
24211         var cls = 'roo-htmleditor-body';
24212         
24213         if(this.bodyCls.length){
24214             cls += ' ' + this.bodyCls;
24215         }
24216         
24217         return '<html><head>' + st  +
24218             //<style type="text/css">' +
24219             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24220             //'</style>' +
24221             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24222     },
24223
24224     // private
24225     onRender : function(ct, position)
24226     {
24227         var _t = this;
24228         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24229         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24230         
24231         
24232         this.el.dom.style.border = '0 none';
24233         this.el.dom.setAttribute('tabIndex', -1);
24234         this.el.addClass('x-hidden hide');
24235         
24236         
24237         
24238         if(Roo.isIE){ // fix IE 1px bogus margin
24239             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24240         }
24241        
24242         
24243         this.frameId = Roo.id();
24244         
24245          
24246         
24247         var iframe = this.owner.wrap.createChild({
24248             tag: 'iframe',
24249             cls: 'form-control', // bootstrap..
24250             id: this.frameId,
24251             name: this.frameId,
24252             frameBorder : 'no',
24253             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24254         }, this.el
24255         );
24256         
24257         
24258         this.iframe = iframe.dom;
24259
24260          this.assignDocWin();
24261         
24262         this.doc.designMode = 'on';
24263        
24264         this.doc.open();
24265         this.doc.write(this.getDocMarkup());
24266         this.doc.close();
24267
24268         
24269         var task = { // must defer to wait for browser to be ready
24270             run : function(){
24271                 //console.log("run task?" + this.doc.readyState);
24272                 this.assignDocWin();
24273                 if(this.doc.body || this.doc.readyState == 'complete'){
24274                     try {
24275                         this.doc.designMode="on";
24276                     } catch (e) {
24277                         return;
24278                     }
24279                     Roo.TaskMgr.stop(task);
24280                     this.initEditor.defer(10, this);
24281                 }
24282             },
24283             interval : 10,
24284             duration: 10000,
24285             scope: this
24286         };
24287         Roo.TaskMgr.start(task);
24288
24289     },
24290
24291     // private
24292     onResize : function(w, h)
24293     {
24294          Roo.log('resize: ' +w + ',' + h );
24295         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24296         if(!this.iframe){
24297             return;
24298         }
24299         if(typeof w == 'number'){
24300             
24301             this.iframe.style.width = w + 'px';
24302         }
24303         if(typeof h == 'number'){
24304             
24305             this.iframe.style.height = h + 'px';
24306             if(this.doc){
24307                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24308             }
24309         }
24310         
24311     },
24312
24313     /**
24314      * Toggles the editor between standard and source edit mode.
24315      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24316      */
24317     toggleSourceEdit : function(sourceEditMode){
24318         
24319         this.sourceEditMode = sourceEditMode === true;
24320         
24321         if(this.sourceEditMode){
24322  
24323             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24324             
24325         }else{
24326             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24327             //this.iframe.className = '';
24328             this.deferFocus();
24329         }
24330         //this.setSize(this.owner.wrap.getSize());
24331         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24332     },
24333
24334     
24335   
24336
24337     /**
24338      * Protected method that will not generally be called directly. If you need/want
24339      * custom HTML cleanup, this is the method you should override.
24340      * @param {String} html The HTML to be cleaned
24341      * return {String} The cleaned HTML
24342      */
24343     cleanHtml : function(html){
24344         html = String(html);
24345         if(html.length > 5){
24346             if(Roo.isSafari){ // strip safari nonsense
24347                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24348             }
24349         }
24350         if(html == '&nbsp;'){
24351             html = '';
24352         }
24353         return html;
24354     },
24355
24356     /**
24357      * HTML Editor -> Textarea
24358      * Protected method that will not generally be called directly. Syncs the contents
24359      * of the editor iframe with the textarea.
24360      */
24361     syncValue : function(){
24362         if(this.initialized){
24363             var bd = (this.doc.body || this.doc.documentElement);
24364             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24365             var html = bd.innerHTML;
24366             if(Roo.isSafari){
24367                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24368                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24369                 if(m && m[1]){
24370                     html = '<div style="'+m[0]+'">' + html + '</div>';
24371                 }
24372             }
24373             html = this.cleanHtml(html);
24374             // fix up the special chars.. normaly like back quotes in word...
24375             // however we do not want to do this with chinese..
24376             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24377                 
24378                 var cc = match.charCodeAt();
24379
24380                 // Get the character value, handling surrogate pairs
24381                 if (match.length == 2) {
24382                     // It's a surrogate pair, calculate the Unicode code point
24383                     var high = match.charCodeAt(0) - 0xD800;
24384                     var low  = match.charCodeAt(1) - 0xDC00;
24385                     cc = (high * 0x400) + low + 0x10000;
24386                 }  else if (
24387                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24388                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24389                     (cc >= 0xf900 && cc < 0xfb00 )
24390                 ) {
24391                         return match;
24392                 }  
24393          
24394                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24395                 return "&#" + cc + ";";
24396                 
24397                 
24398             });
24399             
24400             
24401              
24402             if(this.owner.fireEvent('beforesync', this, html) !== false){
24403                 this.el.dom.value = html;
24404                 this.owner.fireEvent('sync', this, html);
24405             }
24406         }
24407     },
24408
24409     /**
24410      * Protected method that will not generally be called directly. Pushes the value of the textarea
24411      * into the iframe editor.
24412      */
24413     pushValue : function(){
24414         if(this.initialized){
24415             var v = this.el.dom.value.trim();
24416             
24417 //            if(v.length < 1){
24418 //                v = '&#160;';
24419 //            }
24420             
24421             if(this.owner.fireEvent('beforepush', this, v) !== false){
24422                 var d = (this.doc.body || this.doc.documentElement);
24423                 d.innerHTML = v;
24424                 this.cleanUpPaste();
24425                 this.el.dom.value = d.innerHTML;
24426                 this.owner.fireEvent('push', this, v);
24427             }
24428         }
24429     },
24430
24431     // private
24432     deferFocus : function(){
24433         this.focus.defer(10, this);
24434     },
24435
24436     // doc'ed in Field
24437     focus : function(){
24438         if(this.win && !this.sourceEditMode){
24439             this.win.focus();
24440         }else{
24441             this.el.focus();
24442         }
24443     },
24444     
24445     assignDocWin: function()
24446     {
24447         var iframe = this.iframe;
24448         
24449          if(Roo.isIE){
24450             this.doc = iframe.contentWindow.document;
24451             this.win = iframe.contentWindow;
24452         } else {
24453 //            if (!Roo.get(this.frameId)) {
24454 //                return;
24455 //            }
24456 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24457 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24458             
24459             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24460                 return;
24461             }
24462             
24463             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24464             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24465         }
24466     },
24467     
24468     // private
24469     initEditor : function(){
24470         //console.log("INIT EDITOR");
24471         this.assignDocWin();
24472         
24473         
24474         
24475         this.doc.designMode="on";
24476         this.doc.open();
24477         this.doc.write(this.getDocMarkup());
24478         this.doc.close();
24479         
24480         var dbody = (this.doc.body || this.doc.documentElement);
24481         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24482         // this copies styles from the containing element into thsi one..
24483         // not sure why we need all of this..
24484         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24485         
24486         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24487         //ss['background-attachment'] = 'fixed'; // w3c
24488         dbody.bgProperties = 'fixed'; // ie
24489         //Roo.DomHelper.applyStyles(dbody, ss);
24490         Roo.EventManager.on(this.doc, {
24491             //'mousedown': this.onEditorEvent,
24492             'mouseup': this.onEditorEvent,
24493             'dblclick': this.onEditorEvent,
24494             'click': this.onEditorEvent,
24495             'keyup': this.onEditorEvent,
24496             buffer:100,
24497             scope: this
24498         });
24499         if(Roo.isGecko){
24500             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24501         }
24502         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24503             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24504         }
24505         this.initialized = true;
24506
24507         this.owner.fireEvent('initialize', this);
24508         this.pushValue();
24509     },
24510
24511     // private
24512     onDestroy : function(){
24513         
24514         
24515         
24516         if(this.rendered){
24517             
24518             //for (var i =0; i < this.toolbars.length;i++) {
24519             //    // fixme - ask toolbars for heights?
24520             //    this.toolbars[i].onDestroy();
24521            // }
24522             
24523             //this.wrap.dom.innerHTML = '';
24524             //this.wrap.remove();
24525         }
24526     },
24527
24528     // private
24529     onFirstFocus : function(){
24530         
24531         this.assignDocWin();
24532         
24533         
24534         this.activated = true;
24535          
24536     
24537         if(Roo.isGecko){ // prevent silly gecko errors
24538             this.win.focus();
24539             var s = this.win.getSelection();
24540             if(!s.focusNode || s.focusNode.nodeType != 3){
24541                 var r = s.getRangeAt(0);
24542                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24543                 r.collapse(true);
24544                 this.deferFocus();
24545             }
24546             try{
24547                 this.execCmd('useCSS', true);
24548                 this.execCmd('styleWithCSS', false);
24549             }catch(e){}
24550         }
24551         this.owner.fireEvent('activate', this);
24552     },
24553
24554     // private
24555     adjustFont: function(btn){
24556         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24557         //if(Roo.isSafari){ // safari
24558         //    adjust *= 2;
24559        // }
24560         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24561         if(Roo.isSafari){ // safari
24562             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24563             v =  (v < 10) ? 10 : v;
24564             v =  (v > 48) ? 48 : v;
24565             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24566             
24567         }
24568         
24569         
24570         v = Math.max(1, v+adjust);
24571         
24572         this.execCmd('FontSize', v  );
24573     },
24574
24575     onEditorEvent : function(e)
24576     {
24577         this.owner.fireEvent('editorevent', this, e);
24578       //  this.updateToolbar();
24579         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24580     },
24581
24582     insertTag : function(tg)
24583     {
24584         // could be a bit smarter... -> wrap the current selected tRoo..
24585         if (tg.toLowerCase() == 'span' ||
24586             tg.toLowerCase() == 'code' ||
24587             tg.toLowerCase() == 'sup' ||
24588             tg.toLowerCase() == 'sub' 
24589             ) {
24590             
24591             range = this.createRange(this.getSelection());
24592             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24593             wrappingNode.appendChild(range.extractContents());
24594             range.insertNode(wrappingNode);
24595
24596             return;
24597             
24598             
24599             
24600         }
24601         this.execCmd("formatblock",   tg);
24602         
24603     },
24604     
24605     insertText : function(txt)
24606     {
24607         
24608         
24609         var range = this.createRange();
24610         range.deleteContents();
24611                //alert(Sender.getAttribute('label'));
24612                
24613         range.insertNode(this.doc.createTextNode(txt));
24614     } ,
24615     
24616      
24617
24618     /**
24619      * Executes a Midas editor command on the editor document and performs necessary focus and
24620      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24621      * @param {String} cmd The Midas command
24622      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24623      */
24624     relayCmd : function(cmd, value){
24625         this.win.focus();
24626         this.execCmd(cmd, value);
24627         this.owner.fireEvent('editorevent', this);
24628         //this.updateToolbar();
24629         this.owner.deferFocus();
24630     },
24631
24632     /**
24633      * Executes a Midas editor command directly on the editor document.
24634      * For visual commands, you should use {@link #relayCmd} instead.
24635      * <b>This should only be called after the editor is initialized.</b>
24636      * @param {String} cmd The Midas command
24637      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24638      */
24639     execCmd : function(cmd, value){
24640         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24641         this.syncValue();
24642     },
24643  
24644  
24645    
24646     /**
24647      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24648      * to insert tRoo.
24649      * @param {String} text | dom node.. 
24650      */
24651     insertAtCursor : function(text)
24652     {
24653         
24654         if(!this.activated){
24655             return;
24656         }
24657         /*
24658         if(Roo.isIE){
24659             this.win.focus();
24660             var r = this.doc.selection.createRange();
24661             if(r){
24662                 r.collapse(true);
24663                 r.pasteHTML(text);
24664                 this.syncValue();
24665                 this.deferFocus();
24666             
24667             }
24668             return;
24669         }
24670         */
24671         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24672             this.win.focus();
24673             
24674             
24675             // from jquery ui (MIT licenced)
24676             var range, node;
24677             var win = this.win;
24678             
24679             if (win.getSelection && win.getSelection().getRangeAt) {
24680                 range = win.getSelection().getRangeAt(0);
24681                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24682                 range.insertNode(node);
24683             } else if (win.document.selection && win.document.selection.createRange) {
24684                 // no firefox support
24685                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24686                 win.document.selection.createRange().pasteHTML(txt);
24687             } else {
24688                 // no firefox support
24689                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24690                 this.execCmd('InsertHTML', txt);
24691             } 
24692             
24693             this.syncValue();
24694             
24695             this.deferFocus();
24696         }
24697     },
24698  // private
24699     mozKeyPress : function(e){
24700         if(e.ctrlKey){
24701             var c = e.getCharCode(), cmd;
24702           
24703             if(c > 0){
24704                 c = String.fromCharCode(c).toLowerCase();
24705                 switch(c){
24706                     case 'b':
24707                         cmd = 'bold';
24708                         break;
24709                     case 'i':
24710                         cmd = 'italic';
24711                         break;
24712                     
24713                     case 'u':
24714                         cmd = 'underline';
24715                         break;
24716                     
24717                     case 'v':
24718                         this.cleanUpPaste.defer(100, this);
24719                         return;
24720                         
24721                 }
24722                 if(cmd){
24723                     this.win.focus();
24724                     this.execCmd(cmd);
24725                     this.deferFocus();
24726                     e.preventDefault();
24727                 }
24728                 
24729             }
24730         }
24731     },
24732
24733     // private
24734     fixKeys : function(){ // load time branching for fastest keydown performance
24735         if(Roo.isIE){
24736             return function(e){
24737                 var k = e.getKey(), r;
24738                 if(k == e.TAB){
24739                     e.stopEvent();
24740                     r = this.doc.selection.createRange();
24741                     if(r){
24742                         r.collapse(true);
24743                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24744                         this.deferFocus();
24745                     }
24746                     return;
24747                 }
24748                 
24749                 if(k == e.ENTER){
24750                     r = this.doc.selection.createRange();
24751                     if(r){
24752                         var target = r.parentElement();
24753                         if(!target || target.tagName.toLowerCase() != 'li'){
24754                             e.stopEvent();
24755                             r.pasteHTML('<br />');
24756                             r.collapse(false);
24757                             r.select();
24758                         }
24759                     }
24760                 }
24761                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24762                     this.cleanUpPaste.defer(100, this);
24763                     return;
24764                 }
24765                 
24766                 
24767             };
24768         }else if(Roo.isOpera){
24769             return function(e){
24770                 var k = e.getKey();
24771                 if(k == e.TAB){
24772                     e.stopEvent();
24773                     this.win.focus();
24774                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24775                     this.deferFocus();
24776                 }
24777                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24778                     this.cleanUpPaste.defer(100, this);
24779                     return;
24780                 }
24781                 
24782             };
24783         }else if(Roo.isSafari){
24784             return function(e){
24785                 var k = e.getKey();
24786                 
24787                 if(k == e.TAB){
24788                     e.stopEvent();
24789                     this.execCmd('InsertText','\t');
24790                     this.deferFocus();
24791                     return;
24792                 }
24793                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24794                     this.cleanUpPaste.defer(100, this);
24795                     return;
24796                 }
24797                 
24798              };
24799         }
24800     }(),
24801     
24802     getAllAncestors: function()
24803     {
24804         var p = this.getSelectedNode();
24805         var a = [];
24806         if (!p) {
24807             a.push(p); // push blank onto stack..
24808             p = this.getParentElement();
24809         }
24810         
24811         
24812         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24813             a.push(p);
24814             p = p.parentNode;
24815         }
24816         a.push(this.doc.body);
24817         return a;
24818     },
24819     lastSel : false,
24820     lastSelNode : false,
24821     
24822     
24823     getSelection : function() 
24824     {
24825         this.assignDocWin();
24826         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24827     },
24828     
24829     getSelectedNode: function() 
24830     {
24831         // this may only work on Gecko!!!
24832         
24833         // should we cache this!!!!
24834         
24835         
24836         
24837          
24838         var range = this.createRange(this.getSelection()).cloneRange();
24839         
24840         if (Roo.isIE) {
24841             var parent = range.parentElement();
24842             while (true) {
24843                 var testRange = range.duplicate();
24844                 testRange.moveToElementText(parent);
24845                 if (testRange.inRange(range)) {
24846                     break;
24847                 }
24848                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24849                     break;
24850                 }
24851                 parent = parent.parentElement;
24852             }
24853             return parent;
24854         }
24855         
24856         // is ancestor a text element.
24857         var ac =  range.commonAncestorContainer;
24858         if (ac.nodeType == 3) {
24859             ac = ac.parentNode;
24860         }
24861         
24862         var ar = ac.childNodes;
24863          
24864         var nodes = [];
24865         var other_nodes = [];
24866         var has_other_nodes = false;
24867         for (var i=0;i<ar.length;i++) {
24868             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24869                 continue;
24870             }
24871             // fullly contained node.
24872             
24873             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24874                 nodes.push(ar[i]);
24875                 continue;
24876             }
24877             
24878             // probably selected..
24879             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24880                 other_nodes.push(ar[i]);
24881                 continue;
24882             }
24883             // outer..
24884             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24885                 continue;
24886             }
24887             
24888             
24889             has_other_nodes = true;
24890         }
24891         if (!nodes.length && other_nodes.length) {
24892             nodes= other_nodes;
24893         }
24894         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24895             return false;
24896         }
24897         
24898         return nodes[0];
24899     },
24900     createRange: function(sel)
24901     {
24902         // this has strange effects when using with 
24903         // top toolbar - not sure if it's a great idea.
24904         //this.editor.contentWindow.focus();
24905         if (typeof sel != "undefined") {
24906             try {
24907                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24908             } catch(e) {
24909                 return this.doc.createRange();
24910             }
24911         } else {
24912             return this.doc.createRange();
24913         }
24914     },
24915     getParentElement: function()
24916     {
24917         
24918         this.assignDocWin();
24919         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24920         
24921         var range = this.createRange(sel);
24922          
24923         try {
24924             var p = range.commonAncestorContainer;
24925             while (p.nodeType == 3) { // text node
24926                 p = p.parentNode;
24927             }
24928             return p;
24929         } catch (e) {
24930             return null;
24931         }
24932     
24933     },
24934     /***
24935      *
24936      * Range intersection.. the hard stuff...
24937      *  '-1' = before
24938      *  '0' = hits..
24939      *  '1' = after.
24940      *         [ -- selected range --- ]
24941      *   [fail]                        [fail]
24942      *
24943      *    basically..
24944      *      if end is before start or  hits it. fail.
24945      *      if start is after end or hits it fail.
24946      *
24947      *   if either hits (but other is outside. - then it's not 
24948      *   
24949      *    
24950      **/
24951     
24952     
24953     // @see http://www.thismuchiknow.co.uk/?p=64.
24954     rangeIntersectsNode : function(range, node)
24955     {
24956         var nodeRange = node.ownerDocument.createRange();
24957         try {
24958             nodeRange.selectNode(node);
24959         } catch (e) {
24960             nodeRange.selectNodeContents(node);
24961         }
24962     
24963         var rangeStartRange = range.cloneRange();
24964         rangeStartRange.collapse(true);
24965     
24966         var rangeEndRange = range.cloneRange();
24967         rangeEndRange.collapse(false);
24968     
24969         var nodeStartRange = nodeRange.cloneRange();
24970         nodeStartRange.collapse(true);
24971     
24972         var nodeEndRange = nodeRange.cloneRange();
24973         nodeEndRange.collapse(false);
24974     
24975         return rangeStartRange.compareBoundaryPoints(
24976                  Range.START_TO_START, nodeEndRange) == -1 &&
24977                rangeEndRange.compareBoundaryPoints(
24978                  Range.START_TO_START, nodeStartRange) == 1;
24979         
24980          
24981     },
24982     rangeCompareNode : function(range, node)
24983     {
24984         var nodeRange = node.ownerDocument.createRange();
24985         try {
24986             nodeRange.selectNode(node);
24987         } catch (e) {
24988             nodeRange.selectNodeContents(node);
24989         }
24990         
24991         
24992         range.collapse(true);
24993     
24994         nodeRange.collapse(true);
24995      
24996         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24997         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24998          
24999         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25000         
25001         var nodeIsBefore   =  ss == 1;
25002         var nodeIsAfter    = ee == -1;
25003         
25004         if (nodeIsBefore && nodeIsAfter) {
25005             return 0; // outer
25006         }
25007         if (!nodeIsBefore && nodeIsAfter) {
25008             return 1; //right trailed.
25009         }
25010         
25011         if (nodeIsBefore && !nodeIsAfter) {
25012             return 2;  // left trailed.
25013         }
25014         // fully contined.
25015         return 3;
25016     },
25017
25018     // private? - in a new class?
25019     cleanUpPaste :  function()
25020     {
25021         // cleans up the whole document..
25022         Roo.log('cleanuppaste');
25023         
25024         this.cleanUpChildren(this.doc.body);
25025         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25026         if (clean != this.doc.body.innerHTML) {
25027             this.doc.body.innerHTML = clean;
25028         }
25029         
25030     },
25031     
25032     cleanWordChars : function(input) {// change the chars to hex code
25033         var he = Roo.HtmlEditorCore;
25034         
25035         var output = input;
25036         Roo.each(he.swapCodes, function(sw) { 
25037             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25038             
25039             output = output.replace(swapper, sw[1]);
25040         });
25041         
25042         return output;
25043     },
25044     
25045     
25046     cleanUpChildren : function (n)
25047     {
25048         if (!n.childNodes.length) {
25049             return;
25050         }
25051         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25052            this.cleanUpChild(n.childNodes[i]);
25053         }
25054     },
25055     
25056     
25057         
25058     
25059     cleanUpChild : function (node)
25060     {
25061         var ed = this;
25062         //console.log(node);
25063         if (node.nodeName == "#text") {
25064             // clean up silly Windows -- stuff?
25065             return; 
25066         }
25067         if (node.nodeName == "#comment") {
25068             node.parentNode.removeChild(node);
25069             // clean up silly Windows -- stuff?
25070             return; 
25071         }
25072         var lcname = node.tagName.toLowerCase();
25073         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25074         // whitelist of tags..
25075         
25076         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25077             // remove node.
25078             node.parentNode.removeChild(node);
25079             return;
25080             
25081         }
25082         
25083         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25084         
25085         // spans with no attributes - just remove them..
25086         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25087             remove_keep_children = true;
25088         }
25089         
25090         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25091         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25092         
25093         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25094         //    remove_keep_children = true;
25095         //}
25096         
25097         if (remove_keep_children) {
25098             this.cleanUpChildren(node);
25099             // inserts everything just before this node...
25100             while (node.childNodes.length) {
25101                 var cn = node.childNodes[0];
25102                 node.removeChild(cn);
25103                 node.parentNode.insertBefore(cn, node);
25104             }
25105             node.parentNode.removeChild(node);
25106             return;
25107         }
25108         
25109         if (!node.attributes || !node.attributes.length) {
25110             
25111           
25112             
25113             
25114             this.cleanUpChildren(node);
25115             return;
25116         }
25117         
25118         function cleanAttr(n,v)
25119         {
25120             
25121             if (v.match(/^\./) || v.match(/^\//)) {
25122                 return;
25123             }
25124             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25125                 return;
25126             }
25127             if (v.match(/^#/)) {
25128                 return;
25129             }
25130             if (v.match(/^\{/)) { // allow template editing.
25131                 return;
25132             }
25133 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25134             node.removeAttribute(n);
25135             
25136         }
25137         
25138         var cwhite = this.cwhite;
25139         var cblack = this.cblack;
25140             
25141         function cleanStyle(n,v)
25142         {
25143             if (v.match(/expression/)) { //XSS?? should we even bother..
25144                 node.removeAttribute(n);
25145                 return;
25146             }
25147             
25148             var parts = v.split(/;/);
25149             var clean = [];
25150             
25151             Roo.each(parts, function(p) {
25152                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25153                 if (!p.length) {
25154                     return true;
25155                 }
25156                 var l = p.split(':').shift().replace(/\s+/g,'');
25157                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25158                 
25159                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25160 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25161                     //node.removeAttribute(n);
25162                     return true;
25163                 }
25164                 //Roo.log()
25165                 // only allow 'c whitelisted system attributes'
25166                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25167 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25168                     //node.removeAttribute(n);
25169                     return true;
25170                 }
25171                 
25172                 
25173                  
25174                 
25175                 clean.push(p);
25176                 return true;
25177             });
25178             if (clean.length) { 
25179                 node.setAttribute(n, clean.join(';'));
25180             } else {
25181                 node.removeAttribute(n);
25182             }
25183             
25184         }
25185         
25186         
25187         for (var i = node.attributes.length-1; i > -1 ; i--) {
25188             var a = node.attributes[i];
25189             //console.log(a);
25190             
25191             if (a.name.toLowerCase().substr(0,2)=='on')  {
25192                 node.removeAttribute(a.name);
25193                 continue;
25194             }
25195             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25196                 node.removeAttribute(a.name);
25197                 continue;
25198             }
25199             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25200                 cleanAttr(a.name,a.value); // fixme..
25201                 continue;
25202             }
25203             if (a.name == 'style') {
25204                 cleanStyle(a.name,a.value);
25205                 continue;
25206             }
25207             /// clean up MS crap..
25208             // tecnically this should be a list of valid class'es..
25209             
25210             
25211             if (a.name == 'class') {
25212                 if (a.value.match(/^Mso/)) {
25213                     node.removeAttribute('class');
25214                 }
25215                 
25216                 if (a.value.match(/^body$/)) {
25217                     node.removeAttribute('class');
25218                 }
25219                 continue;
25220             }
25221             
25222             // style cleanup!?
25223             // class cleanup?
25224             
25225         }
25226         
25227         
25228         this.cleanUpChildren(node);
25229         
25230         
25231     },
25232     
25233     /**
25234      * Clean up MS wordisms...
25235      */
25236     cleanWord : function(node)
25237     {
25238         if (!node) {
25239             this.cleanWord(this.doc.body);
25240             return;
25241         }
25242         
25243         if(
25244                 node.nodeName == 'SPAN' &&
25245                 !node.hasAttributes() &&
25246                 node.childNodes.length == 1 &&
25247                 node.firstChild.nodeName == "#text"  
25248         ) {
25249             var textNode = node.firstChild;
25250             node.removeChild(textNode);
25251             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25252                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25253             }
25254             node.parentNode.insertBefore(textNode, node);
25255             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25256                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25257             }
25258             node.parentNode.removeChild(node);
25259         }
25260         
25261         if (node.nodeName == "#text") {
25262             // clean up silly Windows -- stuff?
25263             return; 
25264         }
25265         if (node.nodeName == "#comment") {
25266             node.parentNode.removeChild(node);
25267             // clean up silly Windows -- stuff?
25268             return; 
25269         }
25270         
25271         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25272             node.parentNode.removeChild(node);
25273             return;
25274         }
25275         //Roo.log(node.tagName);
25276         // remove - but keep children..
25277         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25278             //Roo.log('-- removed');
25279             while (node.childNodes.length) {
25280                 var cn = node.childNodes[0];
25281                 node.removeChild(cn);
25282                 node.parentNode.insertBefore(cn, node);
25283                 // move node to parent - and clean it..
25284                 this.cleanWord(cn);
25285             }
25286             node.parentNode.removeChild(node);
25287             /// no need to iterate chidlren = it's got none..
25288             //this.iterateChildren(node, this.cleanWord);
25289             return;
25290         }
25291         // clean styles
25292         if (node.className.length) {
25293             
25294             var cn = node.className.split(/\W+/);
25295             var cna = [];
25296             Roo.each(cn, function(cls) {
25297                 if (cls.match(/Mso[a-zA-Z]+/)) {
25298                     return;
25299                 }
25300                 cna.push(cls);
25301             });
25302             node.className = cna.length ? cna.join(' ') : '';
25303             if (!cna.length) {
25304                 node.removeAttribute("class");
25305             }
25306         }
25307         
25308         if (node.hasAttribute("lang")) {
25309             node.removeAttribute("lang");
25310         }
25311         
25312         if (node.hasAttribute("style")) {
25313             
25314             var styles = node.getAttribute("style").split(";");
25315             var nstyle = [];
25316             Roo.each(styles, function(s) {
25317                 if (!s.match(/:/)) {
25318                     return;
25319                 }
25320                 var kv = s.split(":");
25321                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25322                     return;
25323                 }
25324                 // what ever is left... we allow.
25325                 nstyle.push(s);
25326             });
25327             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25328             if (!nstyle.length) {
25329                 node.removeAttribute('style');
25330             }
25331         }
25332         this.iterateChildren(node, this.cleanWord);
25333         
25334         
25335         
25336     },
25337     /**
25338      * iterateChildren of a Node, calling fn each time, using this as the scole..
25339      * @param {DomNode} node node to iterate children of.
25340      * @param {Function} fn method of this class to call on each item.
25341      */
25342     iterateChildren : function(node, fn)
25343     {
25344         if (!node.childNodes.length) {
25345                 return;
25346         }
25347         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25348            fn.call(this, node.childNodes[i])
25349         }
25350     },
25351     
25352     
25353     /**
25354      * cleanTableWidths.
25355      *
25356      * Quite often pasting from word etc.. results in tables with column and widths.
25357      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25358      *
25359      */
25360     cleanTableWidths : function(node)
25361     {
25362          
25363          
25364         if (!node) {
25365             this.cleanTableWidths(this.doc.body);
25366             return;
25367         }
25368         
25369         // ignore list...
25370         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25371             return; 
25372         }
25373         Roo.log(node.tagName);
25374         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25375             this.iterateChildren(node, this.cleanTableWidths);
25376             return;
25377         }
25378         if (node.hasAttribute('width')) {
25379             node.removeAttribute('width');
25380         }
25381         
25382          
25383         if (node.hasAttribute("style")) {
25384             // pretty basic...
25385             
25386             var styles = node.getAttribute("style").split(";");
25387             var nstyle = [];
25388             Roo.each(styles, function(s) {
25389                 if (!s.match(/:/)) {
25390                     return;
25391                 }
25392                 var kv = s.split(":");
25393                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25394                     return;
25395                 }
25396                 // what ever is left... we allow.
25397                 nstyle.push(s);
25398             });
25399             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25400             if (!nstyle.length) {
25401                 node.removeAttribute('style');
25402             }
25403         }
25404         
25405         this.iterateChildren(node, this.cleanTableWidths);
25406         
25407         
25408     },
25409     
25410     
25411     
25412     
25413     domToHTML : function(currentElement, depth, nopadtext) {
25414         
25415         depth = depth || 0;
25416         nopadtext = nopadtext || false;
25417     
25418         if (!currentElement) {
25419             return this.domToHTML(this.doc.body);
25420         }
25421         
25422         //Roo.log(currentElement);
25423         var j;
25424         var allText = false;
25425         var nodeName = currentElement.nodeName;
25426         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25427         
25428         if  (nodeName == '#text') {
25429             
25430             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25431         }
25432         
25433         
25434         var ret = '';
25435         if (nodeName != 'BODY') {
25436              
25437             var i = 0;
25438             // Prints the node tagName, such as <A>, <IMG>, etc
25439             if (tagName) {
25440                 var attr = [];
25441                 for(i = 0; i < currentElement.attributes.length;i++) {
25442                     // quoting?
25443                     var aname = currentElement.attributes.item(i).name;
25444                     if (!currentElement.attributes.item(i).value.length) {
25445                         continue;
25446                     }
25447                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25448                 }
25449                 
25450                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25451             } 
25452             else {
25453                 
25454                 // eack
25455             }
25456         } else {
25457             tagName = false;
25458         }
25459         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25460             return ret;
25461         }
25462         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25463             nopadtext = true;
25464         }
25465         
25466         
25467         // Traverse the tree
25468         i = 0;
25469         var currentElementChild = currentElement.childNodes.item(i);
25470         var allText = true;
25471         var innerHTML  = '';
25472         lastnode = '';
25473         while (currentElementChild) {
25474             // Formatting code (indent the tree so it looks nice on the screen)
25475             var nopad = nopadtext;
25476             if (lastnode == 'SPAN') {
25477                 nopad  = true;
25478             }
25479             // text
25480             if  (currentElementChild.nodeName == '#text') {
25481                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25482                 toadd = nopadtext ? toadd : toadd.trim();
25483                 if (!nopad && toadd.length > 80) {
25484                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25485                 }
25486                 innerHTML  += toadd;
25487                 
25488                 i++;
25489                 currentElementChild = currentElement.childNodes.item(i);
25490                 lastNode = '';
25491                 continue;
25492             }
25493             allText = false;
25494             
25495             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25496                 
25497             // Recursively traverse the tree structure of the child node
25498             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25499             lastnode = currentElementChild.nodeName;
25500             i++;
25501             currentElementChild=currentElement.childNodes.item(i);
25502         }
25503         
25504         ret += innerHTML;
25505         
25506         if (!allText) {
25507                 // The remaining code is mostly for formatting the tree
25508             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25509         }
25510         
25511         
25512         if (tagName) {
25513             ret+= "</"+tagName+">";
25514         }
25515         return ret;
25516         
25517     },
25518         
25519     applyBlacklists : function()
25520     {
25521         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25522         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25523         
25524         this.white = [];
25525         this.black = [];
25526         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25527             if (b.indexOf(tag) > -1) {
25528                 return;
25529             }
25530             this.white.push(tag);
25531             
25532         }, this);
25533         
25534         Roo.each(w, function(tag) {
25535             if (b.indexOf(tag) > -1) {
25536                 return;
25537             }
25538             if (this.white.indexOf(tag) > -1) {
25539                 return;
25540             }
25541             this.white.push(tag);
25542             
25543         }, this);
25544         
25545         
25546         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25547             if (w.indexOf(tag) > -1) {
25548                 return;
25549             }
25550             this.black.push(tag);
25551             
25552         }, this);
25553         
25554         Roo.each(b, function(tag) {
25555             if (w.indexOf(tag) > -1) {
25556                 return;
25557             }
25558             if (this.black.indexOf(tag) > -1) {
25559                 return;
25560             }
25561             this.black.push(tag);
25562             
25563         }, this);
25564         
25565         
25566         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25567         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25568         
25569         this.cwhite = [];
25570         this.cblack = [];
25571         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25572             if (b.indexOf(tag) > -1) {
25573                 return;
25574             }
25575             this.cwhite.push(tag);
25576             
25577         }, this);
25578         
25579         Roo.each(w, function(tag) {
25580             if (b.indexOf(tag) > -1) {
25581                 return;
25582             }
25583             if (this.cwhite.indexOf(tag) > -1) {
25584                 return;
25585             }
25586             this.cwhite.push(tag);
25587             
25588         }, this);
25589         
25590         
25591         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25592             if (w.indexOf(tag) > -1) {
25593                 return;
25594             }
25595             this.cblack.push(tag);
25596             
25597         }, this);
25598         
25599         Roo.each(b, function(tag) {
25600             if (w.indexOf(tag) > -1) {
25601                 return;
25602             }
25603             if (this.cblack.indexOf(tag) > -1) {
25604                 return;
25605             }
25606             this.cblack.push(tag);
25607             
25608         }, this);
25609     },
25610     
25611     setStylesheets : function(stylesheets)
25612     {
25613         if(typeof(stylesheets) == 'string'){
25614             Roo.get(this.iframe.contentDocument.head).createChild({
25615                 tag : 'link',
25616                 rel : 'stylesheet',
25617                 type : 'text/css',
25618                 href : stylesheets
25619             });
25620             
25621             return;
25622         }
25623         var _this = this;
25624      
25625         Roo.each(stylesheets, function(s) {
25626             if(!s.length){
25627                 return;
25628             }
25629             
25630             Roo.get(_this.iframe.contentDocument.head).createChild({
25631                 tag : 'link',
25632                 rel : 'stylesheet',
25633                 type : 'text/css',
25634                 href : s
25635             });
25636         });
25637
25638         
25639     },
25640     
25641     removeStylesheets : function()
25642     {
25643         var _this = this;
25644         
25645         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25646             s.remove();
25647         });
25648     },
25649     
25650     setStyle : function(style)
25651     {
25652         Roo.get(this.iframe.contentDocument.head).createChild({
25653             tag : 'style',
25654             type : 'text/css',
25655             html : style
25656         });
25657
25658         return;
25659     }
25660     
25661     // hide stuff that is not compatible
25662     /**
25663      * @event blur
25664      * @hide
25665      */
25666     /**
25667      * @event change
25668      * @hide
25669      */
25670     /**
25671      * @event focus
25672      * @hide
25673      */
25674     /**
25675      * @event specialkey
25676      * @hide
25677      */
25678     /**
25679      * @cfg {String} fieldClass @hide
25680      */
25681     /**
25682      * @cfg {String} focusClass @hide
25683      */
25684     /**
25685      * @cfg {String} autoCreate @hide
25686      */
25687     /**
25688      * @cfg {String} inputType @hide
25689      */
25690     /**
25691      * @cfg {String} invalidClass @hide
25692      */
25693     /**
25694      * @cfg {String} invalidText @hide
25695      */
25696     /**
25697      * @cfg {String} msgFx @hide
25698      */
25699     /**
25700      * @cfg {String} validateOnBlur @hide
25701      */
25702 });
25703
25704 Roo.HtmlEditorCore.white = [
25705         'area', 'br', 'img', 'input', 'hr', 'wbr',
25706         
25707        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25708        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25709        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25710        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25711        'table',   'ul',         'xmp', 
25712        
25713        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25714       'thead',   'tr', 
25715      
25716       'dir', 'menu', 'ol', 'ul', 'dl',
25717        
25718       'embed',  'object'
25719 ];
25720
25721
25722 Roo.HtmlEditorCore.black = [
25723     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25724         'applet', // 
25725         'base',   'basefont', 'bgsound', 'blink',  'body', 
25726         'frame',  'frameset', 'head',    'html',   'ilayer', 
25727         'iframe', 'layer',  'link',     'meta',    'object',   
25728         'script', 'style' ,'title',  'xml' // clean later..
25729 ];
25730 Roo.HtmlEditorCore.clean = [
25731     'script', 'style', 'title', 'xml'
25732 ];
25733 Roo.HtmlEditorCore.remove = [
25734     'font'
25735 ];
25736 // attributes..
25737
25738 Roo.HtmlEditorCore.ablack = [
25739     'on'
25740 ];
25741     
25742 Roo.HtmlEditorCore.aclean = [ 
25743     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25744 ];
25745
25746 // protocols..
25747 Roo.HtmlEditorCore.pwhite= [
25748         'http',  'https',  'mailto'
25749 ];
25750
25751 // white listed style attributes.
25752 Roo.HtmlEditorCore.cwhite= [
25753       //  'text-align', /// default is to allow most things..
25754       
25755          
25756 //        'font-size'//??
25757 ];
25758
25759 // black listed style attributes.
25760 Roo.HtmlEditorCore.cblack= [
25761       //  'font-size' -- this can be set by the project 
25762 ];
25763
25764
25765 Roo.HtmlEditorCore.swapCodes   =[ 
25766     [    8211, "--" ], 
25767     [    8212, "--" ], 
25768     [    8216,  "'" ],  
25769     [    8217, "'" ],  
25770     [    8220, '"' ],  
25771     [    8221, '"' ],  
25772     [    8226, "*" ],  
25773     [    8230, "..." ]
25774 ]; 
25775
25776     /*
25777  * - LGPL
25778  *
25779  * HtmlEditor
25780  * 
25781  */
25782
25783 /**
25784  * @class Roo.bootstrap.HtmlEditor
25785  * @extends Roo.bootstrap.TextArea
25786  * Bootstrap HtmlEditor class
25787
25788  * @constructor
25789  * Create a new HtmlEditor
25790  * @param {Object} config The config object
25791  */
25792
25793 Roo.bootstrap.HtmlEditor = function(config){
25794     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25795     if (!this.toolbars) {
25796         this.toolbars = [];
25797     }
25798     
25799     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25800     this.addEvents({
25801             /**
25802              * @event initialize
25803              * Fires when the editor is fully initialized (including the iframe)
25804              * @param {HtmlEditor} this
25805              */
25806             initialize: true,
25807             /**
25808              * @event activate
25809              * Fires when the editor is first receives the focus. Any insertion must wait
25810              * until after this event.
25811              * @param {HtmlEditor} this
25812              */
25813             activate: true,
25814              /**
25815              * @event beforesync
25816              * Fires before the textarea is updated with content from the editor iframe. Return false
25817              * to cancel the sync.
25818              * @param {HtmlEditor} this
25819              * @param {String} html
25820              */
25821             beforesync: true,
25822              /**
25823              * @event beforepush
25824              * Fires before the iframe editor is updated with content from the textarea. Return false
25825              * to cancel the push.
25826              * @param {HtmlEditor} this
25827              * @param {String} html
25828              */
25829             beforepush: true,
25830              /**
25831              * @event sync
25832              * Fires when the textarea is updated with content from the editor iframe.
25833              * @param {HtmlEditor} this
25834              * @param {String} html
25835              */
25836             sync: true,
25837              /**
25838              * @event push
25839              * Fires when the iframe editor is updated with content from the textarea.
25840              * @param {HtmlEditor} this
25841              * @param {String} html
25842              */
25843             push: true,
25844              /**
25845              * @event editmodechange
25846              * Fires when the editor switches edit modes
25847              * @param {HtmlEditor} this
25848              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25849              */
25850             editmodechange: true,
25851             /**
25852              * @event editorevent
25853              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25854              * @param {HtmlEditor} this
25855              */
25856             editorevent: true,
25857             /**
25858              * @event firstfocus
25859              * Fires when on first focus - needed by toolbars..
25860              * @param {HtmlEditor} this
25861              */
25862             firstfocus: true,
25863             /**
25864              * @event autosave
25865              * Auto save the htmlEditor value as a file into Events
25866              * @param {HtmlEditor} this
25867              */
25868             autosave: true,
25869             /**
25870              * @event savedpreview
25871              * preview the saved version of htmlEditor
25872              * @param {HtmlEditor} this
25873              */
25874             savedpreview: true
25875         });
25876 };
25877
25878
25879 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25880     
25881     
25882       /**
25883      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25884      */
25885     toolbars : false,
25886     
25887      /**
25888     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25889     */
25890     btns : [],
25891    
25892      /**
25893      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25894      *                        Roo.resizable.
25895      */
25896     resizable : false,
25897      /**
25898      * @cfg {Number} height (in pixels)
25899      */   
25900     height: 300,
25901    /**
25902      * @cfg {Number} width (in pixels)
25903      */   
25904     width: false,
25905     
25906     /**
25907      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25908      * 
25909      */
25910     stylesheets: false,
25911     
25912     // id of frame..
25913     frameId: false,
25914     
25915     // private properties
25916     validationEvent : false,
25917     deferHeight: true,
25918     initialized : false,
25919     activated : false,
25920     
25921     onFocus : Roo.emptyFn,
25922     iframePad:3,
25923     hideMode:'offsets',
25924     
25925     tbContainer : false,
25926     
25927     bodyCls : '',
25928     
25929     toolbarContainer :function() {
25930         return this.wrap.select('.x-html-editor-tb',true).first();
25931     },
25932
25933     /**
25934      * Protected method that will not generally be called directly. It
25935      * is called when the editor creates its toolbar. Override this method if you need to
25936      * add custom toolbar buttons.
25937      * @param {HtmlEditor} editor
25938      */
25939     createToolbar : function(){
25940         Roo.log('renewing');
25941         Roo.log("create toolbars");
25942         
25943         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25944         this.toolbars[0].render(this.toolbarContainer());
25945         
25946         return;
25947         
25948 //        if (!editor.toolbars || !editor.toolbars.length) {
25949 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25950 //        }
25951 //        
25952 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25953 //            editor.toolbars[i] = Roo.factory(
25954 //                    typeof(editor.toolbars[i]) == 'string' ?
25955 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25956 //                Roo.bootstrap.HtmlEditor);
25957 //            editor.toolbars[i].init(editor);
25958 //        }
25959     },
25960
25961      
25962     // private
25963     onRender : function(ct, position)
25964     {
25965        // Roo.log("Call onRender: " + this.xtype);
25966         var _t = this;
25967         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25968       
25969         this.wrap = this.inputEl().wrap({
25970             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25971         });
25972         
25973         this.editorcore.onRender(ct, position);
25974          
25975         if (this.resizable) {
25976             this.resizeEl = new Roo.Resizable(this.wrap, {
25977                 pinned : true,
25978                 wrap: true,
25979                 dynamic : true,
25980                 minHeight : this.height,
25981                 height: this.height,
25982                 handles : this.resizable,
25983                 width: this.width,
25984                 listeners : {
25985                     resize : function(r, w, h) {
25986                         _t.onResize(w,h); // -something
25987                     }
25988                 }
25989             });
25990             
25991         }
25992         this.createToolbar(this);
25993        
25994         
25995         if(!this.width && this.resizable){
25996             this.setSize(this.wrap.getSize());
25997         }
25998         if (this.resizeEl) {
25999             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26000             // should trigger onReize..
26001         }
26002         
26003     },
26004
26005     // private
26006     onResize : function(w, h)
26007     {
26008         Roo.log('resize: ' +w + ',' + h );
26009         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26010         var ew = false;
26011         var eh = false;
26012         
26013         if(this.inputEl() ){
26014             if(typeof w == 'number'){
26015                 var aw = w - this.wrap.getFrameWidth('lr');
26016                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26017                 ew = aw;
26018             }
26019             if(typeof h == 'number'){
26020                  var tbh = -11;  // fixme it needs to tool bar size!
26021                 for (var i =0; i < this.toolbars.length;i++) {
26022                     // fixme - ask toolbars for heights?
26023                     tbh += this.toolbars[i].el.getHeight();
26024                     //if (this.toolbars[i].footer) {
26025                     //    tbh += this.toolbars[i].footer.el.getHeight();
26026                     //}
26027                 }
26028               
26029                 
26030                 
26031                 
26032                 
26033                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26034                 ah -= 5; // knock a few pixes off for look..
26035                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26036                 var eh = ah;
26037             }
26038         }
26039         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26040         this.editorcore.onResize(ew,eh);
26041         
26042     },
26043
26044     /**
26045      * Toggles the editor between standard and source edit mode.
26046      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26047      */
26048     toggleSourceEdit : function(sourceEditMode)
26049     {
26050         this.editorcore.toggleSourceEdit(sourceEditMode);
26051         
26052         if(this.editorcore.sourceEditMode){
26053             Roo.log('editor - showing textarea');
26054             
26055 //            Roo.log('in');
26056 //            Roo.log(this.syncValue());
26057             this.syncValue();
26058             this.inputEl().removeClass(['hide', 'x-hidden']);
26059             this.inputEl().dom.removeAttribute('tabIndex');
26060             this.inputEl().focus();
26061         }else{
26062             Roo.log('editor - hiding textarea');
26063 //            Roo.log('out')
26064 //            Roo.log(this.pushValue()); 
26065             this.pushValue();
26066             
26067             this.inputEl().addClass(['hide', 'x-hidden']);
26068             this.inputEl().dom.setAttribute('tabIndex', -1);
26069             //this.deferFocus();
26070         }
26071          
26072         if(this.resizable){
26073             this.setSize(this.wrap.getSize());
26074         }
26075         
26076         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26077     },
26078  
26079     // private (for BoxComponent)
26080     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26081
26082     // private (for BoxComponent)
26083     getResizeEl : function(){
26084         return this.wrap;
26085     },
26086
26087     // private (for BoxComponent)
26088     getPositionEl : function(){
26089         return this.wrap;
26090     },
26091
26092     // private
26093     initEvents : function(){
26094         this.originalValue = this.getValue();
26095     },
26096
26097 //    /**
26098 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26099 //     * @method
26100 //     */
26101 //    markInvalid : Roo.emptyFn,
26102 //    /**
26103 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26104 //     * @method
26105 //     */
26106 //    clearInvalid : Roo.emptyFn,
26107
26108     setValue : function(v){
26109         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26110         this.editorcore.pushValue();
26111     },
26112
26113      
26114     // private
26115     deferFocus : function(){
26116         this.focus.defer(10, this);
26117     },
26118
26119     // doc'ed in Field
26120     focus : function(){
26121         this.editorcore.focus();
26122         
26123     },
26124       
26125
26126     // private
26127     onDestroy : function(){
26128         
26129         
26130         
26131         if(this.rendered){
26132             
26133             for (var i =0; i < this.toolbars.length;i++) {
26134                 // fixme - ask toolbars for heights?
26135                 this.toolbars[i].onDestroy();
26136             }
26137             
26138             this.wrap.dom.innerHTML = '';
26139             this.wrap.remove();
26140         }
26141     },
26142
26143     // private
26144     onFirstFocus : function(){
26145         //Roo.log("onFirstFocus");
26146         this.editorcore.onFirstFocus();
26147          for (var i =0; i < this.toolbars.length;i++) {
26148             this.toolbars[i].onFirstFocus();
26149         }
26150         
26151     },
26152     
26153     // private
26154     syncValue : function()
26155     {   
26156         this.editorcore.syncValue();
26157     },
26158     
26159     pushValue : function()
26160     {   
26161         this.editorcore.pushValue();
26162     }
26163      
26164     
26165     // hide stuff that is not compatible
26166     /**
26167      * @event blur
26168      * @hide
26169      */
26170     /**
26171      * @event change
26172      * @hide
26173      */
26174     /**
26175      * @event focus
26176      * @hide
26177      */
26178     /**
26179      * @event specialkey
26180      * @hide
26181      */
26182     /**
26183      * @cfg {String} fieldClass @hide
26184      */
26185     /**
26186      * @cfg {String} focusClass @hide
26187      */
26188     /**
26189      * @cfg {String} autoCreate @hide
26190      */
26191     /**
26192      * @cfg {String} inputType @hide
26193      */
26194      
26195     /**
26196      * @cfg {String} invalidText @hide
26197      */
26198     /**
26199      * @cfg {String} msgFx @hide
26200      */
26201     /**
26202      * @cfg {String} validateOnBlur @hide
26203      */
26204 });
26205  
26206     
26207    
26208    
26209    
26210       
26211 Roo.namespace('Roo.bootstrap.htmleditor');
26212 /**
26213  * @class Roo.bootstrap.HtmlEditorToolbar1
26214  * Basic Toolbar
26215  * 
26216  * @example
26217  * Usage:
26218  *
26219  new Roo.bootstrap.HtmlEditor({
26220     ....
26221     toolbars : [
26222         new Roo.bootstrap.HtmlEditorToolbar1({
26223             disable : { fonts: 1 , format: 1, ..., ... , ...],
26224             btns : [ .... ]
26225         })
26226     }
26227      
26228  * 
26229  * @cfg {Object} disable List of elements to disable..
26230  * @cfg {Array} btns List of additional buttons.
26231  * 
26232  * 
26233  * NEEDS Extra CSS? 
26234  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26235  */
26236  
26237 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26238 {
26239     
26240     Roo.apply(this, config);
26241     
26242     // default disabled, based on 'good practice'..
26243     this.disable = this.disable || {};
26244     Roo.applyIf(this.disable, {
26245         fontSize : true,
26246         colors : true,
26247         specialElements : true
26248     });
26249     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26250     
26251     this.editor = config.editor;
26252     this.editorcore = config.editor.editorcore;
26253     
26254     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26255     
26256     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26257     // dont call parent... till later.
26258 }
26259 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26260      
26261     bar : true,
26262     
26263     editor : false,
26264     editorcore : false,
26265     
26266     
26267     formats : [
26268         "p" ,  
26269         "h1","h2","h3","h4","h5","h6", 
26270         "pre", "code", 
26271         "abbr", "acronym", "address", "cite", "samp", "var",
26272         'div','span'
26273     ],
26274     
26275     onRender : function(ct, position)
26276     {
26277        // Roo.log("Call onRender: " + this.xtype);
26278         
26279        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26280        Roo.log(this.el);
26281        this.el.dom.style.marginBottom = '0';
26282        var _this = this;
26283        var editorcore = this.editorcore;
26284        var editor= this.editor;
26285        
26286        var children = [];
26287        var btn = function(id,cmd , toggle, handler, html){
26288        
26289             var  event = toggle ? 'toggle' : 'click';
26290        
26291             var a = {
26292                 size : 'sm',
26293                 xtype: 'Button',
26294                 xns: Roo.bootstrap,
26295                 //glyphicon : id,
26296                 fa: id,
26297                 cmd : id || cmd,
26298                 enableToggle:toggle !== false,
26299                 html : html || '',
26300                 pressed : toggle ? false : null,
26301                 listeners : {}
26302             };
26303             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26304                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26305             };
26306             children.push(a);
26307             return a;
26308        }
26309        
26310     //    var cb_box = function...
26311         
26312         var style = {
26313                 xtype: 'Button',
26314                 size : 'sm',
26315                 xns: Roo.bootstrap,
26316                 fa : 'font',
26317                 //html : 'submit'
26318                 menu : {
26319                     xtype: 'Menu',
26320                     xns: Roo.bootstrap,
26321                     items:  []
26322                 }
26323         };
26324         Roo.each(this.formats, function(f) {
26325             style.menu.items.push({
26326                 xtype :'MenuItem',
26327                 xns: Roo.bootstrap,
26328                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26329                 tagname : f,
26330                 listeners : {
26331                     click : function()
26332                     {
26333                         editorcore.insertTag(this.tagname);
26334                         editor.focus();
26335                     }
26336                 }
26337                 
26338             });
26339         });
26340         children.push(style);   
26341         
26342         btn('bold',false,true);
26343         btn('italic',false,true);
26344         btn('align-left', 'justifyleft',true);
26345         btn('align-center', 'justifycenter',true);
26346         btn('align-right' , 'justifyright',true);
26347         btn('link', false, false, function(btn) {
26348             //Roo.log("create link?");
26349             var url = prompt(this.createLinkText, this.defaultLinkValue);
26350             if(url && url != 'http:/'+'/'){
26351                 this.editorcore.relayCmd('createlink', url);
26352             }
26353         }),
26354         btn('list','insertunorderedlist',true);
26355         btn('pencil', false,true, function(btn){
26356                 Roo.log(this);
26357                 this.toggleSourceEdit(btn.pressed);
26358         });
26359         
26360         if (this.editor.btns.length > 0) {
26361             for (var i = 0; i<this.editor.btns.length; i++) {
26362                 children.push(this.editor.btns[i]);
26363             }
26364         }
26365         
26366         /*
26367         var cog = {
26368                 xtype: 'Button',
26369                 size : 'sm',
26370                 xns: Roo.bootstrap,
26371                 glyphicon : 'cog',
26372                 //html : 'submit'
26373                 menu : {
26374                     xtype: 'Menu',
26375                     xns: Roo.bootstrap,
26376                     items:  []
26377                 }
26378         };
26379         
26380         cog.menu.items.push({
26381             xtype :'MenuItem',
26382             xns: Roo.bootstrap,
26383             html : Clean styles,
26384             tagname : f,
26385             listeners : {
26386                 click : function()
26387                 {
26388                     editorcore.insertTag(this.tagname);
26389                     editor.focus();
26390                 }
26391             }
26392             
26393         });
26394        */
26395         
26396          
26397        this.xtype = 'NavSimplebar';
26398         
26399         for(var i=0;i< children.length;i++) {
26400             
26401             this.buttons.add(this.addxtypeChild(children[i]));
26402             
26403         }
26404         
26405         editor.on('editorevent', this.updateToolbar, this);
26406     },
26407     onBtnClick : function(id)
26408     {
26409        this.editorcore.relayCmd(id);
26410        this.editorcore.focus();
26411     },
26412     
26413     /**
26414      * Protected method that will not generally be called directly. It triggers
26415      * a toolbar update by reading the markup state of the current selection in the editor.
26416      */
26417     updateToolbar: function(){
26418
26419         if(!this.editorcore.activated){
26420             this.editor.onFirstFocus(); // is this neeed?
26421             return;
26422         }
26423
26424         var btns = this.buttons; 
26425         var doc = this.editorcore.doc;
26426         btns.get('bold').setActive(doc.queryCommandState('bold'));
26427         btns.get('italic').setActive(doc.queryCommandState('italic'));
26428         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26429         
26430         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26431         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26432         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26433         
26434         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26435         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26436          /*
26437         
26438         var ans = this.editorcore.getAllAncestors();
26439         if (this.formatCombo) {
26440             
26441             
26442             var store = this.formatCombo.store;
26443             this.formatCombo.setValue("");
26444             for (var i =0; i < ans.length;i++) {
26445                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26446                     // select it..
26447                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26448                     break;
26449                 }
26450             }
26451         }
26452         
26453         
26454         
26455         // hides menus... - so this cant be on a menu...
26456         Roo.bootstrap.MenuMgr.hideAll();
26457         */
26458         Roo.bootstrap.MenuMgr.hideAll();
26459         //this.editorsyncValue();
26460     },
26461     onFirstFocus: function() {
26462         this.buttons.each(function(item){
26463            item.enable();
26464         });
26465     },
26466     toggleSourceEdit : function(sourceEditMode){
26467         
26468           
26469         if(sourceEditMode){
26470             Roo.log("disabling buttons");
26471            this.buttons.each( function(item){
26472                 if(item.cmd != 'pencil'){
26473                     item.disable();
26474                 }
26475             });
26476           
26477         }else{
26478             Roo.log("enabling buttons");
26479             if(this.editorcore.initialized){
26480                 this.buttons.each( function(item){
26481                     item.enable();
26482                 });
26483             }
26484             
26485         }
26486         Roo.log("calling toggole on editor");
26487         // tell the editor that it's been pressed..
26488         this.editor.toggleSourceEdit(sourceEditMode);
26489        
26490     }
26491 });
26492
26493
26494
26495
26496  
26497 /*
26498  * - LGPL
26499  */
26500
26501 /**
26502  * @class Roo.bootstrap.Markdown
26503  * @extends Roo.bootstrap.TextArea
26504  * Bootstrap Showdown editable area
26505  * @cfg {string} content
26506  * 
26507  * @constructor
26508  * Create a new Showdown
26509  */
26510
26511 Roo.bootstrap.Markdown = function(config){
26512     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26513    
26514 };
26515
26516 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26517     
26518     editing :false,
26519     
26520     initEvents : function()
26521     {
26522         
26523         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26524         this.markdownEl = this.el.createChild({
26525             cls : 'roo-markdown-area'
26526         });
26527         this.inputEl().addClass('d-none');
26528         if (this.getValue() == '') {
26529             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26530             
26531         } else {
26532             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26533         }
26534         this.markdownEl.on('click', this.toggleTextEdit, this);
26535         this.on('blur', this.toggleTextEdit, this);
26536         this.on('specialkey', this.resizeTextArea, this);
26537     },
26538     
26539     toggleTextEdit : function()
26540     {
26541         var sh = this.markdownEl.getHeight();
26542         this.inputEl().addClass('d-none');
26543         this.markdownEl.addClass('d-none');
26544         if (!this.editing) {
26545             // show editor?
26546             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26547             this.inputEl().removeClass('d-none');
26548             this.inputEl().focus();
26549             this.editing = true;
26550             return;
26551         }
26552         // show showdown...
26553         this.updateMarkdown();
26554         this.markdownEl.removeClass('d-none');
26555         this.editing = false;
26556         return;
26557     },
26558     updateMarkdown : function()
26559     {
26560         if (this.getValue() == '') {
26561             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26562             return;
26563         }
26564  
26565         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26566     },
26567     
26568     resizeTextArea: function () {
26569         
26570         var sh = 100;
26571         Roo.log([sh, this.getValue().split("\n").length * 30]);
26572         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26573     },
26574     setValue : function(val)
26575     {
26576         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26577         if (!this.editing) {
26578             this.updateMarkdown();
26579         }
26580         
26581     },
26582     focus : function()
26583     {
26584         if (!this.editing) {
26585             this.toggleTextEdit();
26586         }
26587         
26588     }
26589
26590
26591 });
26592 /**
26593  * @class Roo.bootstrap.Table.AbstractSelectionModel
26594  * @extends Roo.util.Observable
26595  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26596  * implemented by descendant classes.  This class should not be directly instantiated.
26597  * @constructor
26598  */
26599 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26600     this.locked = false;
26601     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26602 };
26603
26604
26605 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26606     /** @ignore Called by the grid automatically. Do not call directly. */
26607     init : function(grid){
26608         this.grid = grid;
26609         this.initEvents();
26610     },
26611
26612     /**
26613      * Locks the selections.
26614      */
26615     lock : function(){
26616         this.locked = true;
26617     },
26618
26619     /**
26620      * Unlocks the selections.
26621      */
26622     unlock : function(){
26623         this.locked = false;
26624     },
26625
26626     /**
26627      * Returns true if the selections are locked.
26628      * @return {Boolean}
26629      */
26630     isLocked : function(){
26631         return this.locked;
26632     },
26633     
26634     
26635     initEvents : function ()
26636     {
26637         
26638     }
26639 });
26640 /**
26641  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26642  * @class Roo.bootstrap.Table.RowSelectionModel
26643  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26644  * It supports multiple selections and keyboard selection/navigation. 
26645  * @constructor
26646  * @param {Object} config
26647  */
26648
26649 Roo.bootstrap.Table.RowSelectionModel = function(config){
26650     Roo.apply(this, config);
26651     this.selections = new Roo.util.MixedCollection(false, function(o){
26652         return o.id;
26653     });
26654
26655     this.last = false;
26656     this.lastActive = false;
26657
26658     this.addEvents({
26659         /**
26660              * @event selectionchange
26661              * Fires when the selection changes
26662              * @param {SelectionModel} this
26663              */
26664             "selectionchange" : true,
26665         /**
26666              * @event afterselectionchange
26667              * Fires after the selection changes (eg. by key press or clicking)
26668              * @param {SelectionModel} this
26669              */
26670             "afterselectionchange" : true,
26671         /**
26672              * @event beforerowselect
26673              * Fires when a row is selected being selected, return false to cancel.
26674              * @param {SelectionModel} this
26675              * @param {Number} rowIndex The selected index
26676              * @param {Boolean} keepExisting False if other selections will be cleared
26677              */
26678             "beforerowselect" : true,
26679         /**
26680              * @event rowselect
26681              * Fires when a row is selected.
26682              * @param {SelectionModel} this
26683              * @param {Number} rowIndex The selected index
26684              * @param {Roo.data.Record} r The record
26685              */
26686             "rowselect" : true,
26687         /**
26688              * @event rowdeselect
26689              * Fires when a row is deselected.
26690              * @param {SelectionModel} this
26691              * @param {Number} rowIndex The selected index
26692              */
26693         "rowdeselect" : true
26694     });
26695     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26696     this.locked = false;
26697  };
26698
26699 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26700     /**
26701      * @cfg {Boolean} singleSelect
26702      * True to allow selection of only one row at a time (defaults to false)
26703      */
26704     singleSelect : false,
26705
26706     // private
26707     initEvents : function()
26708     {
26709
26710         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26711         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26712         //}else{ // allow click to work like normal
26713          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26714         //}
26715         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26716         this.grid.on("rowclick", this.handleMouseDown, this);
26717         
26718         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26719             "up" : function(e){
26720                 if(!e.shiftKey){
26721                     this.selectPrevious(e.shiftKey);
26722                 }else if(this.last !== false && this.lastActive !== false){
26723                     var last = this.last;
26724                     this.selectRange(this.last,  this.lastActive-1);
26725                     this.grid.getView().focusRow(this.lastActive);
26726                     if(last !== false){
26727                         this.last = last;
26728                     }
26729                 }else{
26730                     this.selectFirstRow();
26731                 }
26732                 this.fireEvent("afterselectionchange", this);
26733             },
26734             "down" : function(e){
26735                 if(!e.shiftKey){
26736                     this.selectNext(e.shiftKey);
26737                 }else if(this.last !== false && this.lastActive !== false){
26738                     var last = this.last;
26739                     this.selectRange(this.last,  this.lastActive+1);
26740                     this.grid.getView().focusRow(this.lastActive);
26741                     if(last !== false){
26742                         this.last = last;
26743                     }
26744                 }else{
26745                     this.selectFirstRow();
26746                 }
26747                 this.fireEvent("afterselectionchange", this);
26748             },
26749             scope: this
26750         });
26751         this.grid.store.on('load', function(){
26752             this.selections.clear();
26753         },this);
26754         /*
26755         var view = this.grid.view;
26756         view.on("refresh", this.onRefresh, this);
26757         view.on("rowupdated", this.onRowUpdated, this);
26758         view.on("rowremoved", this.onRemove, this);
26759         */
26760     },
26761
26762     // private
26763     onRefresh : function()
26764     {
26765         var ds = this.grid.store, i, v = this.grid.view;
26766         var s = this.selections;
26767         s.each(function(r){
26768             if((i = ds.indexOfId(r.id)) != -1){
26769                 v.onRowSelect(i);
26770             }else{
26771                 s.remove(r);
26772             }
26773         });
26774     },
26775
26776     // private
26777     onRemove : function(v, index, r){
26778         this.selections.remove(r);
26779     },
26780
26781     // private
26782     onRowUpdated : function(v, index, r){
26783         if(this.isSelected(r)){
26784             v.onRowSelect(index);
26785         }
26786     },
26787
26788     /**
26789      * Select records.
26790      * @param {Array} records The records to select
26791      * @param {Boolean} keepExisting (optional) True to keep existing selections
26792      */
26793     selectRecords : function(records, keepExisting)
26794     {
26795         if(!keepExisting){
26796             this.clearSelections();
26797         }
26798             var ds = this.grid.store;
26799         for(var i = 0, len = records.length; i < len; i++){
26800             this.selectRow(ds.indexOf(records[i]), true);
26801         }
26802     },
26803
26804     /**
26805      * Gets the number of selected rows.
26806      * @return {Number}
26807      */
26808     getCount : function(){
26809         return this.selections.length;
26810     },
26811
26812     /**
26813      * Selects the first row in the grid.
26814      */
26815     selectFirstRow : function(){
26816         this.selectRow(0);
26817     },
26818
26819     /**
26820      * Select the last row.
26821      * @param {Boolean} keepExisting (optional) True to keep existing selections
26822      */
26823     selectLastRow : function(keepExisting){
26824         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26825         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26826     },
26827
26828     /**
26829      * Selects the row immediately following the last selected row.
26830      * @param {Boolean} keepExisting (optional) True to keep existing selections
26831      */
26832     selectNext : function(keepExisting)
26833     {
26834             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26835             this.selectRow(this.last+1, keepExisting);
26836             this.grid.getView().focusRow(this.last);
26837         }
26838     },
26839
26840     /**
26841      * Selects the row that precedes the last selected row.
26842      * @param {Boolean} keepExisting (optional) True to keep existing selections
26843      */
26844     selectPrevious : function(keepExisting){
26845         if(this.last){
26846             this.selectRow(this.last-1, keepExisting);
26847             this.grid.getView().focusRow(this.last);
26848         }
26849     },
26850
26851     /**
26852      * Returns the selected records
26853      * @return {Array} Array of selected records
26854      */
26855     getSelections : function(){
26856         return [].concat(this.selections.items);
26857     },
26858
26859     /**
26860      * Returns the first selected record.
26861      * @return {Record}
26862      */
26863     getSelected : function(){
26864         return this.selections.itemAt(0);
26865     },
26866
26867
26868     /**
26869      * Clears all selections.
26870      */
26871     clearSelections : function(fast)
26872     {
26873         if(this.locked) {
26874             return;
26875         }
26876         if(fast !== true){
26877                 var ds = this.grid.store;
26878             var s = this.selections;
26879             s.each(function(r){
26880                 this.deselectRow(ds.indexOfId(r.id));
26881             }, this);
26882             s.clear();
26883         }else{
26884             this.selections.clear();
26885         }
26886         this.last = false;
26887     },
26888
26889
26890     /**
26891      * Selects all rows.
26892      */
26893     selectAll : function(){
26894         if(this.locked) {
26895             return;
26896         }
26897         this.selections.clear();
26898         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26899             this.selectRow(i, true);
26900         }
26901     },
26902
26903     /**
26904      * Returns True if there is a selection.
26905      * @return {Boolean}
26906      */
26907     hasSelection : function(){
26908         return this.selections.length > 0;
26909     },
26910
26911     /**
26912      * Returns True if the specified row is selected.
26913      * @param {Number/Record} record The record or index of the record to check
26914      * @return {Boolean}
26915      */
26916     isSelected : function(index){
26917             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26918         return (r && this.selections.key(r.id) ? true : false);
26919     },
26920
26921     /**
26922      * Returns True if the specified record id is selected.
26923      * @param {String} id The id of record to check
26924      * @return {Boolean}
26925      */
26926     isIdSelected : function(id){
26927         return (this.selections.key(id) ? true : false);
26928     },
26929
26930
26931     // private
26932     handleMouseDBClick : function(e, t){
26933         
26934     },
26935     // private
26936     handleMouseDown : function(e, t)
26937     {
26938             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26939         if(this.isLocked() || rowIndex < 0 ){
26940             return;
26941         };
26942         if(e.shiftKey && this.last !== false){
26943             var last = this.last;
26944             this.selectRange(last, rowIndex, e.ctrlKey);
26945             this.last = last; // reset the last
26946             t.focus();
26947     
26948         }else{
26949             var isSelected = this.isSelected(rowIndex);
26950             //Roo.log("select row:" + rowIndex);
26951             if(isSelected){
26952                 this.deselectRow(rowIndex);
26953             } else {
26954                         this.selectRow(rowIndex, true);
26955             }
26956     
26957             /*
26958                 if(e.button !== 0 && isSelected){
26959                 alert('rowIndex 2: ' + rowIndex);
26960                     view.focusRow(rowIndex);
26961                 }else if(e.ctrlKey && isSelected){
26962                     this.deselectRow(rowIndex);
26963                 }else if(!isSelected){
26964                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26965                     view.focusRow(rowIndex);
26966                 }
26967             */
26968         }
26969         this.fireEvent("afterselectionchange", this);
26970     },
26971     // private
26972     handleDragableRowClick :  function(grid, rowIndex, e) 
26973     {
26974         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26975             this.selectRow(rowIndex, false);
26976             grid.view.focusRow(rowIndex);
26977              this.fireEvent("afterselectionchange", this);
26978         }
26979     },
26980     
26981     /**
26982      * Selects multiple rows.
26983      * @param {Array} rows Array of the indexes of the row to select
26984      * @param {Boolean} keepExisting (optional) True to keep existing selections
26985      */
26986     selectRows : function(rows, keepExisting){
26987         if(!keepExisting){
26988             this.clearSelections();
26989         }
26990         for(var i = 0, len = rows.length; i < len; i++){
26991             this.selectRow(rows[i], true);
26992         }
26993     },
26994
26995     /**
26996      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26997      * @param {Number} startRow The index of the first row in the range
26998      * @param {Number} endRow The index of the last row in the range
26999      * @param {Boolean} keepExisting (optional) True to retain existing selections
27000      */
27001     selectRange : function(startRow, endRow, keepExisting){
27002         if(this.locked) {
27003             return;
27004         }
27005         if(!keepExisting){
27006             this.clearSelections();
27007         }
27008         if(startRow <= endRow){
27009             for(var i = startRow; i <= endRow; i++){
27010                 this.selectRow(i, true);
27011             }
27012         }else{
27013             for(var i = startRow; i >= endRow; i--){
27014                 this.selectRow(i, true);
27015             }
27016         }
27017     },
27018
27019     /**
27020      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27021      * @param {Number} startRow The index of the first row in the range
27022      * @param {Number} endRow The index of the last row in the range
27023      */
27024     deselectRange : function(startRow, endRow, preventViewNotify){
27025         if(this.locked) {
27026             return;
27027         }
27028         for(var i = startRow; i <= endRow; i++){
27029             this.deselectRow(i, preventViewNotify);
27030         }
27031     },
27032
27033     /**
27034      * Selects a row.
27035      * @param {Number} row The index of the row to select
27036      * @param {Boolean} keepExisting (optional) True to keep existing selections
27037      */
27038     selectRow : function(index, keepExisting, preventViewNotify)
27039     {
27040             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27041             return;
27042         }
27043         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27044             if(!keepExisting || this.singleSelect){
27045                 this.clearSelections();
27046             }
27047             
27048             var r = this.grid.store.getAt(index);
27049             //console.log('selectRow - record id :' + r.id);
27050             
27051             this.selections.add(r);
27052             this.last = this.lastActive = index;
27053             if(!preventViewNotify){
27054                 var proxy = new Roo.Element(
27055                                 this.grid.getRowDom(index)
27056                 );
27057                 proxy.addClass('bg-info info');
27058             }
27059             this.fireEvent("rowselect", this, index, r);
27060             this.fireEvent("selectionchange", this);
27061         }
27062     },
27063
27064     /**
27065      * Deselects a row.
27066      * @param {Number} row The index of the row to deselect
27067      */
27068     deselectRow : function(index, preventViewNotify)
27069     {
27070         if(this.locked) {
27071             return;
27072         }
27073         if(this.last == index){
27074             this.last = false;
27075         }
27076         if(this.lastActive == index){
27077             this.lastActive = false;
27078         }
27079         
27080         var r = this.grid.store.getAt(index);
27081         if (!r) {
27082             return;
27083         }
27084         
27085         this.selections.remove(r);
27086         //.console.log('deselectRow - record id :' + r.id);
27087         if(!preventViewNotify){
27088         
27089             var proxy = new Roo.Element(
27090                 this.grid.getRowDom(index)
27091             );
27092             proxy.removeClass('bg-info info');
27093         }
27094         this.fireEvent("rowdeselect", this, index);
27095         this.fireEvent("selectionchange", this);
27096     },
27097
27098     // private
27099     restoreLast : function(){
27100         if(this._last){
27101             this.last = this._last;
27102         }
27103     },
27104
27105     // private
27106     acceptsNav : function(row, col, cm){
27107         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27108     },
27109
27110     // private
27111     onEditorKey : function(field, e){
27112         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27113         if(k == e.TAB){
27114             e.stopEvent();
27115             ed.completeEdit();
27116             if(e.shiftKey){
27117                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27118             }else{
27119                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27120             }
27121         }else if(k == e.ENTER && !e.ctrlKey){
27122             e.stopEvent();
27123             ed.completeEdit();
27124             if(e.shiftKey){
27125                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27126             }else{
27127                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27128             }
27129         }else if(k == e.ESC){
27130             ed.cancelEdit();
27131         }
27132         if(newCell){
27133             g.startEditing(newCell[0], newCell[1]);
27134         }
27135     }
27136 });
27137 /*
27138  * Based on:
27139  * Ext JS Library 1.1.1
27140  * Copyright(c) 2006-2007, Ext JS, LLC.
27141  *
27142  * Originally Released Under LGPL - original licence link has changed is not relivant.
27143  *
27144  * Fork - LGPL
27145  * <script type="text/javascript">
27146  */
27147  
27148 /**
27149  * @class Roo.bootstrap.PagingToolbar
27150  * @extends Roo.bootstrap.NavSimplebar
27151  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27152  * @constructor
27153  * Create a new PagingToolbar
27154  * @param {Object} config The config object
27155  * @param {Roo.data.Store} store
27156  */
27157 Roo.bootstrap.PagingToolbar = function(config)
27158 {
27159     // old args format still supported... - xtype is prefered..
27160         // created from xtype...
27161     
27162     this.ds = config.dataSource;
27163     
27164     if (config.store && !this.ds) {
27165         this.store= Roo.factory(config.store, Roo.data);
27166         this.ds = this.store;
27167         this.ds.xmodule = this.xmodule || false;
27168     }
27169     
27170     this.toolbarItems = [];
27171     if (config.items) {
27172         this.toolbarItems = config.items;
27173     }
27174     
27175     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27176     
27177     this.cursor = 0;
27178     
27179     if (this.ds) { 
27180         this.bind(this.ds);
27181     }
27182     
27183     if (Roo.bootstrap.version == 4) {
27184         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27185     } else {
27186         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27187     }
27188     
27189 };
27190
27191 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27192     /**
27193      * @cfg {Roo.data.Store} dataSource
27194      * The underlying data store providing the paged data
27195      */
27196     /**
27197      * @cfg {String/HTMLElement/Element} container
27198      * container The id or element that will contain the toolbar
27199      */
27200     /**
27201      * @cfg {Boolean} displayInfo
27202      * True to display the displayMsg (defaults to false)
27203      */
27204     /**
27205      * @cfg {Number} pageSize
27206      * The number of records to display per page (defaults to 20)
27207      */
27208     pageSize: 20,
27209     /**
27210      * @cfg {String} displayMsg
27211      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27212      */
27213     displayMsg : 'Displaying {0} - {1} of {2}',
27214     /**
27215      * @cfg {String} emptyMsg
27216      * The message to display when no records are found (defaults to "No data to display")
27217      */
27218     emptyMsg : 'No data to display',
27219     /**
27220      * Customizable piece of the default paging text (defaults to "Page")
27221      * @type String
27222      */
27223     beforePageText : "Page",
27224     /**
27225      * Customizable piece of the default paging text (defaults to "of %0")
27226      * @type String
27227      */
27228     afterPageText : "of {0}",
27229     /**
27230      * Customizable piece of the default paging text (defaults to "First Page")
27231      * @type String
27232      */
27233     firstText : "First Page",
27234     /**
27235      * Customizable piece of the default paging text (defaults to "Previous Page")
27236      * @type String
27237      */
27238     prevText : "Previous Page",
27239     /**
27240      * Customizable piece of the default paging text (defaults to "Next Page")
27241      * @type String
27242      */
27243     nextText : "Next Page",
27244     /**
27245      * Customizable piece of the default paging text (defaults to "Last Page")
27246      * @type String
27247      */
27248     lastText : "Last Page",
27249     /**
27250      * Customizable piece of the default paging text (defaults to "Refresh")
27251      * @type String
27252      */
27253     refreshText : "Refresh",
27254
27255     buttons : false,
27256     // private
27257     onRender : function(ct, position) 
27258     {
27259         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27260         this.navgroup.parentId = this.id;
27261         this.navgroup.onRender(this.el, null);
27262         // add the buttons to the navgroup
27263         
27264         if(this.displayInfo){
27265             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27266             this.displayEl = this.el.select('.x-paging-info', true).first();
27267 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27268 //            this.displayEl = navel.el.select('span',true).first();
27269         }
27270         
27271         var _this = this;
27272         
27273         if(this.buttons){
27274             Roo.each(_this.buttons, function(e){ // this might need to use render????
27275                Roo.factory(e).render(_this.el);
27276             });
27277         }
27278             
27279         Roo.each(_this.toolbarItems, function(e) {
27280             _this.navgroup.addItem(e);
27281         });
27282         
27283         
27284         this.first = this.navgroup.addItem({
27285             tooltip: this.firstText,
27286             cls: "prev btn-outline-secondary",
27287             html : ' <i class="fa fa-step-backward"></i>',
27288             disabled: true,
27289             preventDefault: true,
27290             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27291         });
27292         
27293         this.prev =  this.navgroup.addItem({
27294             tooltip: this.prevText,
27295             cls: "prev btn-outline-secondary",
27296             html : ' <i class="fa fa-backward"></i>',
27297             disabled: true,
27298             preventDefault: true,
27299             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27300         });
27301     //this.addSeparator();
27302         
27303         
27304         var field = this.navgroup.addItem( {
27305             tagtype : 'span',
27306             cls : 'x-paging-position  btn-outline-secondary',
27307              disabled: true,
27308             html : this.beforePageText  +
27309                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27310                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27311          } ); //?? escaped?
27312         
27313         this.field = field.el.select('input', true).first();
27314         this.field.on("keydown", this.onPagingKeydown, this);
27315         this.field.on("focus", function(){this.dom.select();});
27316     
27317     
27318         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27319         //this.field.setHeight(18);
27320         //this.addSeparator();
27321         this.next = this.navgroup.addItem({
27322             tooltip: this.nextText,
27323             cls: "next btn-outline-secondary",
27324             html : ' <i class="fa fa-forward"></i>',
27325             disabled: true,
27326             preventDefault: true,
27327             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27328         });
27329         this.last = this.navgroup.addItem({
27330             tooltip: this.lastText,
27331             html : ' <i class="fa fa-step-forward"></i>',
27332             cls: "next btn-outline-secondary",
27333             disabled: true,
27334             preventDefault: true,
27335             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27336         });
27337     //this.addSeparator();
27338         this.loading = this.navgroup.addItem({
27339             tooltip: this.refreshText,
27340             cls: "btn-outline-secondary",
27341             html : ' <i class="fa fa-refresh"></i>',
27342             preventDefault: true,
27343             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27344         });
27345         
27346     },
27347
27348     // private
27349     updateInfo : function(){
27350         if(this.displayEl){
27351             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27352             var msg = count == 0 ?
27353                 this.emptyMsg :
27354                 String.format(
27355                     this.displayMsg,
27356                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27357                 );
27358             this.displayEl.update(msg);
27359         }
27360     },
27361
27362     // private
27363     onLoad : function(ds, r, o)
27364     {
27365         this.cursor = o.params && o.params.start ? o.params.start : 0;
27366         
27367         var d = this.getPageData(),
27368             ap = d.activePage,
27369             ps = d.pages;
27370         
27371         
27372         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27373         this.field.dom.value = ap;
27374         this.first.setDisabled(ap == 1);
27375         this.prev.setDisabled(ap == 1);
27376         this.next.setDisabled(ap == ps);
27377         this.last.setDisabled(ap == ps);
27378         this.loading.enable();
27379         this.updateInfo();
27380     },
27381
27382     // private
27383     getPageData : function(){
27384         var total = this.ds.getTotalCount();
27385         return {
27386             total : total,
27387             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27388             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27389         };
27390     },
27391
27392     // private
27393     onLoadError : function(){
27394         this.loading.enable();
27395     },
27396
27397     // private
27398     onPagingKeydown : function(e){
27399         var k = e.getKey();
27400         var d = this.getPageData();
27401         if(k == e.RETURN){
27402             var v = this.field.dom.value, pageNum;
27403             if(!v || isNaN(pageNum = parseInt(v, 10))){
27404                 this.field.dom.value = d.activePage;
27405                 return;
27406             }
27407             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27408             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27409             e.stopEvent();
27410         }
27411         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))
27412         {
27413           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27414           this.field.dom.value = pageNum;
27415           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27416           e.stopEvent();
27417         }
27418         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27419         {
27420           var v = this.field.dom.value, pageNum; 
27421           var increment = (e.shiftKey) ? 10 : 1;
27422           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27423                 increment *= -1;
27424           }
27425           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27426             this.field.dom.value = d.activePage;
27427             return;
27428           }
27429           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27430           {
27431             this.field.dom.value = parseInt(v, 10) + increment;
27432             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27433             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27434           }
27435           e.stopEvent();
27436         }
27437     },
27438
27439     // private
27440     beforeLoad : function(){
27441         if(this.loading){
27442             this.loading.disable();
27443         }
27444     },
27445
27446     // private
27447     onClick : function(which){
27448         
27449         var ds = this.ds;
27450         if (!ds) {
27451             return;
27452         }
27453         
27454         switch(which){
27455             case "first":
27456                 ds.load({params:{start: 0, limit: this.pageSize}});
27457             break;
27458             case "prev":
27459                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27460             break;
27461             case "next":
27462                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27463             break;
27464             case "last":
27465                 var total = ds.getTotalCount();
27466                 var extra = total % this.pageSize;
27467                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27468                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27469             break;
27470             case "refresh":
27471                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27472             break;
27473         }
27474     },
27475
27476     /**
27477      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27478      * @param {Roo.data.Store} store The data store to unbind
27479      */
27480     unbind : function(ds){
27481         ds.un("beforeload", this.beforeLoad, this);
27482         ds.un("load", this.onLoad, this);
27483         ds.un("loadexception", this.onLoadError, this);
27484         ds.un("remove", this.updateInfo, this);
27485         ds.un("add", this.updateInfo, this);
27486         this.ds = undefined;
27487     },
27488
27489     /**
27490      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27491      * @param {Roo.data.Store} store The data store to bind
27492      */
27493     bind : function(ds){
27494         ds.on("beforeload", this.beforeLoad, this);
27495         ds.on("load", this.onLoad, this);
27496         ds.on("loadexception", this.onLoadError, this);
27497         ds.on("remove", this.updateInfo, this);
27498         ds.on("add", this.updateInfo, this);
27499         this.ds = ds;
27500     }
27501 });/*
27502  * - LGPL
27503  *
27504  * element
27505  * 
27506  */
27507
27508 /**
27509  * @class Roo.bootstrap.MessageBar
27510  * @extends Roo.bootstrap.Component
27511  * Bootstrap MessageBar class
27512  * @cfg {String} html contents of the MessageBar
27513  * @cfg {String} weight (info | success | warning | danger) default info
27514  * @cfg {String} beforeClass insert the bar before the given class
27515  * @cfg {Boolean} closable (true | false) default false
27516  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27517  * 
27518  * @constructor
27519  * Create a new Element
27520  * @param {Object} config The config object
27521  */
27522
27523 Roo.bootstrap.MessageBar = function(config){
27524     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27525 };
27526
27527 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27528     
27529     html: '',
27530     weight: 'info',
27531     closable: false,
27532     fixed: false,
27533     beforeClass: 'bootstrap-sticky-wrap',
27534     
27535     getAutoCreate : function(){
27536         
27537         var cfg = {
27538             tag: 'div',
27539             cls: 'alert alert-dismissable alert-' + this.weight,
27540             cn: [
27541                 {
27542                     tag: 'span',
27543                     cls: 'message',
27544                     html: this.html || ''
27545                 }
27546             ]
27547         };
27548         
27549         if(this.fixed){
27550             cfg.cls += ' alert-messages-fixed';
27551         }
27552         
27553         if(this.closable){
27554             cfg.cn.push({
27555                 tag: 'button',
27556                 cls: 'close',
27557                 html: 'x'
27558             });
27559         }
27560         
27561         return cfg;
27562     },
27563     
27564     onRender : function(ct, position)
27565     {
27566         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27567         
27568         if(!this.el){
27569             var cfg = Roo.apply({},  this.getAutoCreate());
27570             cfg.id = Roo.id();
27571             
27572             if (this.cls) {
27573                 cfg.cls += ' ' + this.cls;
27574             }
27575             if (this.style) {
27576                 cfg.style = this.style;
27577             }
27578             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27579             
27580             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27581         }
27582         
27583         this.el.select('>button.close').on('click', this.hide, this);
27584         
27585     },
27586     
27587     show : function()
27588     {
27589         if (!this.rendered) {
27590             this.render();
27591         }
27592         
27593         this.el.show();
27594         
27595         this.fireEvent('show', this);
27596         
27597     },
27598     
27599     hide : function()
27600     {
27601         if (!this.rendered) {
27602             this.render();
27603         }
27604         
27605         this.el.hide();
27606         
27607         this.fireEvent('hide', this);
27608     },
27609     
27610     update : function()
27611     {
27612 //        var e = this.el.dom.firstChild;
27613 //        
27614 //        if(this.closable){
27615 //            e = e.nextSibling;
27616 //        }
27617 //        
27618 //        e.data = this.html || '';
27619
27620         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27621     }
27622    
27623 });
27624
27625  
27626
27627      /*
27628  * - LGPL
27629  *
27630  * Graph
27631  * 
27632  */
27633
27634
27635 /**
27636  * @class Roo.bootstrap.Graph
27637  * @extends Roo.bootstrap.Component
27638  * Bootstrap Graph class
27639 > Prameters
27640  -sm {number} sm 4
27641  -md {number} md 5
27642  @cfg {String} graphtype  bar | vbar | pie
27643  @cfg {number} g_x coodinator | centre x (pie)
27644  @cfg {number} g_y coodinator | centre y (pie)
27645  @cfg {number} g_r radius (pie)
27646  @cfg {number} g_height height of the chart (respected by all elements in the set)
27647  @cfg {number} g_width width of the chart (respected by all elements in the set)
27648  @cfg {Object} title The title of the chart
27649     
27650  -{Array}  values
27651  -opts (object) options for the chart 
27652      o {
27653      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27654      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27655      o vgutter (number)
27656      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.
27657      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27658      o to
27659      o stretch (boolean)
27660      o }
27661  -opts (object) options for the pie
27662      o{
27663      o cut
27664      o startAngle (number)
27665      o endAngle (number)
27666      } 
27667  *
27668  * @constructor
27669  * Create a new Input
27670  * @param {Object} config The config object
27671  */
27672
27673 Roo.bootstrap.Graph = function(config){
27674     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27675     
27676     this.addEvents({
27677         // img events
27678         /**
27679          * @event click
27680          * The img click event for the img.
27681          * @param {Roo.EventObject} e
27682          */
27683         "click" : true
27684     });
27685 };
27686
27687 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27688     
27689     sm: 4,
27690     md: 5,
27691     graphtype: 'bar',
27692     g_height: 250,
27693     g_width: 400,
27694     g_x: 50,
27695     g_y: 50,
27696     g_r: 30,
27697     opts:{
27698         //g_colors: this.colors,
27699         g_type: 'soft',
27700         g_gutter: '20%'
27701
27702     },
27703     title : false,
27704
27705     getAutoCreate : function(){
27706         
27707         var cfg = {
27708             tag: 'div',
27709             html : null
27710         };
27711         
27712         
27713         return  cfg;
27714     },
27715
27716     onRender : function(ct,position){
27717         
27718         
27719         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27720         
27721         if (typeof(Raphael) == 'undefined') {
27722             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27723             return;
27724         }
27725         
27726         this.raphael = Raphael(this.el.dom);
27727         
27728                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27729                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27730                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27731                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27732                 /*
27733                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27734                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27735                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27736                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27737                 
27738                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27739                 r.barchart(330, 10, 300, 220, data1);
27740                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27741                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27742                 */
27743                 
27744                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27745                 // r.barchart(30, 30, 560, 250,  xdata, {
27746                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27747                 //     axis : "0 0 1 1",
27748                 //     axisxlabels :  xdata
27749                 //     //yvalues : cols,
27750                    
27751                 // });
27752 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27753 //        
27754 //        this.load(null,xdata,{
27755 //                axis : "0 0 1 1",
27756 //                axisxlabels :  xdata
27757 //                });
27758
27759     },
27760
27761     load : function(graphtype,xdata,opts)
27762     {
27763         this.raphael.clear();
27764         if(!graphtype) {
27765             graphtype = this.graphtype;
27766         }
27767         if(!opts){
27768             opts = this.opts;
27769         }
27770         var r = this.raphael,
27771             fin = function () {
27772                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27773             },
27774             fout = function () {
27775                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27776             },
27777             pfin = function() {
27778                 this.sector.stop();
27779                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27780
27781                 if (this.label) {
27782                     this.label[0].stop();
27783                     this.label[0].attr({ r: 7.5 });
27784                     this.label[1].attr({ "font-weight": 800 });
27785                 }
27786             },
27787             pfout = function() {
27788                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27789
27790                 if (this.label) {
27791                     this.label[0].animate({ r: 5 }, 500, "bounce");
27792                     this.label[1].attr({ "font-weight": 400 });
27793                 }
27794             };
27795
27796         switch(graphtype){
27797             case 'bar':
27798                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27799                 break;
27800             case 'hbar':
27801                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27802                 break;
27803             case 'pie':
27804 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27805 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27806 //            
27807                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27808                 
27809                 break;
27810
27811         }
27812         
27813         if(this.title){
27814             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27815         }
27816         
27817     },
27818     
27819     setTitle: function(o)
27820     {
27821         this.title = o;
27822     },
27823     
27824     initEvents: function() {
27825         
27826         if(!this.href){
27827             this.el.on('click', this.onClick, this);
27828         }
27829     },
27830     
27831     onClick : function(e)
27832     {
27833         Roo.log('img onclick');
27834         this.fireEvent('click', this, e);
27835     }
27836    
27837 });
27838
27839  
27840 /*
27841  * - LGPL
27842  *
27843  * numberBox
27844  * 
27845  */
27846 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27847
27848 /**
27849  * @class Roo.bootstrap.dash.NumberBox
27850  * @extends Roo.bootstrap.Component
27851  * Bootstrap NumberBox class
27852  * @cfg {String} headline Box headline
27853  * @cfg {String} content Box content
27854  * @cfg {String} icon Box icon
27855  * @cfg {String} footer Footer text
27856  * @cfg {String} fhref Footer href
27857  * 
27858  * @constructor
27859  * Create a new NumberBox
27860  * @param {Object} config The config object
27861  */
27862
27863
27864 Roo.bootstrap.dash.NumberBox = function(config){
27865     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27866     
27867 };
27868
27869 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27870     
27871     headline : '',
27872     content : '',
27873     icon : '',
27874     footer : '',
27875     fhref : '',
27876     ficon : '',
27877     
27878     getAutoCreate : function(){
27879         
27880         var cfg = {
27881             tag : 'div',
27882             cls : 'small-box ',
27883             cn : [
27884                 {
27885                     tag : 'div',
27886                     cls : 'inner',
27887                     cn :[
27888                         {
27889                             tag : 'h3',
27890                             cls : 'roo-headline',
27891                             html : this.headline
27892                         },
27893                         {
27894                             tag : 'p',
27895                             cls : 'roo-content',
27896                             html : this.content
27897                         }
27898                     ]
27899                 }
27900             ]
27901         };
27902         
27903         if(this.icon){
27904             cfg.cn.push({
27905                 tag : 'div',
27906                 cls : 'icon',
27907                 cn :[
27908                     {
27909                         tag : 'i',
27910                         cls : 'ion ' + this.icon
27911                     }
27912                 ]
27913             });
27914         }
27915         
27916         if(this.footer){
27917             var footer = {
27918                 tag : 'a',
27919                 cls : 'small-box-footer',
27920                 href : this.fhref || '#',
27921                 html : this.footer
27922             };
27923             
27924             cfg.cn.push(footer);
27925             
27926         }
27927         
27928         return  cfg;
27929     },
27930
27931     onRender : function(ct,position){
27932         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27933
27934
27935        
27936                 
27937     },
27938
27939     setHeadline: function (value)
27940     {
27941         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27942     },
27943     
27944     setFooter: function (value, href)
27945     {
27946         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27947         
27948         if(href){
27949             this.el.select('a.small-box-footer',true).first().attr('href', href);
27950         }
27951         
27952     },
27953
27954     setContent: function (value)
27955     {
27956         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27957     },
27958
27959     initEvents: function() 
27960     {   
27961         
27962     }
27963     
27964 });
27965
27966  
27967 /*
27968  * - LGPL
27969  *
27970  * TabBox
27971  * 
27972  */
27973 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27974
27975 /**
27976  * @class Roo.bootstrap.dash.TabBox
27977  * @extends Roo.bootstrap.Component
27978  * Bootstrap TabBox class
27979  * @cfg {String} title Title of the TabBox
27980  * @cfg {String} icon Icon of the TabBox
27981  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27982  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27983  * 
27984  * @constructor
27985  * Create a new TabBox
27986  * @param {Object} config The config object
27987  */
27988
27989
27990 Roo.bootstrap.dash.TabBox = function(config){
27991     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27992     this.addEvents({
27993         // raw events
27994         /**
27995          * @event addpane
27996          * When a pane is added
27997          * @param {Roo.bootstrap.dash.TabPane} pane
27998          */
27999         "addpane" : true,
28000         /**
28001          * @event activatepane
28002          * When a pane is activated
28003          * @param {Roo.bootstrap.dash.TabPane} pane
28004          */
28005         "activatepane" : true
28006         
28007          
28008     });
28009     
28010     this.panes = [];
28011 };
28012
28013 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28014
28015     title : '',
28016     icon : false,
28017     showtabs : true,
28018     tabScrollable : false,
28019     
28020     getChildContainer : function()
28021     {
28022         return this.el.select('.tab-content', true).first();
28023     },
28024     
28025     getAutoCreate : function(){
28026         
28027         var header = {
28028             tag: 'li',
28029             cls: 'pull-left header',
28030             html: this.title,
28031             cn : []
28032         };
28033         
28034         if(this.icon){
28035             header.cn.push({
28036                 tag: 'i',
28037                 cls: 'fa ' + this.icon
28038             });
28039         }
28040         
28041         var h = {
28042             tag: 'ul',
28043             cls: 'nav nav-tabs pull-right',
28044             cn: [
28045                 header
28046             ]
28047         };
28048         
28049         if(this.tabScrollable){
28050             h = {
28051                 tag: 'div',
28052                 cls: 'tab-header',
28053                 cn: [
28054                     {
28055                         tag: 'ul',
28056                         cls: 'nav nav-tabs pull-right',
28057                         cn: [
28058                             header
28059                         ]
28060                     }
28061                 ]
28062             };
28063         }
28064         
28065         var cfg = {
28066             tag: 'div',
28067             cls: 'nav-tabs-custom',
28068             cn: [
28069                 h,
28070                 {
28071                     tag: 'div',
28072                     cls: 'tab-content no-padding',
28073                     cn: []
28074                 }
28075             ]
28076         };
28077
28078         return  cfg;
28079     },
28080     initEvents : function()
28081     {
28082         //Roo.log('add add pane handler');
28083         this.on('addpane', this.onAddPane, this);
28084     },
28085      /**
28086      * Updates the box title
28087      * @param {String} html to set the title to.
28088      */
28089     setTitle : function(value)
28090     {
28091         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28092     },
28093     onAddPane : function(pane)
28094     {
28095         this.panes.push(pane);
28096         //Roo.log('addpane');
28097         //Roo.log(pane);
28098         // tabs are rendere left to right..
28099         if(!this.showtabs){
28100             return;
28101         }
28102         
28103         var ctr = this.el.select('.nav-tabs', true).first();
28104          
28105          
28106         var existing = ctr.select('.nav-tab',true);
28107         var qty = existing.getCount();;
28108         
28109         
28110         var tab = ctr.createChild({
28111             tag : 'li',
28112             cls : 'nav-tab' + (qty ? '' : ' active'),
28113             cn : [
28114                 {
28115                     tag : 'a',
28116                     href:'#',
28117                     html : pane.title
28118                 }
28119             ]
28120         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28121         pane.tab = tab;
28122         
28123         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28124         if (!qty) {
28125             pane.el.addClass('active');
28126         }
28127         
28128                 
28129     },
28130     onTabClick : function(ev,un,ob,pane)
28131     {
28132         //Roo.log('tab - prev default');
28133         ev.preventDefault();
28134         
28135         
28136         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28137         pane.tab.addClass('active');
28138         //Roo.log(pane.title);
28139         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28140         // technically we should have a deactivate event.. but maybe add later.
28141         // and it should not de-activate the selected tab...
28142         this.fireEvent('activatepane', pane);
28143         pane.el.addClass('active');
28144         pane.fireEvent('activate');
28145         
28146         
28147     },
28148     
28149     getActivePane : function()
28150     {
28151         var r = false;
28152         Roo.each(this.panes, function(p) {
28153             if(p.el.hasClass('active')){
28154                 r = p;
28155                 return false;
28156             }
28157             
28158             return;
28159         });
28160         
28161         return r;
28162     }
28163     
28164     
28165 });
28166
28167  
28168 /*
28169  * - LGPL
28170  *
28171  * Tab pane
28172  * 
28173  */
28174 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28175 /**
28176  * @class Roo.bootstrap.TabPane
28177  * @extends Roo.bootstrap.Component
28178  * Bootstrap TabPane class
28179  * @cfg {Boolean} active (false | true) Default false
28180  * @cfg {String} title title of panel
28181
28182  * 
28183  * @constructor
28184  * Create a new TabPane
28185  * @param {Object} config The config object
28186  */
28187
28188 Roo.bootstrap.dash.TabPane = function(config){
28189     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28190     
28191     this.addEvents({
28192         // raw events
28193         /**
28194          * @event activate
28195          * When a pane is activated
28196          * @param {Roo.bootstrap.dash.TabPane} pane
28197          */
28198         "activate" : true
28199          
28200     });
28201 };
28202
28203 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28204     
28205     active : false,
28206     title : '',
28207     
28208     // the tabBox that this is attached to.
28209     tab : false,
28210      
28211     getAutoCreate : function() 
28212     {
28213         var cfg = {
28214             tag: 'div',
28215             cls: 'tab-pane'
28216         };
28217         
28218         if(this.active){
28219             cfg.cls += ' active';
28220         }
28221         
28222         return cfg;
28223     },
28224     initEvents  : function()
28225     {
28226         //Roo.log('trigger add pane handler');
28227         this.parent().fireEvent('addpane', this)
28228     },
28229     
28230      /**
28231      * Updates the tab title 
28232      * @param {String} html to set the title to.
28233      */
28234     setTitle: function(str)
28235     {
28236         if (!this.tab) {
28237             return;
28238         }
28239         this.title = str;
28240         this.tab.select('a', true).first().dom.innerHTML = str;
28241         
28242     }
28243     
28244     
28245     
28246 });
28247
28248  
28249
28250
28251  /*
28252  * - LGPL
28253  *
28254  * menu
28255  * 
28256  */
28257 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28258
28259 /**
28260  * @class Roo.bootstrap.menu.Menu
28261  * @extends Roo.bootstrap.Component
28262  * Bootstrap Menu class - container for Menu
28263  * @cfg {String} html Text of the menu
28264  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28265  * @cfg {String} icon Font awesome icon
28266  * @cfg {String} pos Menu align to (top | bottom) default bottom
28267  * 
28268  * 
28269  * @constructor
28270  * Create a new Menu
28271  * @param {Object} config The config object
28272  */
28273
28274
28275 Roo.bootstrap.menu.Menu = function(config){
28276     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28277     
28278     this.addEvents({
28279         /**
28280          * @event beforeshow
28281          * Fires before this menu is displayed
28282          * @param {Roo.bootstrap.menu.Menu} this
28283          */
28284         beforeshow : true,
28285         /**
28286          * @event beforehide
28287          * Fires before this menu is hidden
28288          * @param {Roo.bootstrap.menu.Menu} this
28289          */
28290         beforehide : true,
28291         /**
28292          * @event show
28293          * Fires after this menu is displayed
28294          * @param {Roo.bootstrap.menu.Menu} this
28295          */
28296         show : true,
28297         /**
28298          * @event hide
28299          * Fires after this menu is hidden
28300          * @param {Roo.bootstrap.menu.Menu} this
28301          */
28302         hide : true,
28303         /**
28304          * @event click
28305          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28306          * @param {Roo.bootstrap.menu.Menu} this
28307          * @param {Roo.EventObject} e
28308          */
28309         click : true
28310     });
28311     
28312 };
28313
28314 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28315     
28316     submenu : false,
28317     html : '',
28318     weight : 'default',
28319     icon : false,
28320     pos : 'bottom',
28321     
28322     
28323     getChildContainer : function() {
28324         if(this.isSubMenu){
28325             return this.el;
28326         }
28327         
28328         return this.el.select('ul.dropdown-menu', true).first();  
28329     },
28330     
28331     getAutoCreate : function()
28332     {
28333         var text = [
28334             {
28335                 tag : 'span',
28336                 cls : 'roo-menu-text',
28337                 html : this.html
28338             }
28339         ];
28340         
28341         if(this.icon){
28342             text.unshift({
28343                 tag : 'i',
28344                 cls : 'fa ' + this.icon
28345             })
28346         }
28347         
28348         
28349         var cfg = {
28350             tag : 'div',
28351             cls : 'btn-group',
28352             cn : [
28353                 {
28354                     tag : 'button',
28355                     cls : 'dropdown-button btn btn-' + this.weight,
28356                     cn : text
28357                 },
28358                 {
28359                     tag : 'button',
28360                     cls : 'dropdown-toggle btn btn-' + this.weight,
28361                     cn : [
28362                         {
28363                             tag : 'span',
28364                             cls : 'caret'
28365                         }
28366                     ]
28367                 },
28368                 {
28369                     tag : 'ul',
28370                     cls : 'dropdown-menu'
28371                 }
28372             ]
28373             
28374         };
28375         
28376         if(this.pos == 'top'){
28377             cfg.cls += ' dropup';
28378         }
28379         
28380         if(this.isSubMenu){
28381             cfg = {
28382                 tag : 'ul',
28383                 cls : 'dropdown-menu'
28384             }
28385         }
28386         
28387         return cfg;
28388     },
28389     
28390     onRender : function(ct, position)
28391     {
28392         this.isSubMenu = ct.hasClass('dropdown-submenu');
28393         
28394         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28395     },
28396     
28397     initEvents : function() 
28398     {
28399         if(this.isSubMenu){
28400             return;
28401         }
28402         
28403         this.hidden = true;
28404         
28405         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28406         this.triggerEl.on('click', this.onTriggerPress, this);
28407         
28408         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28409         this.buttonEl.on('click', this.onClick, this);
28410         
28411     },
28412     
28413     list : function()
28414     {
28415         if(this.isSubMenu){
28416             return this.el;
28417         }
28418         
28419         return this.el.select('ul.dropdown-menu', true).first();
28420     },
28421     
28422     onClick : function(e)
28423     {
28424         this.fireEvent("click", this, e);
28425     },
28426     
28427     onTriggerPress  : function(e)
28428     {   
28429         if (this.isVisible()) {
28430             this.hide();
28431         } else {
28432             this.show();
28433         }
28434     },
28435     
28436     isVisible : function(){
28437         return !this.hidden;
28438     },
28439     
28440     show : function()
28441     {
28442         this.fireEvent("beforeshow", this);
28443         
28444         this.hidden = false;
28445         this.el.addClass('open');
28446         
28447         Roo.get(document).on("mouseup", this.onMouseUp, this);
28448         
28449         this.fireEvent("show", this);
28450         
28451         
28452     },
28453     
28454     hide : function()
28455     {
28456         this.fireEvent("beforehide", this);
28457         
28458         this.hidden = true;
28459         this.el.removeClass('open');
28460         
28461         Roo.get(document).un("mouseup", this.onMouseUp);
28462         
28463         this.fireEvent("hide", this);
28464     },
28465     
28466     onMouseUp : function()
28467     {
28468         this.hide();
28469     }
28470     
28471 });
28472
28473  
28474  /*
28475  * - LGPL
28476  *
28477  * menu item
28478  * 
28479  */
28480 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28481
28482 /**
28483  * @class Roo.bootstrap.menu.Item
28484  * @extends Roo.bootstrap.Component
28485  * Bootstrap MenuItem class
28486  * @cfg {Boolean} submenu (true | false) default false
28487  * @cfg {String} html text of the item
28488  * @cfg {String} href the link
28489  * @cfg {Boolean} disable (true | false) default false
28490  * @cfg {Boolean} preventDefault (true | false) default true
28491  * @cfg {String} icon Font awesome icon
28492  * @cfg {String} pos Submenu align to (left | right) default right 
28493  * 
28494  * 
28495  * @constructor
28496  * Create a new Item
28497  * @param {Object} config The config object
28498  */
28499
28500
28501 Roo.bootstrap.menu.Item = function(config){
28502     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28503     this.addEvents({
28504         /**
28505          * @event mouseover
28506          * Fires when the mouse is hovering over this menu
28507          * @param {Roo.bootstrap.menu.Item} this
28508          * @param {Roo.EventObject} e
28509          */
28510         mouseover : true,
28511         /**
28512          * @event mouseout
28513          * Fires when the mouse exits this menu
28514          * @param {Roo.bootstrap.menu.Item} this
28515          * @param {Roo.EventObject} e
28516          */
28517         mouseout : true,
28518         // raw events
28519         /**
28520          * @event click
28521          * The raw click event for the entire grid.
28522          * @param {Roo.EventObject} e
28523          */
28524         click : true
28525     });
28526 };
28527
28528 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28529     
28530     submenu : false,
28531     href : '',
28532     html : '',
28533     preventDefault: true,
28534     disable : false,
28535     icon : false,
28536     pos : 'right',
28537     
28538     getAutoCreate : function()
28539     {
28540         var text = [
28541             {
28542                 tag : 'span',
28543                 cls : 'roo-menu-item-text',
28544                 html : this.html
28545             }
28546         ];
28547         
28548         if(this.icon){
28549             text.unshift({
28550                 tag : 'i',
28551                 cls : 'fa ' + this.icon
28552             })
28553         }
28554         
28555         var cfg = {
28556             tag : 'li',
28557             cn : [
28558                 {
28559                     tag : 'a',
28560                     href : this.href || '#',
28561                     cn : text
28562                 }
28563             ]
28564         };
28565         
28566         if(this.disable){
28567             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28568         }
28569         
28570         if(this.submenu){
28571             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28572             
28573             if(this.pos == 'left'){
28574                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28575             }
28576         }
28577         
28578         return cfg;
28579     },
28580     
28581     initEvents : function() 
28582     {
28583         this.el.on('mouseover', this.onMouseOver, this);
28584         this.el.on('mouseout', this.onMouseOut, this);
28585         
28586         this.el.select('a', true).first().on('click', this.onClick, this);
28587         
28588     },
28589     
28590     onClick : function(e)
28591     {
28592         if(this.preventDefault){
28593             e.preventDefault();
28594         }
28595         
28596         this.fireEvent("click", this, e);
28597     },
28598     
28599     onMouseOver : function(e)
28600     {
28601         if(this.submenu && this.pos == 'left'){
28602             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28603         }
28604         
28605         this.fireEvent("mouseover", this, e);
28606     },
28607     
28608     onMouseOut : function(e)
28609     {
28610         this.fireEvent("mouseout", this, e);
28611     }
28612 });
28613
28614  
28615
28616  /*
28617  * - LGPL
28618  *
28619  * menu separator
28620  * 
28621  */
28622 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28623
28624 /**
28625  * @class Roo.bootstrap.menu.Separator
28626  * @extends Roo.bootstrap.Component
28627  * Bootstrap Separator class
28628  * 
28629  * @constructor
28630  * Create a new Separator
28631  * @param {Object} config The config object
28632  */
28633
28634
28635 Roo.bootstrap.menu.Separator = function(config){
28636     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28637 };
28638
28639 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28640     
28641     getAutoCreate : function(){
28642         var cfg = {
28643             tag : 'li',
28644             cls: 'divider'
28645         };
28646         
28647         return cfg;
28648     }
28649    
28650 });
28651
28652  
28653
28654  /*
28655  * - LGPL
28656  *
28657  * Tooltip
28658  * 
28659  */
28660
28661 /**
28662  * @class Roo.bootstrap.Tooltip
28663  * Bootstrap Tooltip class
28664  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28665  * to determine which dom element triggers the tooltip.
28666  * 
28667  * It needs to add support for additional attributes like tooltip-position
28668  * 
28669  * @constructor
28670  * Create a new Toolti
28671  * @param {Object} config The config object
28672  */
28673
28674 Roo.bootstrap.Tooltip = function(config){
28675     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28676     
28677     this.alignment = Roo.bootstrap.Tooltip.alignment;
28678     
28679     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28680         this.alignment = config.alignment;
28681     }
28682     
28683 };
28684
28685 Roo.apply(Roo.bootstrap.Tooltip, {
28686     /**
28687      * @function init initialize tooltip monitoring.
28688      * @static
28689      */
28690     currentEl : false,
28691     currentTip : false,
28692     currentRegion : false,
28693     
28694     //  init : delay?
28695     
28696     init : function()
28697     {
28698         Roo.get(document).on('mouseover', this.enter ,this);
28699         Roo.get(document).on('mouseout', this.leave, this);
28700          
28701         
28702         this.currentTip = new Roo.bootstrap.Tooltip();
28703     },
28704     
28705     enter : function(ev)
28706     {
28707         var dom = ev.getTarget();
28708         
28709         //Roo.log(['enter',dom]);
28710         var el = Roo.fly(dom);
28711         if (this.currentEl) {
28712             //Roo.log(dom);
28713             //Roo.log(this.currentEl);
28714             //Roo.log(this.currentEl.contains(dom));
28715             if (this.currentEl == el) {
28716                 return;
28717             }
28718             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28719                 return;
28720             }
28721
28722         }
28723         
28724         if (this.currentTip.el) {
28725             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28726         }    
28727         //Roo.log(ev);
28728         
28729         if(!el || el.dom == document){
28730             return;
28731         }
28732         
28733         var bindEl = el;
28734         
28735         // you can not look for children, as if el is the body.. then everythign is the child..
28736         if (!el.attr('tooltip')) { //
28737             if (!el.select("[tooltip]").elements.length) {
28738                 return;
28739             }
28740             // is the mouse over this child...?
28741             bindEl = el.select("[tooltip]").first();
28742             var xy = ev.getXY();
28743             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28744                 //Roo.log("not in region.");
28745                 return;
28746             }
28747             //Roo.log("child element over..");
28748             
28749         }
28750         this.currentEl = bindEl;
28751         this.currentTip.bind(bindEl);
28752         this.currentRegion = Roo.lib.Region.getRegion(dom);
28753         this.currentTip.enter();
28754         
28755     },
28756     leave : function(ev)
28757     {
28758         var dom = ev.getTarget();
28759         //Roo.log(['leave',dom]);
28760         if (!this.currentEl) {
28761             return;
28762         }
28763         
28764         
28765         if (dom != this.currentEl.dom) {
28766             return;
28767         }
28768         var xy = ev.getXY();
28769         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28770             return;
28771         }
28772         // only activate leave if mouse cursor is outside... bounding box..
28773         
28774         
28775         
28776         
28777         if (this.currentTip) {
28778             this.currentTip.leave();
28779         }
28780         //Roo.log('clear currentEl');
28781         this.currentEl = false;
28782         
28783         
28784     },
28785     alignment : {
28786         'left' : ['r-l', [-2,0], 'right'],
28787         'right' : ['l-r', [2,0], 'left'],
28788         'bottom' : ['t-b', [0,2], 'top'],
28789         'top' : [ 'b-t', [0,-2], 'bottom']
28790     }
28791     
28792 });
28793
28794
28795 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28796     
28797     
28798     bindEl : false,
28799     
28800     delay : null, // can be { show : 300 , hide: 500}
28801     
28802     timeout : null,
28803     
28804     hoverState : null, //???
28805     
28806     placement : 'bottom', 
28807     
28808     alignment : false,
28809     
28810     getAutoCreate : function(){
28811     
28812         var cfg = {
28813            cls : 'tooltip',   
28814            role : 'tooltip',
28815            cn : [
28816                 {
28817                     cls : 'tooltip-arrow arrow'
28818                 },
28819                 {
28820                     cls : 'tooltip-inner'
28821                 }
28822            ]
28823         };
28824         
28825         return cfg;
28826     },
28827     bind : function(el)
28828     {
28829         this.bindEl = el;
28830     },
28831     
28832     initEvents : function()
28833     {
28834         this.arrowEl = this.el.select('.arrow', true).first();
28835         this.innerEl = this.el.select('.tooltip-inner', true).first();
28836     },
28837     
28838     enter : function () {
28839        
28840         if (this.timeout != null) {
28841             clearTimeout(this.timeout);
28842         }
28843         
28844         this.hoverState = 'in';
28845          //Roo.log("enter - show");
28846         if (!this.delay || !this.delay.show) {
28847             this.show();
28848             return;
28849         }
28850         var _t = this;
28851         this.timeout = setTimeout(function () {
28852             if (_t.hoverState == 'in') {
28853                 _t.show();
28854             }
28855         }, this.delay.show);
28856     },
28857     leave : function()
28858     {
28859         clearTimeout(this.timeout);
28860     
28861         this.hoverState = 'out';
28862          if (!this.delay || !this.delay.hide) {
28863             this.hide();
28864             return;
28865         }
28866        
28867         var _t = this;
28868         this.timeout = setTimeout(function () {
28869             //Roo.log("leave - timeout");
28870             
28871             if (_t.hoverState == 'out') {
28872                 _t.hide();
28873                 Roo.bootstrap.Tooltip.currentEl = false;
28874             }
28875         }, delay);
28876     },
28877     
28878     show : function (msg)
28879     {
28880         if (!this.el) {
28881             this.render(document.body);
28882         }
28883         // set content.
28884         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28885         
28886         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28887         
28888         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28889         
28890         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28891                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28892         
28893         var placement = typeof this.placement == 'function' ?
28894             this.placement.call(this, this.el, on_el) :
28895             this.placement;
28896             
28897         var autoToken = /\s?auto?\s?/i;
28898         var autoPlace = autoToken.test(placement);
28899         if (autoPlace) {
28900             placement = placement.replace(autoToken, '') || 'top';
28901         }
28902         
28903         //this.el.detach()
28904         //this.el.setXY([0,0]);
28905         this.el.show();
28906         //this.el.dom.style.display='block';
28907         
28908         //this.el.appendTo(on_el);
28909         
28910         var p = this.getPosition();
28911         var box = this.el.getBox();
28912         
28913         if (autoPlace) {
28914             // fixme..
28915         }
28916         
28917         var align = this.alignment[placement];
28918         
28919         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28920         
28921         if(placement == 'top' || placement == 'bottom'){
28922             if(xy[0] < 0){
28923                 placement = 'right';
28924             }
28925             
28926             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28927                 placement = 'left';
28928             }
28929             
28930             var scroll = Roo.select('body', true).first().getScroll();
28931             
28932             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28933                 placement = 'top';
28934             }
28935             
28936             align = this.alignment[placement];
28937             
28938             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28939             
28940         }
28941         
28942         this.el.alignTo(this.bindEl, align[0],align[1]);
28943         //var arrow = this.el.select('.arrow',true).first();
28944         //arrow.set(align[2], 
28945         
28946         this.el.addClass(placement);
28947         this.el.addClass("bs-tooltip-"+ placement);
28948         
28949         this.el.addClass('in fade show');
28950         
28951         this.hoverState = null;
28952         
28953         if (this.el.hasClass('fade')) {
28954             // fade it?
28955         }
28956         
28957         
28958         
28959         
28960         
28961     },
28962     hide : function()
28963     {
28964          
28965         if (!this.el) {
28966             return;
28967         }
28968         //this.el.setXY([0,0]);
28969         this.el.removeClass(['show', 'in']);
28970         //this.el.hide();
28971         
28972     }
28973     
28974 });
28975  
28976
28977  /*
28978  * - LGPL
28979  *
28980  * Location Picker
28981  * 
28982  */
28983
28984 /**
28985  * @class Roo.bootstrap.LocationPicker
28986  * @extends Roo.bootstrap.Component
28987  * Bootstrap LocationPicker class
28988  * @cfg {Number} latitude Position when init default 0
28989  * @cfg {Number} longitude Position when init default 0
28990  * @cfg {Number} zoom default 15
28991  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28992  * @cfg {Boolean} mapTypeControl default false
28993  * @cfg {Boolean} disableDoubleClickZoom default false
28994  * @cfg {Boolean} scrollwheel default true
28995  * @cfg {Boolean} streetViewControl default false
28996  * @cfg {Number} radius default 0
28997  * @cfg {String} locationName
28998  * @cfg {Boolean} draggable default true
28999  * @cfg {Boolean} enableAutocomplete default false
29000  * @cfg {Boolean} enableReverseGeocode default true
29001  * @cfg {String} markerTitle
29002  * 
29003  * @constructor
29004  * Create a new LocationPicker
29005  * @param {Object} config The config object
29006  */
29007
29008
29009 Roo.bootstrap.LocationPicker = function(config){
29010     
29011     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29012     
29013     this.addEvents({
29014         /**
29015          * @event initial
29016          * Fires when the picker initialized.
29017          * @param {Roo.bootstrap.LocationPicker} this
29018          * @param {Google Location} location
29019          */
29020         initial : true,
29021         /**
29022          * @event positionchanged
29023          * Fires when the picker position changed.
29024          * @param {Roo.bootstrap.LocationPicker} this
29025          * @param {Google Location} location
29026          */
29027         positionchanged : true,
29028         /**
29029          * @event resize
29030          * Fires when the map resize.
29031          * @param {Roo.bootstrap.LocationPicker} this
29032          */
29033         resize : true,
29034         /**
29035          * @event show
29036          * Fires when the map show.
29037          * @param {Roo.bootstrap.LocationPicker} this
29038          */
29039         show : true,
29040         /**
29041          * @event hide
29042          * Fires when the map hide.
29043          * @param {Roo.bootstrap.LocationPicker} this
29044          */
29045         hide : true,
29046         /**
29047          * @event mapClick
29048          * Fires when click the map.
29049          * @param {Roo.bootstrap.LocationPicker} this
29050          * @param {Map event} e
29051          */
29052         mapClick : true,
29053         /**
29054          * @event mapRightClick
29055          * Fires when right click the map.
29056          * @param {Roo.bootstrap.LocationPicker} this
29057          * @param {Map event} e
29058          */
29059         mapRightClick : true,
29060         /**
29061          * @event markerClick
29062          * Fires when click the marker.
29063          * @param {Roo.bootstrap.LocationPicker} this
29064          * @param {Map event} e
29065          */
29066         markerClick : true,
29067         /**
29068          * @event markerRightClick
29069          * Fires when right click the marker.
29070          * @param {Roo.bootstrap.LocationPicker} this
29071          * @param {Map event} e
29072          */
29073         markerRightClick : true,
29074         /**
29075          * @event OverlayViewDraw
29076          * Fires when OverlayView Draw
29077          * @param {Roo.bootstrap.LocationPicker} this
29078          */
29079         OverlayViewDraw : true,
29080         /**
29081          * @event OverlayViewOnAdd
29082          * Fires when OverlayView Draw
29083          * @param {Roo.bootstrap.LocationPicker} this
29084          */
29085         OverlayViewOnAdd : true,
29086         /**
29087          * @event OverlayViewOnRemove
29088          * Fires when OverlayView Draw
29089          * @param {Roo.bootstrap.LocationPicker} this
29090          */
29091         OverlayViewOnRemove : true,
29092         /**
29093          * @event OverlayViewShow
29094          * Fires when OverlayView Draw
29095          * @param {Roo.bootstrap.LocationPicker} this
29096          * @param {Pixel} cpx
29097          */
29098         OverlayViewShow : true,
29099         /**
29100          * @event OverlayViewHide
29101          * Fires when OverlayView Draw
29102          * @param {Roo.bootstrap.LocationPicker} this
29103          */
29104         OverlayViewHide : true,
29105         /**
29106          * @event loadexception
29107          * Fires when load google lib failed.
29108          * @param {Roo.bootstrap.LocationPicker} this
29109          */
29110         loadexception : true
29111     });
29112         
29113 };
29114
29115 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29116     
29117     gMapContext: false,
29118     
29119     latitude: 0,
29120     longitude: 0,
29121     zoom: 15,
29122     mapTypeId: false,
29123     mapTypeControl: false,
29124     disableDoubleClickZoom: false,
29125     scrollwheel: true,
29126     streetViewControl: false,
29127     radius: 0,
29128     locationName: '',
29129     draggable: true,
29130     enableAutocomplete: false,
29131     enableReverseGeocode: true,
29132     markerTitle: '',
29133     
29134     getAutoCreate: function()
29135     {
29136
29137         var cfg = {
29138             tag: 'div',
29139             cls: 'roo-location-picker'
29140         };
29141         
29142         return cfg
29143     },
29144     
29145     initEvents: function(ct, position)
29146     {       
29147         if(!this.el.getWidth() || this.isApplied()){
29148             return;
29149         }
29150         
29151         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29152         
29153         this.initial();
29154     },
29155     
29156     initial: function()
29157     {
29158         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29159             this.fireEvent('loadexception', this);
29160             return;
29161         }
29162         
29163         if(!this.mapTypeId){
29164             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29165         }
29166         
29167         this.gMapContext = this.GMapContext();
29168         
29169         this.initOverlayView();
29170         
29171         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29172         
29173         var _this = this;
29174                 
29175         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29176             _this.setPosition(_this.gMapContext.marker.position);
29177         });
29178         
29179         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29180             _this.fireEvent('mapClick', this, event);
29181             
29182         });
29183
29184         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29185             _this.fireEvent('mapRightClick', this, event);
29186             
29187         });
29188         
29189         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29190             _this.fireEvent('markerClick', this, event);
29191             
29192         });
29193
29194         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29195             _this.fireEvent('markerRightClick', this, event);
29196             
29197         });
29198         
29199         this.setPosition(this.gMapContext.location);
29200         
29201         this.fireEvent('initial', this, this.gMapContext.location);
29202     },
29203     
29204     initOverlayView: function()
29205     {
29206         var _this = this;
29207         
29208         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29209             
29210             draw: function()
29211             {
29212                 _this.fireEvent('OverlayViewDraw', _this);
29213             },
29214             
29215             onAdd: function()
29216             {
29217                 _this.fireEvent('OverlayViewOnAdd', _this);
29218             },
29219             
29220             onRemove: function()
29221             {
29222                 _this.fireEvent('OverlayViewOnRemove', _this);
29223             },
29224             
29225             show: function(cpx)
29226             {
29227                 _this.fireEvent('OverlayViewShow', _this, cpx);
29228             },
29229             
29230             hide: function()
29231             {
29232                 _this.fireEvent('OverlayViewHide', _this);
29233             }
29234             
29235         });
29236     },
29237     
29238     fromLatLngToContainerPixel: function(event)
29239     {
29240         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29241     },
29242     
29243     isApplied: function() 
29244     {
29245         return this.getGmapContext() == false ? false : true;
29246     },
29247     
29248     getGmapContext: function() 
29249     {
29250         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29251     },
29252     
29253     GMapContext: function() 
29254     {
29255         var position = new google.maps.LatLng(this.latitude, this.longitude);
29256         
29257         var _map = new google.maps.Map(this.el.dom, {
29258             center: position,
29259             zoom: this.zoom,
29260             mapTypeId: this.mapTypeId,
29261             mapTypeControl: this.mapTypeControl,
29262             disableDoubleClickZoom: this.disableDoubleClickZoom,
29263             scrollwheel: this.scrollwheel,
29264             streetViewControl: this.streetViewControl,
29265             locationName: this.locationName,
29266             draggable: this.draggable,
29267             enableAutocomplete: this.enableAutocomplete,
29268             enableReverseGeocode: this.enableReverseGeocode
29269         });
29270         
29271         var _marker = new google.maps.Marker({
29272             position: position,
29273             map: _map,
29274             title: this.markerTitle,
29275             draggable: this.draggable
29276         });
29277         
29278         return {
29279             map: _map,
29280             marker: _marker,
29281             circle: null,
29282             location: position,
29283             radius: this.radius,
29284             locationName: this.locationName,
29285             addressComponents: {
29286                 formatted_address: null,
29287                 addressLine1: null,
29288                 addressLine2: null,
29289                 streetName: null,
29290                 streetNumber: null,
29291                 city: null,
29292                 district: null,
29293                 state: null,
29294                 stateOrProvince: null
29295             },
29296             settings: this,
29297             domContainer: this.el.dom,
29298             geodecoder: new google.maps.Geocoder()
29299         };
29300     },
29301     
29302     drawCircle: function(center, radius, options) 
29303     {
29304         if (this.gMapContext.circle != null) {
29305             this.gMapContext.circle.setMap(null);
29306         }
29307         if (radius > 0) {
29308             radius *= 1;
29309             options = Roo.apply({}, options, {
29310                 strokeColor: "#0000FF",
29311                 strokeOpacity: .35,
29312                 strokeWeight: 2,
29313                 fillColor: "#0000FF",
29314                 fillOpacity: .2
29315             });
29316             
29317             options.map = this.gMapContext.map;
29318             options.radius = radius;
29319             options.center = center;
29320             this.gMapContext.circle = new google.maps.Circle(options);
29321             return this.gMapContext.circle;
29322         }
29323         
29324         return null;
29325     },
29326     
29327     setPosition: function(location) 
29328     {
29329         this.gMapContext.location = location;
29330         this.gMapContext.marker.setPosition(location);
29331         this.gMapContext.map.panTo(location);
29332         this.drawCircle(location, this.gMapContext.radius, {});
29333         
29334         var _this = this;
29335         
29336         if (this.gMapContext.settings.enableReverseGeocode) {
29337             this.gMapContext.geodecoder.geocode({
29338                 latLng: this.gMapContext.location
29339             }, function(results, status) {
29340                 
29341                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29342                     _this.gMapContext.locationName = results[0].formatted_address;
29343                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29344                     
29345                     _this.fireEvent('positionchanged', this, location);
29346                 }
29347             });
29348             
29349             return;
29350         }
29351         
29352         this.fireEvent('positionchanged', this, location);
29353     },
29354     
29355     resize: function()
29356     {
29357         google.maps.event.trigger(this.gMapContext.map, "resize");
29358         
29359         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29360         
29361         this.fireEvent('resize', this);
29362     },
29363     
29364     setPositionByLatLng: function(latitude, longitude)
29365     {
29366         this.setPosition(new google.maps.LatLng(latitude, longitude));
29367     },
29368     
29369     getCurrentPosition: function() 
29370     {
29371         return {
29372             latitude: this.gMapContext.location.lat(),
29373             longitude: this.gMapContext.location.lng()
29374         };
29375     },
29376     
29377     getAddressName: function() 
29378     {
29379         return this.gMapContext.locationName;
29380     },
29381     
29382     getAddressComponents: function() 
29383     {
29384         return this.gMapContext.addressComponents;
29385     },
29386     
29387     address_component_from_google_geocode: function(address_components) 
29388     {
29389         var result = {};
29390         
29391         for (var i = 0; i < address_components.length; i++) {
29392             var component = address_components[i];
29393             if (component.types.indexOf("postal_code") >= 0) {
29394                 result.postalCode = component.short_name;
29395             } else if (component.types.indexOf("street_number") >= 0) {
29396                 result.streetNumber = component.short_name;
29397             } else if (component.types.indexOf("route") >= 0) {
29398                 result.streetName = component.short_name;
29399             } else if (component.types.indexOf("neighborhood") >= 0) {
29400                 result.city = component.short_name;
29401             } else if (component.types.indexOf("locality") >= 0) {
29402                 result.city = component.short_name;
29403             } else if (component.types.indexOf("sublocality") >= 0) {
29404                 result.district = component.short_name;
29405             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29406                 result.stateOrProvince = component.short_name;
29407             } else if (component.types.indexOf("country") >= 0) {
29408                 result.country = component.short_name;
29409             }
29410         }
29411         
29412         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29413         result.addressLine2 = "";
29414         return result;
29415     },
29416     
29417     setZoomLevel: function(zoom)
29418     {
29419         this.gMapContext.map.setZoom(zoom);
29420     },
29421     
29422     show: function()
29423     {
29424         if(!this.el){
29425             return;
29426         }
29427         
29428         this.el.show();
29429         
29430         this.resize();
29431         
29432         this.fireEvent('show', this);
29433     },
29434     
29435     hide: function()
29436     {
29437         if(!this.el){
29438             return;
29439         }
29440         
29441         this.el.hide();
29442         
29443         this.fireEvent('hide', this);
29444     }
29445     
29446 });
29447
29448 Roo.apply(Roo.bootstrap.LocationPicker, {
29449     
29450     OverlayView : function(map, options)
29451     {
29452         options = options || {};
29453         
29454         this.setMap(map);
29455     }
29456     
29457     
29458 });/**
29459  * @class Roo.bootstrap.Alert
29460  * @extends Roo.bootstrap.Component
29461  * Bootstrap Alert class - shows an alert area box
29462  * eg
29463  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29464   Enter a valid email address
29465 </div>
29466  * @licence LGPL
29467  * @cfg {String} title The title of alert
29468  * @cfg {String} html The content of alert
29469  * @cfg {String} weight (  success | info | warning | danger )
29470  * @cfg {String} faicon font-awesomeicon
29471  * 
29472  * @constructor
29473  * Create a new alert
29474  * @param {Object} config The config object
29475  */
29476
29477
29478 Roo.bootstrap.Alert = function(config){
29479     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29480     
29481 };
29482
29483 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29484     
29485     title: '',
29486     html: '',
29487     weight: false,
29488     faicon: false,
29489     
29490     getAutoCreate : function()
29491     {
29492         
29493         var cfg = {
29494             tag : 'div',
29495             cls : 'alert',
29496             cn : [
29497                 {
29498                     tag : 'i',
29499                     cls : 'roo-alert-icon'
29500                     
29501                 },
29502                 {
29503                     tag : 'b',
29504                     cls : 'roo-alert-title',
29505                     html : this.title
29506                 },
29507                 {
29508                     tag : 'span',
29509                     cls : 'roo-alert-text',
29510                     html : this.html
29511                 }
29512             ]
29513         };
29514         
29515         if(this.faicon){
29516             cfg.cn[0].cls += ' fa ' + this.faicon;
29517         }
29518         
29519         if(this.weight){
29520             cfg.cls += ' alert-' + this.weight;
29521         }
29522         
29523         return cfg;
29524     },
29525     
29526     initEvents: function() 
29527     {
29528         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29529     },
29530     
29531     setTitle : function(str)
29532     {
29533         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29534     },
29535     
29536     setText : function(str)
29537     {
29538         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29539     },
29540     
29541     setWeight : function(weight)
29542     {
29543         if(this.weight){
29544             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29545         }
29546         
29547         this.weight = weight;
29548         
29549         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29550     },
29551     
29552     setIcon : function(icon)
29553     {
29554         if(this.faicon){
29555             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29556         }
29557         
29558         this.faicon = icon;
29559         
29560         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29561     },
29562     
29563     hide: function() 
29564     {
29565         this.el.hide();   
29566     },
29567     
29568     show: function() 
29569     {  
29570         this.el.show();   
29571     }
29572     
29573 });
29574
29575  
29576 /*
29577 * Licence: LGPL
29578 */
29579
29580 /**
29581  * @class Roo.bootstrap.UploadCropbox
29582  * @extends Roo.bootstrap.Component
29583  * Bootstrap UploadCropbox class
29584  * @cfg {String} emptyText show when image has been loaded
29585  * @cfg {String} rotateNotify show when image too small to rotate
29586  * @cfg {Number} errorTimeout default 3000
29587  * @cfg {Number} minWidth default 300
29588  * @cfg {Number} minHeight default 300
29589  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29590  * @cfg {Boolean} isDocument (true|false) default false
29591  * @cfg {String} url action url
29592  * @cfg {String} paramName default 'imageUpload'
29593  * @cfg {String} method default POST
29594  * @cfg {Boolean} loadMask (true|false) default true
29595  * @cfg {Boolean} loadingText default 'Loading...'
29596  * 
29597  * @constructor
29598  * Create a new UploadCropbox
29599  * @param {Object} config The config object
29600  */
29601
29602 Roo.bootstrap.UploadCropbox = function(config){
29603     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29604     
29605     this.addEvents({
29606         /**
29607          * @event beforeselectfile
29608          * Fire before select file
29609          * @param {Roo.bootstrap.UploadCropbox} this
29610          */
29611         "beforeselectfile" : true,
29612         /**
29613          * @event initial
29614          * Fire after initEvent
29615          * @param {Roo.bootstrap.UploadCropbox} this
29616          */
29617         "initial" : true,
29618         /**
29619          * @event crop
29620          * Fire after initEvent
29621          * @param {Roo.bootstrap.UploadCropbox} this
29622          * @param {String} data
29623          */
29624         "crop" : true,
29625         /**
29626          * @event prepare
29627          * Fire when preparing the file data
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          * @param {Object} file
29630          */
29631         "prepare" : true,
29632         /**
29633          * @event exception
29634          * Fire when get exception
29635          * @param {Roo.bootstrap.UploadCropbox} this
29636          * @param {XMLHttpRequest} xhr
29637          */
29638         "exception" : true,
29639         /**
29640          * @event beforeloadcanvas
29641          * Fire before load the canvas
29642          * @param {Roo.bootstrap.UploadCropbox} this
29643          * @param {String} src
29644          */
29645         "beforeloadcanvas" : true,
29646         /**
29647          * @event trash
29648          * Fire when trash image
29649          * @param {Roo.bootstrap.UploadCropbox} this
29650          */
29651         "trash" : true,
29652         /**
29653          * @event download
29654          * Fire when download the image
29655          * @param {Roo.bootstrap.UploadCropbox} this
29656          */
29657         "download" : true,
29658         /**
29659          * @event footerbuttonclick
29660          * Fire when footerbuttonclick
29661          * @param {Roo.bootstrap.UploadCropbox} this
29662          * @param {String} type
29663          */
29664         "footerbuttonclick" : true,
29665         /**
29666          * @event resize
29667          * Fire when resize
29668          * @param {Roo.bootstrap.UploadCropbox} this
29669          */
29670         "resize" : true,
29671         /**
29672          * @event rotate
29673          * Fire when rotate the image
29674          * @param {Roo.bootstrap.UploadCropbox} this
29675          * @param {String} pos
29676          */
29677         "rotate" : true,
29678         /**
29679          * @event inspect
29680          * Fire when inspect the file
29681          * @param {Roo.bootstrap.UploadCropbox} this
29682          * @param {Object} file
29683          */
29684         "inspect" : true,
29685         /**
29686          * @event upload
29687          * Fire when xhr upload the file
29688          * @param {Roo.bootstrap.UploadCropbox} this
29689          * @param {Object} data
29690          */
29691         "upload" : true,
29692         /**
29693          * @event arrange
29694          * Fire when arrange the file data
29695          * @param {Roo.bootstrap.UploadCropbox} this
29696          * @param {Object} formData
29697          */
29698         "arrange" : true
29699     });
29700     
29701     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29702 };
29703
29704 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29705     
29706     emptyText : 'Click to upload image',
29707     rotateNotify : 'Image is too small to rotate',
29708     errorTimeout : 3000,
29709     scale : 0,
29710     baseScale : 1,
29711     rotate : 0,
29712     dragable : false,
29713     pinching : false,
29714     mouseX : 0,
29715     mouseY : 0,
29716     cropData : false,
29717     minWidth : 300,
29718     minHeight : 300,
29719     file : false,
29720     exif : {},
29721     baseRotate : 1,
29722     cropType : 'image/jpeg',
29723     buttons : false,
29724     canvasLoaded : false,
29725     isDocument : false,
29726     method : 'POST',
29727     paramName : 'imageUpload',
29728     loadMask : true,
29729     loadingText : 'Loading...',
29730     maskEl : false,
29731     
29732     getAutoCreate : function()
29733     {
29734         var cfg = {
29735             tag : 'div',
29736             cls : 'roo-upload-cropbox',
29737             cn : [
29738                 {
29739                     tag : 'input',
29740                     cls : 'roo-upload-cropbox-selector',
29741                     type : 'file'
29742                 },
29743                 {
29744                     tag : 'div',
29745                     cls : 'roo-upload-cropbox-body',
29746                     style : 'cursor:pointer',
29747                     cn : [
29748                         {
29749                             tag : 'div',
29750                             cls : 'roo-upload-cropbox-preview'
29751                         },
29752                         {
29753                             tag : 'div',
29754                             cls : 'roo-upload-cropbox-thumb'
29755                         },
29756                         {
29757                             tag : 'div',
29758                             cls : 'roo-upload-cropbox-empty-notify',
29759                             html : this.emptyText
29760                         },
29761                         {
29762                             tag : 'div',
29763                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29764                             html : this.rotateNotify
29765                         }
29766                     ]
29767                 },
29768                 {
29769                     tag : 'div',
29770                     cls : 'roo-upload-cropbox-footer',
29771                     cn : {
29772                         tag : 'div',
29773                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29774                         cn : []
29775                     }
29776                 }
29777             ]
29778         };
29779         
29780         return cfg;
29781     },
29782     
29783     onRender : function(ct, position)
29784     {
29785         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29786         
29787         if (this.buttons.length) {
29788             
29789             Roo.each(this.buttons, function(bb) {
29790                 
29791                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29792                 
29793                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29794                 
29795             }, this);
29796         }
29797         
29798         if(this.loadMask){
29799             this.maskEl = this.el;
29800         }
29801     },
29802     
29803     initEvents : function()
29804     {
29805         this.urlAPI = (window.createObjectURL && window) || 
29806                                 (window.URL && URL.revokeObjectURL && URL) || 
29807                                 (window.webkitURL && webkitURL);
29808                         
29809         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29810         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29811         
29812         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29813         this.selectorEl.hide();
29814         
29815         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29816         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29817         
29818         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29819         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29820         this.thumbEl.hide();
29821         
29822         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29823         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29824         
29825         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29826         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29827         this.errorEl.hide();
29828         
29829         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29830         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29831         this.footerEl.hide();
29832         
29833         this.setThumbBoxSize();
29834         
29835         this.bind();
29836         
29837         this.resize();
29838         
29839         this.fireEvent('initial', this);
29840     },
29841
29842     bind : function()
29843     {
29844         var _this = this;
29845         
29846         window.addEventListener("resize", function() { _this.resize(); } );
29847         
29848         this.bodyEl.on('click', this.beforeSelectFile, this);
29849         
29850         if(Roo.isTouch){
29851             this.bodyEl.on('touchstart', this.onTouchStart, this);
29852             this.bodyEl.on('touchmove', this.onTouchMove, this);
29853             this.bodyEl.on('touchend', this.onTouchEnd, this);
29854         }
29855         
29856         if(!Roo.isTouch){
29857             this.bodyEl.on('mousedown', this.onMouseDown, this);
29858             this.bodyEl.on('mousemove', this.onMouseMove, this);
29859             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29860             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29861             Roo.get(document).on('mouseup', this.onMouseUp, this);
29862         }
29863         
29864         this.selectorEl.on('change', this.onFileSelected, this);
29865     },
29866     
29867     reset : function()
29868     {    
29869         this.scale = 0;
29870         this.baseScale = 1;
29871         this.rotate = 0;
29872         this.baseRotate = 1;
29873         this.dragable = false;
29874         this.pinching = false;
29875         this.mouseX = 0;
29876         this.mouseY = 0;
29877         this.cropData = false;
29878         this.notifyEl.dom.innerHTML = this.emptyText;
29879         
29880         this.selectorEl.dom.value = '';
29881         
29882     },
29883     
29884     resize : function()
29885     {
29886         if(this.fireEvent('resize', this) != false){
29887             this.setThumbBoxPosition();
29888             this.setCanvasPosition();
29889         }
29890     },
29891     
29892     onFooterButtonClick : function(e, el, o, type)
29893     {
29894         switch (type) {
29895             case 'rotate-left' :
29896                 this.onRotateLeft(e);
29897                 break;
29898             case 'rotate-right' :
29899                 this.onRotateRight(e);
29900                 break;
29901             case 'picture' :
29902                 this.beforeSelectFile(e);
29903                 break;
29904             case 'trash' :
29905                 this.trash(e);
29906                 break;
29907             case 'crop' :
29908                 this.crop(e);
29909                 break;
29910             case 'download' :
29911                 this.download(e);
29912                 break;
29913             default :
29914                 break;
29915         }
29916         
29917         this.fireEvent('footerbuttonclick', this, type);
29918     },
29919     
29920     beforeSelectFile : function(e)
29921     {
29922         e.preventDefault();
29923         
29924         if(this.fireEvent('beforeselectfile', this) != false){
29925             this.selectorEl.dom.click();
29926         }
29927     },
29928     
29929     onFileSelected : function(e)
29930     {
29931         e.preventDefault();
29932         
29933         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29934             return;
29935         }
29936         
29937         var file = this.selectorEl.dom.files[0];
29938         
29939         if(this.fireEvent('inspect', this, file) != false){
29940             this.prepare(file);
29941         }
29942         
29943     },
29944     
29945     trash : function(e)
29946     {
29947         this.fireEvent('trash', this);
29948     },
29949     
29950     download : function(e)
29951     {
29952         this.fireEvent('download', this);
29953     },
29954     
29955     loadCanvas : function(src)
29956     {   
29957         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29958             
29959             this.reset();
29960             
29961             this.imageEl = document.createElement('img');
29962             
29963             var _this = this;
29964             
29965             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29966             
29967             this.imageEl.src = src;
29968         }
29969     },
29970     
29971     onLoadCanvas : function()
29972     {   
29973         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29974         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29975         
29976         this.bodyEl.un('click', this.beforeSelectFile, this);
29977         
29978         this.notifyEl.hide();
29979         this.thumbEl.show();
29980         this.footerEl.show();
29981         
29982         this.baseRotateLevel();
29983         
29984         if(this.isDocument){
29985             this.setThumbBoxSize();
29986         }
29987         
29988         this.setThumbBoxPosition();
29989         
29990         this.baseScaleLevel();
29991         
29992         this.draw();
29993         
29994         this.resize();
29995         
29996         this.canvasLoaded = true;
29997         
29998         if(this.loadMask){
29999             this.maskEl.unmask();
30000         }
30001         
30002     },
30003     
30004     setCanvasPosition : function()
30005     {   
30006         if(!this.canvasEl){
30007             return;
30008         }
30009         
30010         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30011         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30012         
30013         this.previewEl.setLeft(pw);
30014         this.previewEl.setTop(ph);
30015         
30016     },
30017     
30018     onMouseDown : function(e)
30019     {   
30020         e.stopEvent();
30021         
30022         this.dragable = true;
30023         this.pinching = false;
30024         
30025         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30026             this.dragable = false;
30027             return;
30028         }
30029         
30030         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30031         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30032         
30033     },
30034     
30035     onMouseMove : function(e)
30036     {   
30037         e.stopEvent();
30038         
30039         if(!this.canvasLoaded){
30040             return;
30041         }
30042         
30043         if (!this.dragable){
30044             return;
30045         }
30046         
30047         var minX = Math.ceil(this.thumbEl.getLeft(true));
30048         var minY = Math.ceil(this.thumbEl.getTop(true));
30049         
30050         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30051         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30052         
30053         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30054         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30055         
30056         x = x - this.mouseX;
30057         y = y - this.mouseY;
30058         
30059         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30060         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30061         
30062         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30063         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30064         
30065         this.previewEl.setLeft(bgX);
30066         this.previewEl.setTop(bgY);
30067         
30068         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30069         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30070     },
30071     
30072     onMouseUp : function(e)
30073     {   
30074         e.stopEvent();
30075         
30076         this.dragable = false;
30077     },
30078     
30079     onMouseWheel : function(e)
30080     {   
30081         e.stopEvent();
30082         
30083         this.startScale = this.scale;
30084         
30085         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30086         
30087         if(!this.zoomable()){
30088             this.scale = this.startScale;
30089             return;
30090         }
30091         
30092         this.draw();
30093         
30094         return;
30095     },
30096     
30097     zoomable : function()
30098     {
30099         var minScale = this.thumbEl.getWidth() / this.minWidth;
30100         
30101         if(this.minWidth < this.minHeight){
30102             minScale = this.thumbEl.getHeight() / this.minHeight;
30103         }
30104         
30105         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30106         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30107         
30108         if(
30109                 this.isDocument &&
30110                 (this.rotate == 0 || this.rotate == 180) && 
30111                 (
30112                     width > this.imageEl.OriginWidth || 
30113                     height > this.imageEl.OriginHeight ||
30114                     (width < this.minWidth && height < this.minHeight)
30115                 )
30116         ){
30117             return false;
30118         }
30119         
30120         if(
30121                 this.isDocument &&
30122                 (this.rotate == 90 || this.rotate == 270) && 
30123                 (
30124                     width > this.imageEl.OriginWidth || 
30125                     height > this.imageEl.OriginHeight ||
30126                     (width < this.minHeight && height < this.minWidth)
30127                 )
30128         ){
30129             return false;
30130         }
30131         
30132         if(
30133                 !this.isDocument &&
30134                 (this.rotate == 0 || this.rotate == 180) && 
30135                 (
30136                     width < this.minWidth || 
30137                     width > this.imageEl.OriginWidth || 
30138                     height < this.minHeight || 
30139                     height > this.imageEl.OriginHeight
30140                 )
30141         ){
30142             return false;
30143         }
30144         
30145         if(
30146                 !this.isDocument &&
30147                 (this.rotate == 90 || this.rotate == 270) && 
30148                 (
30149                     width < this.minHeight || 
30150                     width > this.imageEl.OriginWidth || 
30151                     height < this.minWidth || 
30152                     height > this.imageEl.OriginHeight
30153                 )
30154         ){
30155             return false;
30156         }
30157         
30158         return true;
30159         
30160     },
30161     
30162     onRotateLeft : function(e)
30163     {   
30164         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30165             
30166             var minScale = this.thumbEl.getWidth() / this.minWidth;
30167             
30168             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30169             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30170             
30171             this.startScale = this.scale;
30172             
30173             while (this.getScaleLevel() < minScale){
30174             
30175                 this.scale = this.scale + 1;
30176                 
30177                 if(!this.zoomable()){
30178                     break;
30179                 }
30180                 
30181                 if(
30182                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30183                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30184                 ){
30185                     continue;
30186                 }
30187                 
30188                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30189
30190                 this.draw();
30191                 
30192                 return;
30193             }
30194             
30195             this.scale = this.startScale;
30196             
30197             this.onRotateFail();
30198             
30199             return false;
30200         }
30201         
30202         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30203
30204         if(this.isDocument){
30205             this.setThumbBoxSize();
30206             this.setThumbBoxPosition();
30207             this.setCanvasPosition();
30208         }
30209         
30210         this.draw();
30211         
30212         this.fireEvent('rotate', this, 'left');
30213         
30214     },
30215     
30216     onRotateRight : function(e)
30217     {
30218         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30219             
30220             var minScale = this.thumbEl.getWidth() / this.minWidth;
30221         
30222             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30223             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30224             
30225             this.startScale = this.scale;
30226             
30227             while (this.getScaleLevel() < minScale){
30228             
30229                 this.scale = this.scale + 1;
30230                 
30231                 if(!this.zoomable()){
30232                     break;
30233                 }
30234                 
30235                 if(
30236                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30237                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30238                 ){
30239                     continue;
30240                 }
30241                 
30242                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30243
30244                 this.draw();
30245                 
30246                 return;
30247             }
30248             
30249             this.scale = this.startScale;
30250             
30251             this.onRotateFail();
30252             
30253             return false;
30254         }
30255         
30256         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30257
30258         if(this.isDocument){
30259             this.setThumbBoxSize();
30260             this.setThumbBoxPosition();
30261             this.setCanvasPosition();
30262         }
30263         
30264         this.draw();
30265         
30266         this.fireEvent('rotate', this, 'right');
30267     },
30268     
30269     onRotateFail : function()
30270     {
30271         this.errorEl.show(true);
30272         
30273         var _this = this;
30274         
30275         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30276     },
30277     
30278     draw : function()
30279     {
30280         this.previewEl.dom.innerHTML = '';
30281         
30282         var canvasEl = document.createElement("canvas");
30283         
30284         var contextEl = canvasEl.getContext("2d");
30285         
30286         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30287         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30288         var center = this.imageEl.OriginWidth / 2;
30289         
30290         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30291             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30292             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30293             center = this.imageEl.OriginHeight / 2;
30294         }
30295         
30296         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30297         
30298         contextEl.translate(center, center);
30299         contextEl.rotate(this.rotate * Math.PI / 180);
30300
30301         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30302         
30303         this.canvasEl = document.createElement("canvas");
30304         
30305         this.contextEl = this.canvasEl.getContext("2d");
30306         
30307         switch (this.rotate) {
30308             case 0 :
30309                 
30310                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30311                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30312                 
30313                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30314                 
30315                 break;
30316             case 90 : 
30317                 
30318                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30319                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30320                 
30321                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30322                     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);
30323                     break;
30324                 }
30325                 
30326                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30327                 
30328                 break;
30329             case 180 :
30330                 
30331                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30332                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30333                 
30334                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30335                     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);
30336                     break;
30337                 }
30338                 
30339                 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);
30340                 
30341                 break;
30342             case 270 :
30343                 
30344                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30345                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30346         
30347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30348                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30349                     break;
30350                 }
30351                 
30352                 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);
30353                 
30354                 break;
30355             default : 
30356                 break;
30357         }
30358         
30359         this.previewEl.appendChild(this.canvasEl);
30360         
30361         this.setCanvasPosition();
30362     },
30363     
30364     crop : function()
30365     {
30366         if(!this.canvasLoaded){
30367             return;
30368         }
30369         
30370         var imageCanvas = document.createElement("canvas");
30371         
30372         var imageContext = imageCanvas.getContext("2d");
30373         
30374         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30375         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30376         
30377         var center = imageCanvas.width / 2;
30378         
30379         imageContext.translate(center, center);
30380         
30381         imageContext.rotate(this.rotate * Math.PI / 180);
30382         
30383         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30384         
30385         var canvas = document.createElement("canvas");
30386         
30387         var context = canvas.getContext("2d");
30388                 
30389         canvas.width = this.minWidth;
30390         canvas.height = this.minHeight;
30391
30392         switch (this.rotate) {
30393             case 0 :
30394                 
30395                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30396                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30397                 
30398                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30399                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30400                 
30401                 var targetWidth = this.minWidth - 2 * x;
30402                 var targetHeight = this.minHeight - 2 * y;
30403                 
30404                 var scale = 1;
30405                 
30406                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30407                     scale = targetWidth / width;
30408                 }
30409                 
30410                 if(x > 0 && y == 0){
30411                     scale = targetHeight / height;
30412                 }
30413                 
30414                 if(x > 0 && y > 0){
30415                     scale = targetWidth / width;
30416                     
30417                     if(width < height){
30418                         scale = targetHeight / height;
30419                     }
30420                 }
30421                 
30422                 context.scale(scale, scale);
30423                 
30424                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30425                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30426
30427                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30428                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30429
30430                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30431                 
30432                 break;
30433             case 90 : 
30434                 
30435                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30436                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30437                 
30438                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30439                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30440                 
30441                 var targetWidth = this.minWidth - 2 * x;
30442                 var targetHeight = this.minHeight - 2 * y;
30443                 
30444                 var scale = 1;
30445                 
30446                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30447                     scale = targetWidth / width;
30448                 }
30449                 
30450                 if(x > 0 && y == 0){
30451                     scale = targetHeight / height;
30452                 }
30453                 
30454                 if(x > 0 && y > 0){
30455                     scale = targetWidth / width;
30456                     
30457                     if(width < height){
30458                         scale = targetHeight / height;
30459                     }
30460                 }
30461                 
30462                 context.scale(scale, scale);
30463                 
30464                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30465                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30466
30467                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30468                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30469                 
30470                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30471                 
30472                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30473                 
30474                 break;
30475             case 180 :
30476                 
30477                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30478                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30479                 
30480                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30481                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30482                 
30483                 var targetWidth = this.minWidth - 2 * x;
30484                 var targetHeight = this.minHeight - 2 * y;
30485                 
30486                 var scale = 1;
30487                 
30488                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30489                     scale = targetWidth / width;
30490                 }
30491                 
30492                 if(x > 0 && y == 0){
30493                     scale = targetHeight / height;
30494                 }
30495                 
30496                 if(x > 0 && y > 0){
30497                     scale = targetWidth / width;
30498                     
30499                     if(width < height){
30500                         scale = targetHeight / height;
30501                     }
30502                 }
30503                 
30504                 context.scale(scale, scale);
30505                 
30506                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30507                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30508
30509                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30510                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30511
30512                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30513                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30514                 
30515                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30516                 
30517                 break;
30518             case 270 :
30519                 
30520                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30521                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30522                 
30523                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30524                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30525                 
30526                 var targetWidth = this.minWidth - 2 * x;
30527                 var targetHeight = this.minHeight - 2 * y;
30528                 
30529                 var scale = 1;
30530                 
30531                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30532                     scale = targetWidth / width;
30533                 }
30534                 
30535                 if(x > 0 && y == 0){
30536                     scale = targetHeight / height;
30537                 }
30538                 
30539                 if(x > 0 && y > 0){
30540                     scale = targetWidth / width;
30541                     
30542                     if(width < height){
30543                         scale = targetHeight / height;
30544                     }
30545                 }
30546                 
30547                 context.scale(scale, scale);
30548                 
30549                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30550                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30551
30552                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30553                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30554                 
30555                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30556                 
30557                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30558                 
30559                 break;
30560             default : 
30561                 break;
30562         }
30563         
30564         this.cropData = canvas.toDataURL(this.cropType);
30565         
30566         if(this.fireEvent('crop', this, this.cropData) !== false){
30567             this.process(this.file, this.cropData);
30568         }
30569         
30570         return;
30571         
30572     },
30573     
30574     setThumbBoxSize : function()
30575     {
30576         var width, height;
30577         
30578         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30579             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30580             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30581             
30582             this.minWidth = width;
30583             this.minHeight = height;
30584             
30585             if(this.rotate == 90 || this.rotate == 270){
30586                 this.minWidth = height;
30587                 this.minHeight = width;
30588             }
30589         }
30590         
30591         height = 300;
30592         width = Math.ceil(this.minWidth * height / this.minHeight);
30593         
30594         if(this.minWidth > this.minHeight){
30595             width = 300;
30596             height = Math.ceil(this.minHeight * width / this.minWidth);
30597         }
30598         
30599         this.thumbEl.setStyle({
30600             width : width + 'px',
30601             height : height + 'px'
30602         });
30603
30604         return;
30605             
30606     },
30607     
30608     setThumbBoxPosition : function()
30609     {
30610         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30611         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30612         
30613         this.thumbEl.setLeft(x);
30614         this.thumbEl.setTop(y);
30615         
30616     },
30617     
30618     baseRotateLevel : function()
30619     {
30620         this.baseRotate = 1;
30621         
30622         if(
30623                 typeof(this.exif) != 'undefined' &&
30624                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30625                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30626         ){
30627             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30628         }
30629         
30630         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30631         
30632     },
30633     
30634     baseScaleLevel : function()
30635     {
30636         var width, height;
30637         
30638         if(this.isDocument){
30639             
30640             if(this.baseRotate == 6 || this.baseRotate == 8){
30641             
30642                 height = this.thumbEl.getHeight();
30643                 this.baseScale = height / this.imageEl.OriginWidth;
30644
30645                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30646                     width = this.thumbEl.getWidth();
30647                     this.baseScale = width / this.imageEl.OriginHeight;
30648                 }
30649
30650                 return;
30651             }
30652
30653             height = this.thumbEl.getHeight();
30654             this.baseScale = height / this.imageEl.OriginHeight;
30655
30656             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30657                 width = this.thumbEl.getWidth();
30658                 this.baseScale = width / this.imageEl.OriginWidth;
30659             }
30660
30661             return;
30662         }
30663         
30664         if(this.baseRotate == 6 || this.baseRotate == 8){
30665             
30666             width = this.thumbEl.getHeight();
30667             this.baseScale = width / this.imageEl.OriginHeight;
30668             
30669             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30670                 height = this.thumbEl.getWidth();
30671                 this.baseScale = height / this.imageEl.OriginHeight;
30672             }
30673             
30674             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30675                 height = this.thumbEl.getWidth();
30676                 this.baseScale = height / this.imageEl.OriginHeight;
30677                 
30678                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30679                     width = this.thumbEl.getHeight();
30680                     this.baseScale = width / this.imageEl.OriginWidth;
30681                 }
30682             }
30683             
30684             return;
30685         }
30686         
30687         width = this.thumbEl.getWidth();
30688         this.baseScale = width / this.imageEl.OriginWidth;
30689         
30690         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30691             height = this.thumbEl.getHeight();
30692             this.baseScale = height / this.imageEl.OriginHeight;
30693         }
30694         
30695         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30696             
30697             height = this.thumbEl.getHeight();
30698             this.baseScale = height / this.imageEl.OriginHeight;
30699             
30700             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30701                 width = this.thumbEl.getWidth();
30702                 this.baseScale = width / this.imageEl.OriginWidth;
30703             }
30704             
30705         }
30706         
30707         return;
30708     },
30709     
30710     getScaleLevel : function()
30711     {
30712         return this.baseScale * Math.pow(1.1, this.scale);
30713     },
30714     
30715     onTouchStart : function(e)
30716     {
30717         if(!this.canvasLoaded){
30718             this.beforeSelectFile(e);
30719             return;
30720         }
30721         
30722         var touches = e.browserEvent.touches;
30723         
30724         if(!touches){
30725             return;
30726         }
30727         
30728         if(touches.length == 1){
30729             this.onMouseDown(e);
30730             return;
30731         }
30732         
30733         if(touches.length != 2){
30734             return;
30735         }
30736         
30737         var coords = [];
30738         
30739         for(var i = 0, finger; finger = touches[i]; i++){
30740             coords.push(finger.pageX, finger.pageY);
30741         }
30742         
30743         var x = Math.pow(coords[0] - coords[2], 2);
30744         var y = Math.pow(coords[1] - coords[3], 2);
30745         
30746         this.startDistance = Math.sqrt(x + y);
30747         
30748         this.startScale = this.scale;
30749         
30750         this.pinching = true;
30751         this.dragable = false;
30752         
30753     },
30754     
30755     onTouchMove : function(e)
30756     {
30757         if(!this.pinching && !this.dragable){
30758             return;
30759         }
30760         
30761         var touches = e.browserEvent.touches;
30762         
30763         if(!touches){
30764             return;
30765         }
30766         
30767         if(this.dragable){
30768             this.onMouseMove(e);
30769             return;
30770         }
30771         
30772         var coords = [];
30773         
30774         for(var i = 0, finger; finger = touches[i]; i++){
30775             coords.push(finger.pageX, finger.pageY);
30776         }
30777         
30778         var x = Math.pow(coords[0] - coords[2], 2);
30779         var y = Math.pow(coords[1] - coords[3], 2);
30780         
30781         this.endDistance = Math.sqrt(x + y);
30782         
30783         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30784         
30785         if(!this.zoomable()){
30786             this.scale = this.startScale;
30787             return;
30788         }
30789         
30790         this.draw();
30791         
30792     },
30793     
30794     onTouchEnd : function(e)
30795     {
30796         this.pinching = false;
30797         this.dragable = false;
30798         
30799     },
30800     
30801     process : function(file, crop)
30802     {
30803         if(this.loadMask){
30804             this.maskEl.mask(this.loadingText);
30805         }
30806         
30807         this.xhr = new XMLHttpRequest();
30808         
30809         file.xhr = this.xhr;
30810
30811         this.xhr.open(this.method, this.url, true);
30812         
30813         var headers = {
30814             "Accept": "application/json",
30815             "Cache-Control": "no-cache",
30816             "X-Requested-With": "XMLHttpRequest"
30817         };
30818         
30819         for (var headerName in headers) {
30820             var headerValue = headers[headerName];
30821             if (headerValue) {
30822                 this.xhr.setRequestHeader(headerName, headerValue);
30823             }
30824         }
30825         
30826         var _this = this;
30827         
30828         this.xhr.onload = function()
30829         {
30830             _this.xhrOnLoad(_this.xhr);
30831         }
30832         
30833         this.xhr.onerror = function()
30834         {
30835             _this.xhrOnError(_this.xhr);
30836         }
30837         
30838         var formData = new FormData();
30839
30840         formData.append('returnHTML', 'NO');
30841         
30842         if(crop){
30843             formData.append('crop', crop);
30844         }
30845         
30846         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30847             formData.append(this.paramName, file, file.name);
30848         }
30849         
30850         if(typeof(file.filename) != 'undefined'){
30851             formData.append('filename', file.filename);
30852         }
30853         
30854         if(typeof(file.mimetype) != 'undefined'){
30855             formData.append('mimetype', file.mimetype);
30856         }
30857         
30858         if(this.fireEvent('arrange', this, formData) != false){
30859             this.xhr.send(formData);
30860         };
30861     },
30862     
30863     xhrOnLoad : function(xhr)
30864     {
30865         if(this.loadMask){
30866             this.maskEl.unmask();
30867         }
30868         
30869         if (xhr.readyState !== 4) {
30870             this.fireEvent('exception', this, xhr);
30871             return;
30872         }
30873
30874         var response = Roo.decode(xhr.responseText);
30875         
30876         if(!response.success){
30877             this.fireEvent('exception', this, xhr);
30878             return;
30879         }
30880         
30881         var response = Roo.decode(xhr.responseText);
30882         
30883         this.fireEvent('upload', this, response);
30884         
30885     },
30886     
30887     xhrOnError : function()
30888     {
30889         if(this.loadMask){
30890             this.maskEl.unmask();
30891         }
30892         
30893         Roo.log('xhr on error');
30894         
30895         var response = Roo.decode(xhr.responseText);
30896           
30897         Roo.log(response);
30898         
30899     },
30900     
30901     prepare : function(file)
30902     {   
30903         if(this.loadMask){
30904             this.maskEl.mask(this.loadingText);
30905         }
30906         
30907         this.file = false;
30908         this.exif = {};
30909         
30910         if(typeof(file) === 'string'){
30911             this.loadCanvas(file);
30912             return;
30913         }
30914         
30915         if(!file || !this.urlAPI){
30916             return;
30917         }
30918         
30919         this.file = file;
30920         this.cropType = file.type;
30921         
30922         var _this = this;
30923         
30924         if(this.fireEvent('prepare', this, this.file) != false){
30925             
30926             var reader = new FileReader();
30927             
30928             reader.onload = function (e) {
30929                 if (e.target.error) {
30930                     Roo.log(e.target.error);
30931                     return;
30932                 }
30933                 
30934                 var buffer = e.target.result,
30935                     dataView = new DataView(buffer),
30936                     offset = 2,
30937                     maxOffset = dataView.byteLength - 4,
30938                     markerBytes,
30939                     markerLength;
30940                 
30941                 if (dataView.getUint16(0) === 0xffd8) {
30942                     while (offset < maxOffset) {
30943                         markerBytes = dataView.getUint16(offset);
30944                         
30945                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30946                             markerLength = dataView.getUint16(offset + 2) + 2;
30947                             if (offset + markerLength > dataView.byteLength) {
30948                                 Roo.log('Invalid meta data: Invalid segment size.');
30949                                 break;
30950                             }
30951                             
30952                             if(markerBytes == 0xffe1){
30953                                 _this.parseExifData(
30954                                     dataView,
30955                                     offset,
30956                                     markerLength
30957                                 );
30958                             }
30959                             
30960                             offset += markerLength;
30961                             
30962                             continue;
30963                         }
30964                         
30965                         break;
30966                     }
30967                     
30968                 }
30969                 
30970                 var url = _this.urlAPI.createObjectURL(_this.file);
30971                 
30972                 _this.loadCanvas(url);
30973                 
30974                 return;
30975             }
30976             
30977             reader.readAsArrayBuffer(this.file);
30978             
30979         }
30980         
30981     },
30982     
30983     parseExifData : function(dataView, offset, length)
30984     {
30985         var tiffOffset = offset + 10,
30986             littleEndian,
30987             dirOffset;
30988     
30989         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30990             // No Exif data, might be XMP data instead
30991             return;
30992         }
30993         
30994         // Check for the ASCII code for "Exif" (0x45786966):
30995         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30996             // No Exif data, might be XMP data instead
30997             return;
30998         }
30999         if (tiffOffset + 8 > dataView.byteLength) {
31000             Roo.log('Invalid Exif data: Invalid segment size.');
31001             return;
31002         }
31003         // Check for the two null bytes:
31004         if (dataView.getUint16(offset + 8) !== 0x0000) {
31005             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31006             return;
31007         }
31008         // Check the byte alignment:
31009         switch (dataView.getUint16(tiffOffset)) {
31010         case 0x4949:
31011             littleEndian = true;
31012             break;
31013         case 0x4D4D:
31014             littleEndian = false;
31015             break;
31016         default:
31017             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31018             return;
31019         }
31020         // Check for the TIFF tag marker (0x002A):
31021         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31022             Roo.log('Invalid Exif data: Missing TIFF marker.');
31023             return;
31024         }
31025         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31026         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31027         
31028         this.parseExifTags(
31029             dataView,
31030             tiffOffset,
31031             tiffOffset + dirOffset,
31032             littleEndian
31033         );
31034     },
31035     
31036     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31037     {
31038         var tagsNumber,
31039             dirEndOffset,
31040             i;
31041         if (dirOffset + 6 > dataView.byteLength) {
31042             Roo.log('Invalid Exif data: Invalid directory offset.');
31043             return;
31044         }
31045         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31046         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31047         if (dirEndOffset + 4 > dataView.byteLength) {
31048             Roo.log('Invalid Exif data: Invalid directory size.');
31049             return;
31050         }
31051         for (i = 0; i < tagsNumber; i += 1) {
31052             this.parseExifTag(
31053                 dataView,
31054                 tiffOffset,
31055                 dirOffset + 2 + 12 * i, // tag offset
31056                 littleEndian
31057             );
31058         }
31059         // Return the offset to the next directory:
31060         return dataView.getUint32(dirEndOffset, littleEndian);
31061     },
31062     
31063     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31064     {
31065         var tag = dataView.getUint16(offset, littleEndian);
31066         
31067         this.exif[tag] = this.getExifValue(
31068             dataView,
31069             tiffOffset,
31070             offset,
31071             dataView.getUint16(offset + 2, littleEndian), // tag type
31072             dataView.getUint32(offset + 4, littleEndian), // tag length
31073             littleEndian
31074         );
31075     },
31076     
31077     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31078     {
31079         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31080             tagSize,
31081             dataOffset,
31082             values,
31083             i,
31084             str,
31085             c;
31086     
31087         if (!tagType) {
31088             Roo.log('Invalid Exif data: Invalid tag type.');
31089             return;
31090         }
31091         
31092         tagSize = tagType.size * length;
31093         // Determine if the value is contained in the dataOffset bytes,
31094         // or if the value at the dataOffset is a pointer to the actual data:
31095         dataOffset = tagSize > 4 ?
31096                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31097         if (dataOffset + tagSize > dataView.byteLength) {
31098             Roo.log('Invalid Exif data: Invalid data offset.');
31099             return;
31100         }
31101         if (length === 1) {
31102             return tagType.getValue(dataView, dataOffset, littleEndian);
31103         }
31104         values = [];
31105         for (i = 0; i < length; i += 1) {
31106             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31107         }
31108         
31109         if (tagType.ascii) {
31110             str = '';
31111             // Concatenate the chars:
31112             for (i = 0; i < values.length; i += 1) {
31113                 c = values[i];
31114                 // Ignore the terminating NULL byte(s):
31115                 if (c === '\u0000') {
31116                     break;
31117                 }
31118                 str += c;
31119             }
31120             return str;
31121         }
31122         return values;
31123     }
31124     
31125 });
31126
31127 Roo.apply(Roo.bootstrap.UploadCropbox, {
31128     tags : {
31129         'Orientation': 0x0112
31130     },
31131     
31132     Orientation: {
31133             1: 0, //'top-left',
31134 //            2: 'top-right',
31135             3: 180, //'bottom-right',
31136 //            4: 'bottom-left',
31137 //            5: 'left-top',
31138             6: 90, //'right-top',
31139 //            7: 'right-bottom',
31140             8: 270 //'left-bottom'
31141     },
31142     
31143     exifTagTypes : {
31144         // byte, 8-bit unsigned int:
31145         1: {
31146             getValue: function (dataView, dataOffset) {
31147                 return dataView.getUint8(dataOffset);
31148             },
31149             size: 1
31150         },
31151         // ascii, 8-bit byte:
31152         2: {
31153             getValue: function (dataView, dataOffset) {
31154                 return String.fromCharCode(dataView.getUint8(dataOffset));
31155             },
31156             size: 1,
31157             ascii: true
31158         },
31159         // short, 16 bit int:
31160         3: {
31161             getValue: function (dataView, dataOffset, littleEndian) {
31162                 return dataView.getUint16(dataOffset, littleEndian);
31163             },
31164             size: 2
31165         },
31166         // long, 32 bit int:
31167         4: {
31168             getValue: function (dataView, dataOffset, littleEndian) {
31169                 return dataView.getUint32(dataOffset, littleEndian);
31170             },
31171             size: 4
31172         },
31173         // rational = two long values, first is numerator, second is denominator:
31174         5: {
31175             getValue: function (dataView, dataOffset, littleEndian) {
31176                 return dataView.getUint32(dataOffset, littleEndian) /
31177                     dataView.getUint32(dataOffset + 4, littleEndian);
31178             },
31179             size: 8
31180         },
31181         // slong, 32 bit signed int:
31182         9: {
31183             getValue: function (dataView, dataOffset, littleEndian) {
31184                 return dataView.getInt32(dataOffset, littleEndian);
31185             },
31186             size: 4
31187         },
31188         // srational, two slongs, first is numerator, second is denominator:
31189         10: {
31190             getValue: function (dataView, dataOffset, littleEndian) {
31191                 return dataView.getInt32(dataOffset, littleEndian) /
31192                     dataView.getInt32(dataOffset + 4, littleEndian);
31193             },
31194             size: 8
31195         }
31196     },
31197     
31198     footer : {
31199         STANDARD : [
31200             {
31201                 tag : 'div',
31202                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31203                 action : 'rotate-left',
31204                 cn : [
31205                     {
31206                         tag : 'button',
31207                         cls : 'btn btn-default',
31208                         html : '<i class="fa fa-undo"></i>'
31209                     }
31210                 ]
31211             },
31212             {
31213                 tag : 'div',
31214                 cls : 'btn-group roo-upload-cropbox-picture',
31215                 action : 'picture',
31216                 cn : [
31217                     {
31218                         tag : 'button',
31219                         cls : 'btn btn-default',
31220                         html : '<i class="fa fa-picture-o"></i>'
31221                     }
31222                 ]
31223             },
31224             {
31225                 tag : 'div',
31226                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31227                 action : 'rotate-right',
31228                 cn : [
31229                     {
31230                         tag : 'button',
31231                         cls : 'btn btn-default',
31232                         html : '<i class="fa fa-repeat"></i>'
31233                     }
31234                 ]
31235             }
31236         ],
31237         DOCUMENT : [
31238             {
31239                 tag : 'div',
31240                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31241                 action : 'rotate-left',
31242                 cn : [
31243                     {
31244                         tag : 'button',
31245                         cls : 'btn btn-default',
31246                         html : '<i class="fa fa-undo"></i>'
31247                     }
31248                 ]
31249             },
31250             {
31251                 tag : 'div',
31252                 cls : 'btn-group roo-upload-cropbox-download',
31253                 action : 'download',
31254                 cn : [
31255                     {
31256                         tag : 'button',
31257                         cls : 'btn btn-default',
31258                         html : '<i class="fa fa-download"></i>'
31259                     }
31260                 ]
31261             },
31262             {
31263                 tag : 'div',
31264                 cls : 'btn-group roo-upload-cropbox-crop',
31265                 action : 'crop',
31266                 cn : [
31267                     {
31268                         tag : 'button',
31269                         cls : 'btn btn-default',
31270                         html : '<i class="fa fa-crop"></i>'
31271                     }
31272                 ]
31273             },
31274             {
31275                 tag : 'div',
31276                 cls : 'btn-group roo-upload-cropbox-trash',
31277                 action : 'trash',
31278                 cn : [
31279                     {
31280                         tag : 'button',
31281                         cls : 'btn btn-default',
31282                         html : '<i class="fa fa-trash"></i>'
31283                     }
31284                 ]
31285             },
31286             {
31287                 tag : 'div',
31288                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31289                 action : 'rotate-right',
31290                 cn : [
31291                     {
31292                         tag : 'button',
31293                         cls : 'btn btn-default',
31294                         html : '<i class="fa fa-repeat"></i>'
31295                     }
31296                 ]
31297             }
31298         ],
31299         ROTATOR : [
31300             {
31301                 tag : 'div',
31302                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31303                 action : 'rotate-left',
31304                 cn : [
31305                     {
31306                         tag : 'button',
31307                         cls : 'btn btn-default',
31308                         html : '<i class="fa fa-undo"></i>'
31309                     }
31310                 ]
31311             },
31312             {
31313                 tag : 'div',
31314                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31315                 action : 'rotate-right',
31316                 cn : [
31317                     {
31318                         tag : 'button',
31319                         cls : 'btn btn-default',
31320                         html : '<i class="fa fa-repeat"></i>'
31321                     }
31322                 ]
31323             }
31324         ]
31325     }
31326 });
31327
31328 /*
31329 * Licence: LGPL
31330 */
31331
31332 /**
31333  * @class Roo.bootstrap.DocumentManager
31334  * @extends Roo.bootstrap.Component
31335  * Bootstrap DocumentManager class
31336  * @cfg {String} paramName default 'imageUpload'
31337  * @cfg {String} toolTipName default 'filename'
31338  * @cfg {String} method default POST
31339  * @cfg {String} url action url
31340  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31341  * @cfg {Boolean} multiple multiple upload default true
31342  * @cfg {Number} thumbSize default 300
31343  * @cfg {String} fieldLabel
31344  * @cfg {Number} labelWidth default 4
31345  * @cfg {String} labelAlign (left|top) default left
31346  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31347 * @cfg {Number} labellg set the width of label (1-12)
31348  * @cfg {Number} labelmd set the width of label (1-12)
31349  * @cfg {Number} labelsm set the width of label (1-12)
31350  * @cfg {Number} labelxs set the width of label (1-12)
31351  * 
31352  * @constructor
31353  * Create a new DocumentManager
31354  * @param {Object} config The config object
31355  */
31356
31357 Roo.bootstrap.DocumentManager = function(config){
31358     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31359     
31360     this.files = [];
31361     this.delegates = [];
31362     
31363     this.addEvents({
31364         /**
31365          * @event initial
31366          * Fire when initial the DocumentManager
31367          * @param {Roo.bootstrap.DocumentManager} this
31368          */
31369         "initial" : true,
31370         /**
31371          * @event inspect
31372          * inspect selected file
31373          * @param {Roo.bootstrap.DocumentManager} this
31374          * @param {File} file
31375          */
31376         "inspect" : true,
31377         /**
31378          * @event exception
31379          * Fire when xhr load exception
31380          * @param {Roo.bootstrap.DocumentManager} this
31381          * @param {XMLHttpRequest} xhr
31382          */
31383         "exception" : true,
31384         /**
31385          * @event afterupload
31386          * Fire when xhr load exception
31387          * @param {Roo.bootstrap.DocumentManager} this
31388          * @param {XMLHttpRequest} xhr
31389          */
31390         "afterupload" : true,
31391         /**
31392          * @event prepare
31393          * prepare the form data
31394          * @param {Roo.bootstrap.DocumentManager} this
31395          * @param {Object} formData
31396          */
31397         "prepare" : true,
31398         /**
31399          * @event remove
31400          * Fire when remove the file
31401          * @param {Roo.bootstrap.DocumentManager} this
31402          * @param {Object} file
31403          */
31404         "remove" : true,
31405         /**
31406          * @event refresh
31407          * Fire after refresh the file
31408          * @param {Roo.bootstrap.DocumentManager} this
31409          */
31410         "refresh" : true,
31411         /**
31412          * @event click
31413          * Fire after click the image
31414          * @param {Roo.bootstrap.DocumentManager} this
31415          * @param {Object} file
31416          */
31417         "click" : true,
31418         /**
31419          * @event edit
31420          * Fire when upload a image and editable set to true
31421          * @param {Roo.bootstrap.DocumentManager} this
31422          * @param {Object} file
31423          */
31424         "edit" : true,
31425         /**
31426          * @event beforeselectfile
31427          * Fire before select file
31428          * @param {Roo.bootstrap.DocumentManager} this
31429          */
31430         "beforeselectfile" : true,
31431         /**
31432          * @event process
31433          * Fire before process file
31434          * @param {Roo.bootstrap.DocumentManager} this
31435          * @param {Object} file
31436          */
31437         "process" : true,
31438         /**
31439          * @event previewrendered
31440          * Fire when preview rendered
31441          * @param {Roo.bootstrap.DocumentManager} this
31442          * @param {Object} file
31443          */
31444         "previewrendered" : true,
31445         /**
31446          */
31447         "previewResize" : true
31448         
31449     });
31450 };
31451
31452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31453     
31454     boxes : 0,
31455     inputName : '',
31456     thumbSize : 300,
31457     multiple : true,
31458     files : false,
31459     method : 'POST',
31460     url : '',
31461     paramName : 'imageUpload',
31462     toolTipName : 'filename',
31463     fieldLabel : '',
31464     labelWidth : 4,
31465     labelAlign : 'left',
31466     editable : true,
31467     delegates : false,
31468     xhr : false, 
31469     
31470     labellg : 0,
31471     labelmd : 0,
31472     labelsm : 0,
31473     labelxs : 0,
31474     
31475     getAutoCreate : function()
31476     {   
31477         var managerWidget = {
31478             tag : 'div',
31479             cls : 'roo-document-manager',
31480             cn : [
31481                 {
31482                     tag : 'input',
31483                     cls : 'roo-document-manager-selector',
31484                     type : 'file'
31485                 },
31486                 {
31487                     tag : 'div',
31488                     cls : 'roo-document-manager-uploader',
31489                     cn : [
31490                         {
31491                             tag : 'div',
31492                             cls : 'roo-document-manager-upload-btn',
31493                             html : '<i class="fa fa-plus"></i>'
31494                         }
31495                     ]
31496                     
31497                 }
31498             ]
31499         };
31500         
31501         var content = [
31502             {
31503                 tag : 'div',
31504                 cls : 'column col-md-12',
31505                 cn : managerWidget
31506             }
31507         ];
31508         
31509         if(this.fieldLabel.length){
31510             
31511             content = [
31512                 {
31513                     tag : 'div',
31514                     cls : 'column col-md-12',
31515                     html : this.fieldLabel
31516                 },
31517                 {
31518                     tag : 'div',
31519                     cls : 'column col-md-12',
31520                     cn : managerWidget
31521                 }
31522             ];
31523
31524             if(this.labelAlign == 'left'){
31525                 content = [
31526                     {
31527                         tag : 'div',
31528                         cls : 'column',
31529                         html : this.fieldLabel
31530                     },
31531                     {
31532                         tag : 'div',
31533                         cls : 'column',
31534                         cn : managerWidget
31535                     }
31536                 ];
31537                 
31538                 if(this.labelWidth > 12){
31539                     content[0].style = "width: " + this.labelWidth + 'px';
31540                 }
31541
31542                 if(this.labelWidth < 13 && this.labelmd == 0){
31543                     this.labelmd = this.labelWidth;
31544                 }
31545
31546                 if(this.labellg > 0){
31547                     content[0].cls += ' col-lg-' + this.labellg;
31548                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31549                 }
31550
31551                 if(this.labelmd > 0){
31552                     content[0].cls += ' col-md-' + this.labelmd;
31553                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31554                 }
31555
31556                 if(this.labelsm > 0){
31557                     content[0].cls += ' col-sm-' + this.labelsm;
31558                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31559                 }
31560
31561                 if(this.labelxs > 0){
31562                     content[0].cls += ' col-xs-' + this.labelxs;
31563                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31564                 }
31565                 
31566             }
31567         }
31568         
31569         var cfg = {
31570             tag : 'div',
31571             cls : 'row clearfix',
31572             cn : content
31573         };
31574         
31575         return cfg;
31576         
31577     },
31578     
31579     initEvents : function()
31580     {
31581         this.managerEl = this.el.select('.roo-document-manager', true).first();
31582         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31583         
31584         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31585         this.selectorEl.hide();
31586         
31587         if(this.multiple){
31588             this.selectorEl.attr('multiple', 'multiple');
31589         }
31590         
31591         this.selectorEl.on('change', this.onFileSelected, this);
31592         
31593         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31594         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31595         
31596         this.uploader.on('click', this.onUploaderClick, this);
31597         
31598         this.renderProgressDialog();
31599         
31600         var _this = this;
31601         
31602         window.addEventListener("resize", function() { _this.refresh(); } );
31603         
31604         this.fireEvent('initial', this);
31605     },
31606     
31607     renderProgressDialog : function()
31608     {
31609         var _this = this;
31610         
31611         this.progressDialog = new Roo.bootstrap.Modal({
31612             cls : 'roo-document-manager-progress-dialog',
31613             allow_close : false,
31614             animate : false,
31615             title : '',
31616             buttons : [
31617                 {
31618                     name  :'cancel',
31619                     weight : 'danger',
31620                     html : 'Cancel'
31621                 }
31622             ], 
31623             listeners : { 
31624                 btnclick : function() {
31625                     _this.uploadCancel();
31626                     this.hide();
31627                 }
31628             }
31629         });
31630          
31631         this.progressDialog.render(Roo.get(document.body));
31632          
31633         this.progress = new Roo.bootstrap.Progress({
31634             cls : 'roo-document-manager-progress',
31635             active : true,
31636             striped : true
31637         });
31638         
31639         this.progress.render(this.progressDialog.getChildContainer());
31640         
31641         this.progressBar = new Roo.bootstrap.ProgressBar({
31642             cls : 'roo-document-manager-progress-bar',
31643             aria_valuenow : 0,
31644             aria_valuemin : 0,
31645             aria_valuemax : 12,
31646             panel : 'success'
31647         });
31648         
31649         this.progressBar.render(this.progress.getChildContainer());
31650     },
31651     
31652     onUploaderClick : function(e)
31653     {
31654         e.preventDefault();
31655      
31656         if(this.fireEvent('beforeselectfile', this) != false){
31657             this.selectorEl.dom.click();
31658         }
31659         
31660     },
31661     
31662     onFileSelected : function(e)
31663     {
31664         e.preventDefault();
31665         
31666         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31667             return;
31668         }
31669         
31670         Roo.each(this.selectorEl.dom.files, function(file){
31671             if(this.fireEvent('inspect', this, file) != false){
31672                 this.files.push(file);
31673             }
31674         }, this);
31675         
31676         this.queue();
31677         
31678     },
31679     
31680     queue : function()
31681     {
31682         this.selectorEl.dom.value = '';
31683         
31684         if(!this.files || !this.files.length){
31685             return;
31686         }
31687         
31688         if(this.boxes > 0 && this.files.length > this.boxes){
31689             this.files = this.files.slice(0, this.boxes);
31690         }
31691         
31692         this.uploader.show();
31693         
31694         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31695             this.uploader.hide();
31696         }
31697         
31698         var _this = this;
31699         
31700         var files = [];
31701         
31702         var docs = [];
31703         
31704         Roo.each(this.files, function(file){
31705             
31706             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31707                 var f = this.renderPreview(file);
31708                 files.push(f);
31709                 return;
31710             }
31711             
31712             if(file.type.indexOf('image') != -1){
31713                 this.delegates.push(
31714                     (function(){
31715                         _this.process(file);
31716                     }).createDelegate(this)
31717                 );
31718         
31719                 return;
31720             }
31721             
31722             docs.push(
31723                 (function(){
31724                     _this.process(file);
31725                 }).createDelegate(this)
31726             );
31727             
31728         }, this);
31729         
31730         this.files = files;
31731         
31732         this.delegates = this.delegates.concat(docs);
31733         
31734         if(!this.delegates.length){
31735             this.refresh();
31736             return;
31737         }
31738         
31739         this.progressBar.aria_valuemax = this.delegates.length;
31740         
31741         this.arrange();
31742         
31743         return;
31744     },
31745     
31746     arrange : function()
31747     {
31748         if(!this.delegates.length){
31749             this.progressDialog.hide();
31750             this.refresh();
31751             return;
31752         }
31753         
31754         var delegate = this.delegates.shift();
31755         
31756         this.progressDialog.show();
31757         
31758         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31759         
31760         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31761         
31762         delegate();
31763     },
31764     
31765     refresh : function()
31766     {
31767         this.uploader.show();
31768         
31769         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31770             this.uploader.hide();
31771         }
31772         
31773         Roo.isTouch ? this.closable(false) : this.closable(true);
31774         
31775         this.fireEvent('refresh', this);
31776     },
31777     
31778     onRemove : function(e, el, o)
31779     {
31780         e.preventDefault();
31781         
31782         this.fireEvent('remove', this, o);
31783         
31784     },
31785     
31786     remove : function(o)
31787     {
31788         var files = [];
31789         
31790         Roo.each(this.files, function(file){
31791             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31792                 files.push(file);
31793                 return;
31794             }
31795
31796             o.target.remove();
31797
31798         }, this);
31799         
31800         this.files = files;
31801         
31802         this.refresh();
31803     },
31804     
31805     clear : function()
31806     {
31807         Roo.each(this.files, function(file){
31808             if(!file.target){
31809                 return;
31810             }
31811             
31812             file.target.remove();
31813
31814         }, this);
31815         
31816         this.files = [];
31817         
31818         this.refresh();
31819     },
31820     
31821     onClick : function(e, el, o)
31822     {
31823         e.preventDefault();
31824         
31825         this.fireEvent('click', this, o);
31826         
31827     },
31828     
31829     closable : function(closable)
31830     {
31831         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31832             
31833             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31834             
31835             if(closable){
31836                 el.show();
31837                 return;
31838             }
31839             
31840             el.hide();
31841             
31842         }, this);
31843     },
31844     
31845     xhrOnLoad : function(xhr)
31846     {
31847         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31848             el.remove();
31849         }, this);
31850         
31851         if (xhr.readyState !== 4) {
31852             this.arrange();
31853             this.fireEvent('exception', this, xhr);
31854             return;
31855         }
31856
31857         var response = Roo.decode(xhr.responseText);
31858         
31859         if(!response.success){
31860             this.arrange();
31861             this.fireEvent('exception', this, xhr);
31862             return;
31863         }
31864         
31865         var file = this.renderPreview(response.data);
31866         
31867         this.files.push(file);
31868         
31869         this.arrange();
31870         
31871         this.fireEvent('afterupload', this, xhr);
31872         
31873     },
31874     
31875     xhrOnError : function(xhr)
31876     {
31877         Roo.log('xhr on error');
31878         
31879         var response = Roo.decode(xhr.responseText);
31880           
31881         Roo.log(response);
31882         
31883         this.arrange();
31884     },
31885     
31886     process : function(file)
31887     {
31888         if(this.fireEvent('process', this, file) !== false){
31889             if(this.editable && file.type.indexOf('image') != -1){
31890                 this.fireEvent('edit', this, file);
31891                 return;
31892             }
31893
31894             this.uploadStart(file, false);
31895
31896             return;
31897         }
31898         
31899     },
31900     
31901     uploadStart : function(file, crop)
31902     {
31903         this.xhr = new XMLHttpRequest();
31904         
31905         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31906             this.arrange();
31907             return;
31908         }
31909         
31910         file.xhr = this.xhr;
31911             
31912         this.managerEl.createChild({
31913             tag : 'div',
31914             cls : 'roo-document-manager-loading',
31915             cn : [
31916                 {
31917                     tag : 'div',
31918                     tooltip : file.name,
31919                     cls : 'roo-document-manager-thumb',
31920                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31921                 }
31922             ]
31923
31924         });
31925
31926         this.xhr.open(this.method, this.url, true);
31927         
31928         var headers = {
31929             "Accept": "application/json",
31930             "Cache-Control": "no-cache",
31931             "X-Requested-With": "XMLHttpRequest"
31932         };
31933         
31934         for (var headerName in headers) {
31935             var headerValue = headers[headerName];
31936             if (headerValue) {
31937                 this.xhr.setRequestHeader(headerName, headerValue);
31938             }
31939         }
31940         
31941         var _this = this;
31942         
31943         this.xhr.onload = function()
31944         {
31945             _this.xhrOnLoad(_this.xhr);
31946         }
31947         
31948         this.xhr.onerror = function()
31949         {
31950             _this.xhrOnError(_this.xhr);
31951         }
31952         
31953         var formData = new FormData();
31954
31955         formData.append('returnHTML', 'NO');
31956         
31957         if(crop){
31958             formData.append('crop', crop);
31959         }
31960         
31961         formData.append(this.paramName, file, file.name);
31962         
31963         var options = {
31964             file : file, 
31965             manually : false
31966         };
31967         
31968         if(this.fireEvent('prepare', this, formData, options) != false){
31969             
31970             if(options.manually){
31971                 return;
31972             }
31973             
31974             this.xhr.send(formData);
31975             return;
31976         };
31977         
31978         this.uploadCancel();
31979     },
31980     
31981     uploadCancel : function()
31982     {
31983         if (this.xhr) {
31984             this.xhr.abort();
31985         }
31986         
31987         this.delegates = [];
31988         
31989         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31990             el.remove();
31991         }, this);
31992         
31993         this.arrange();
31994     },
31995     
31996     renderPreview : function(file)
31997     {
31998         if(typeof(file.target) != 'undefined' && file.target){
31999             return file;
32000         }
32001         
32002         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32003         
32004         var previewEl = this.managerEl.createChild({
32005             tag : 'div',
32006             cls : 'roo-document-manager-preview',
32007             cn : [
32008                 {
32009                     tag : 'div',
32010                     tooltip : file[this.toolTipName],
32011                     cls : 'roo-document-manager-thumb',
32012                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32013                 },
32014                 {
32015                     tag : 'button',
32016                     cls : 'close',
32017                     html : '<i class="fa fa-times-circle"></i>'
32018                 }
32019             ]
32020         });
32021
32022         var close = previewEl.select('button.close', true).first();
32023
32024         close.on('click', this.onRemove, this, file);
32025
32026         file.target = previewEl;
32027
32028         var image = previewEl.select('img', true).first();
32029         
32030         var _this = this;
32031         
32032         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32033         
32034         image.on('click', this.onClick, this, file);
32035         
32036         this.fireEvent('previewrendered', this, file);
32037         
32038         return file;
32039         
32040     },
32041     
32042     onPreviewLoad : function(file, image)
32043     {
32044         if(typeof(file.target) == 'undefined' || !file.target){
32045             return;
32046         }
32047         
32048         var width = image.dom.naturalWidth || image.dom.width;
32049         var height = image.dom.naturalHeight || image.dom.height;
32050         
32051         if(!this.previewResize) {
32052             return;
32053         }
32054         
32055         if(width > height){
32056             file.target.addClass('wide');
32057             return;
32058         }
32059         
32060         file.target.addClass('tall');
32061         return;
32062         
32063     },
32064     
32065     uploadFromSource : function(file, crop)
32066     {
32067         this.xhr = new XMLHttpRequest();
32068         
32069         this.managerEl.createChild({
32070             tag : 'div',
32071             cls : 'roo-document-manager-loading',
32072             cn : [
32073                 {
32074                     tag : 'div',
32075                     tooltip : file.name,
32076                     cls : 'roo-document-manager-thumb',
32077                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32078                 }
32079             ]
32080
32081         });
32082
32083         this.xhr.open(this.method, this.url, true);
32084         
32085         var headers = {
32086             "Accept": "application/json",
32087             "Cache-Control": "no-cache",
32088             "X-Requested-With": "XMLHttpRequest"
32089         };
32090         
32091         for (var headerName in headers) {
32092             var headerValue = headers[headerName];
32093             if (headerValue) {
32094                 this.xhr.setRequestHeader(headerName, headerValue);
32095             }
32096         }
32097         
32098         var _this = this;
32099         
32100         this.xhr.onload = function()
32101         {
32102             _this.xhrOnLoad(_this.xhr);
32103         }
32104         
32105         this.xhr.onerror = function()
32106         {
32107             _this.xhrOnError(_this.xhr);
32108         }
32109         
32110         var formData = new FormData();
32111
32112         formData.append('returnHTML', 'NO');
32113         
32114         formData.append('crop', crop);
32115         
32116         if(typeof(file.filename) != 'undefined'){
32117             formData.append('filename', file.filename);
32118         }
32119         
32120         if(typeof(file.mimetype) != 'undefined'){
32121             formData.append('mimetype', file.mimetype);
32122         }
32123         
32124         Roo.log(formData);
32125         
32126         if(this.fireEvent('prepare', this, formData) != false){
32127             this.xhr.send(formData);
32128         };
32129     }
32130 });
32131
32132 /*
32133 * Licence: LGPL
32134 */
32135
32136 /**
32137  * @class Roo.bootstrap.DocumentViewer
32138  * @extends Roo.bootstrap.Component
32139  * Bootstrap DocumentViewer class
32140  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32141  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32142  * 
32143  * @constructor
32144  * Create a new DocumentViewer
32145  * @param {Object} config The config object
32146  */
32147
32148 Roo.bootstrap.DocumentViewer = function(config){
32149     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32150     
32151     this.addEvents({
32152         /**
32153          * @event initial
32154          * Fire after initEvent
32155          * @param {Roo.bootstrap.DocumentViewer} this
32156          */
32157         "initial" : true,
32158         /**
32159          * @event click
32160          * Fire after click
32161          * @param {Roo.bootstrap.DocumentViewer} this
32162          */
32163         "click" : true,
32164         /**
32165          * @event download
32166          * Fire after download button
32167          * @param {Roo.bootstrap.DocumentViewer} this
32168          */
32169         "download" : true,
32170         /**
32171          * @event trash
32172          * Fire after trash button
32173          * @param {Roo.bootstrap.DocumentViewer} this
32174          */
32175         "trash" : true
32176         
32177     });
32178 };
32179
32180 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32181     
32182     showDownload : true,
32183     
32184     showTrash : true,
32185     
32186     getAutoCreate : function()
32187     {
32188         var cfg = {
32189             tag : 'div',
32190             cls : 'roo-document-viewer',
32191             cn : [
32192                 {
32193                     tag : 'div',
32194                     cls : 'roo-document-viewer-body',
32195                     cn : [
32196                         {
32197                             tag : 'div',
32198                             cls : 'roo-document-viewer-thumb',
32199                             cn : [
32200                                 {
32201                                     tag : 'img',
32202                                     cls : 'roo-document-viewer-image'
32203                                 }
32204                             ]
32205                         }
32206                     ]
32207                 },
32208                 {
32209                     tag : 'div',
32210                     cls : 'roo-document-viewer-footer',
32211                     cn : {
32212                         tag : 'div',
32213                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32214                         cn : [
32215                             {
32216                                 tag : 'div',
32217                                 cls : 'btn-group roo-document-viewer-download',
32218                                 cn : [
32219                                     {
32220                                         tag : 'button',
32221                                         cls : 'btn btn-default',
32222                                         html : '<i class="fa fa-download"></i>'
32223                                     }
32224                                 ]
32225                             },
32226                             {
32227                                 tag : 'div',
32228                                 cls : 'btn-group roo-document-viewer-trash',
32229                                 cn : [
32230                                     {
32231                                         tag : 'button',
32232                                         cls : 'btn btn-default',
32233                                         html : '<i class="fa fa-trash"></i>'
32234                                     }
32235                                 ]
32236                             }
32237                         ]
32238                     }
32239                 }
32240             ]
32241         };
32242         
32243         return cfg;
32244     },
32245     
32246     initEvents : function()
32247     {
32248         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32249         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32250         
32251         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32252         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32253         
32254         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32255         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32256         
32257         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32258         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32259         
32260         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32261         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32262         
32263         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32264         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32265         
32266         this.bodyEl.on('click', this.onClick, this);
32267         this.downloadBtn.on('click', this.onDownload, this);
32268         this.trashBtn.on('click', this.onTrash, this);
32269         
32270         this.downloadBtn.hide();
32271         this.trashBtn.hide();
32272         
32273         if(this.showDownload){
32274             this.downloadBtn.show();
32275         }
32276         
32277         if(this.showTrash){
32278             this.trashBtn.show();
32279         }
32280         
32281         if(!this.showDownload && !this.showTrash) {
32282             this.footerEl.hide();
32283         }
32284         
32285     },
32286     
32287     initial : function()
32288     {
32289         this.fireEvent('initial', this);
32290         
32291     },
32292     
32293     onClick : function(e)
32294     {
32295         e.preventDefault();
32296         
32297         this.fireEvent('click', this);
32298     },
32299     
32300     onDownload : function(e)
32301     {
32302         e.preventDefault();
32303         
32304         this.fireEvent('download', this);
32305     },
32306     
32307     onTrash : function(e)
32308     {
32309         e.preventDefault();
32310         
32311         this.fireEvent('trash', this);
32312     }
32313     
32314 });
32315 /*
32316  * - LGPL
32317  *
32318  * nav progress bar
32319  * 
32320  */
32321
32322 /**
32323  * @class Roo.bootstrap.NavProgressBar
32324  * @extends Roo.bootstrap.Component
32325  * Bootstrap NavProgressBar class
32326  * 
32327  * @constructor
32328  * Create a new nav progress bar
32329  * @param {Object} config The config object
32330  */
32331
32332 Roo.bootstrap.NavProgressBar = function(config){
32333     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32334
32335     this.bullets = this.bullets || [];
32336    
32337 //    Roo.bootstrap.NavProgressBar.register(this);
32338      this.addEvents({
32339         /**
32340              * @event changed
32341              * Fires when the active item changes
32342              * @param {Roo.bootstrap.NavProgressBar} this
32343              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32344              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32345          */
32346         'changed': true
32347      });
32348     
32349 };
32350
32351 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32352     
32353     bullets : [],
32354     barItems : [],
32355     
32356     getAutoCreate : function()
32357     {
32358         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32359         
32360         cfg = {
32361             tag : 'div',
32362             cls : 'roo-navigation-bar-group',
32363             cn : [
32364                 {
32365                     tag : 'div',
32366                     cls : 'roo-navigation-top-bar'
32367                 },
32368                 {
32369                     tag : 'div',
32370                     cls : 'roo-navigation-bullets-bar',
32371                     cn : [
32372                         {
32373                             tag : 'ul',
32374                             cls : 'roo-navigation-bar'
32375                         }
32376                     ]
32377                 },
32378                 
32379                 {
32380                     tag : 'div',
32381                     cls : 'roo-navigation-bottom-bar'
32382                 }
32383             ]
32384             
32385         };
32386         
32387         return cfg;
32388         
32389     },
32390     
32391     initEvents: function() 
32392     {
32393         
32394     },
32395     
32396     onRender : function(ct, position) 
32397     {
32398         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32399         
32400         if(this.bullets.length){
32401             Roo.each(this.bullets, function(b){
32402                this.addItem(b);
32403             }, this);
32404         }
32405         
32406         this.format();
32407         
32408     },
32409     
32410     addItem : function(cfg)
32411     {
32412         var item = new Roo.bootstrap.NavProgressItem(cfg);
32413         
32414         item.parentId = this.id;
32415         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32416         
32417         if(cfg.html){
32418             var top = new Roo.bootstrap.Element({
32419                 tag : 'div',
32420                 cls : 'roo-navigation-bar-text'
32421             });
32422             
32423             var bottom = new Roo.bootstrap.Element({
32424                 tag : 'div',
32425                 cls : 'roo-navigation-bar-text'
32426             });
32427             
32428             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32429             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32430             
32431             var topText = new Roo.bootstrap.Element({
32432                 tag : 'span',
32433                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32434             });
32435             
32436             var bottomText = new Roo.bootstrap.Element({
32437                 tag : 'span',
32438                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32439             });
32440             
32441             topText.onRender(top.el, null);
32442             bottomText.onRender(bottom.el, null);
32443             
32444             item.topEl = top;
32445             item.bottomEl = bottom;
32446         }
32447         
32448         this.barItems.push(item);
32449         
32450         return item;
32451     },
32452     
32453     getActive : function()
32454     {
32455         var active = false;
32456         
32457         Roo.each(this.barItems, function(v){
32458             
32459             if (!v.isActive()) {
32460                 return;
32461             }
32462             
32463             active = v;
32464             return false;
32465             
32466         });
32467         
32468         return active;
32469     },
32470     
32471     setActiveItem : function(item)
32472     {
32473         var prev = false;
32474         
32475         Roo.each(this.barItems, function(v){
32476             if (v.rid == item.rid) {
32477                 return ;
32478             }
32479             
32480             if (v.isActive()) {
32481                 v.setActive(false);
32482                 prev = v;
32483             }
32484         });
32485
32486         item.setActive(true);
32487         
32488         this.fireEvent('changed', this, item, prev);
32489     },
32490     
32491     getBarItem: function(rid)
32492     {
32493         var ret = false;
32494         
32495         Roo.each(this.barItems, function(e) {
32496             if (e.rid != rid) {
32497                 return;
32498             }
32499             
32500             ret =  e;
32501             return false;
32502         });
32503         
32504         return ret;
32505     },
32506     
32507     indexOfItem : function(item)
32508     {
32509         var index = false;
32510         
32511         Roo.each(this.barItems, function(v, i){
32512             
32513             if (v.rid != item.rid) {
32514                 return;
32515             }
32516             
32517             index = i;
32518             return false
32519         });
32520         
32521         return index;
32522     },
32523     
32524     setActiveNext : function()
32525     {
32526         var i = this.indexOfItem(this.getActive());
32527         
32528         if (i > this.barItems.length) {
32529             return;
32530         }
32531         
32532         this.setActiveItem(this.barItems[i+1]);
32533     },
32534     
32535     setActivePrev : function()
32536     {
32537         var i = this.indexOfItem(this.getActive());
32538         
32539         if (i  < 1) {
32540             return;
32541         }
32542         
32543         this.setActiveItem(this.barItems[i-1]);
32544     },
32545     
32546     format : function()
32547     {
32548         if(!this.barItems.length){
32549             return;
32550         }
32551      
32552         var width = 100 / this.barItems.length;
32553         
32554         Roo.each(this.barItems, function(i){
32555             i.el.setStyle('width', width + '%');
32556             i.topEl.el.setStyle('width', width + '%');
32557             i.bottomEl.el.setStyle('width', width + '%');
32558         }, this);
32559         
32560     }
32561     
32562 });
32563 /*
32564  * - LGPL
32565  *
32566  * Nav Progress Item
32567  * 
32568  */
32569
32570 /**
32571  * @class Roo.bootstrap.NavProgressItem
32572  * @extends Roo.bootstrap.Component
32573  * Bootstrap NavProgressItem class
32574  * @cfg {String} rid the reference id
32575  * @cfg {Boolean} active (true|false) Is item active default false
32576  * @cfg {Boolean} disabled (true|false) Is item active default false
32577  * @cfg {String} html
32578  * @cfg {String} position (top|bottom) text position default bottom
32579  * @cfg {String} icon show icon instead of number
32580  * 
32581  * @constructor
32582  * Create a new NavProgressItem
32583  * @param {Object} config The config object
32584  */
32585 Roo.bootstrap.NavProgressItem = function(config){
32586     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32587     this.addEvents({
32588         // raw events
32589         /**
32590          * @event click
32591          * The raw click event for the entire grid.
32592          * @param {Roo.bootstrap.NavProgressItem} this
32593          * @param {Roo.EventObject} e
32594          */
32595         "click" : true
32596     });
32597    
32598 };
32599
32600 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32601     
32602     rid : '',
32603     active : false,
32604     disabled : false,
32605     html : '',
32606     position : 'bottom',
32607     icon : false,
32608     
32609     getAutoCreate : function()
32610     {
32611         var iconCls = 'roo-navigation-bar-item-icon';
32612         
32613         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32614         
32615         var cfg = {
32616             tag: 'li',
32617             cls: 'roo-navigation-bar-item',
32618             cn : [
32619                 {
32620                     tag : 'i',
32621                     cls : iconCls
32622                 }
32623             ]
32624         };
32625         
32626         if(this.active){
32627             cfg.cls += ' active';
32628         }
32629         if(this.disabled){
32630             cfg.cls += ' disabled';
32631         }
32632         
32633         return cfg;
32634     },
32635     
32636     disable : function()
32637     {
32638         this.setDisabled(true);
32639     },
32640     
32641     enable : function()
32642     {
32643         this.setDisabled(false);
32644     },
32645     
32646     initEvents: function() 
32647     {
32648         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32649         
32650         this.iconEl.on('click', this.onClick, this);
32651     },
32652     
32653     onClick : function(e)
32654     {
32655         e.preventDefault();
32656         
32657         if(this.disabled){
32658             return;
32659         }
32660         
32661         if(this.fireEvent('click', this, e) === false){
32662             return;
32663         };
32664         
32665         this.parent().setActiveItem(this);
32666     },
32667     
32668     isActive: function () 
32669     {
32670         return this.active;
32671     },
32672     
32673     setActive : function(state)
32674     {
32675         if(this.active == state){
32676             return;
32677         }
32678         
32679         this.active = state;
32680         
32681         if (state) {
32682             this.el.addClass('active');
32683             return;
32684         }
32685         
32686         this.el.removeClass('active');
32687         
32688         return;
32689     },
32690     
32691     setDisabled : function(state)
32692     {
32693         if(this.disabled == state){
32694             return;
32695         }
32696         
32697         this.disabled = state;
32698         
32699         if (state) {
32700             this.el.addClass('disabled');
32701             return;
32702         }
32703         
32704         this.el.removeClass('disabled');
32705     },
32706     
32707     tooltipEl : function()
32708     {
32709         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32710     }
32711 });
32712  
32713
32714  /*
32715  * - LGPL
32716  *
32717  * FieldLabel
32718  * 
32719  */
32720
32721 /**
32722  * @class Roo.bootstrap.FieldLabel
32723  * @extends Roo.bootstrap.Component
32724  * Bootstrap FieldLabel class
32725  * @cfg {String} html contents of the element
32726  * @cfg {String} tag tag of the element default label
32727  * @cfg {String} cls class of the element
32728  * @cfg {String} target label target 
32729  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32730  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32731  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32732  * @cfg {String} iconTooltip default "This field is required"
32733  * @cfg {String} indicatorpos (left|right) default left
32734  * 
32735  * @constructor
32736  * Create a new FieldLabel
32737  * @param {Object} config The config object
32738  */
32739
32740 Roo.bootstrap.FieldLabel = function(config){
32741     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32742     
32743     this.addEvents({
32744             /**
32745              * @event invalid
32746              * Fires after the field has been marked as invalid.
32747              * @param {Roo.form.FieldLabel} this
32748              * @param {String} msg The validation message
32749              */
32750             invalid : true,
32751             /**
32752              * @event valid
32753              * Fires after the field has been validated with no errors.
32754              * @param {Roo.form.FieldLabel} this
32755              */
32756             valid : true
32757         });
32758 };
32759
32760 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32761     
32762     tag: 'label',
32763     cls: '',
32764     html: '',
32765     target: '',
32766     allowBlank : true,
32767     invalidClass : 'has-warning',
32768     validClass : 'has-success',
32769     iconTooltip : 'This field is required',
32770     indicatorpos : 'left',
32771     
32772     getAutoCreate : function(){
32773         
32774         var cls = "";
32775         if (!this.allowBlank) {
32776             cls  = "visible";
32777         }
32778         
32779         var cfg = {
32780             tag : this.tag,
32781             cls : 'roo-bootstrap-field-label ' + this.cls,
32782             for : this.target,
32783             cn : [
32784                 {
32785                     tag : 'i',
32786                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32787                     tooltip : this.iconTooltip
32788                 },
32789                 {
32790                     tag : 'span',
32791                     html : this.html
32792                 }
32793             ] 
32794         };
32795         
32796         if(this.indicatorpos == 'right'){
32797             var cfg = {
32798                 tag : this.tag,
32799                 cls : 'roo-bootstrap-field-label ' + this.cls,
32800                 for : this.target,
32801                 cn : [
32802                     {
32803                         tag : 'span',
32804                         html : this.html
32805                     },
32806                     {
32807                         tag : 'i',
32808                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32809                         tooltip : this.iconTooltip
32810                     }
32811                 ] 
32812             };
32813         }
32814         
32815         return cfg;
32816     },
32817     
32818     initEvents: function() 
32819     {
32820         Roo.bootstrap.Element.superclass.initEvents.call(this);
32821         
32822         this.indicator = this.indicatorEl();
32823         
32824         if(this.indicator){
32825             this.indicator.removeClass('visible');
32826             this.indicator.addClass('invisible');
32827         }
32828         
32829         Roo.bootstrap.FieldLabel.register(this);
32830     },
32831     
32832     indicatorEl : function()
32833     {
32834         var indicator = this.el.select('i.roo-required-indicator',true).first();
32835         
32836         if(!indicator){
32837             return false;
32838         }
32839         
32840         return indicator;
32841         
32842     },
32843     
32844     /**
32845      * Mark this field as valid
32846      */
32847     markValid : function()
32848     {
32849         if(this.indicator){
32850             this.indicator.removeClass('visible');
32851             this.indicator.addClass('invisible');
32852         }
32853         if (Roo.bootstrap.version == 3) {
32854             this.el.removeClass(this.invalidClass);
32855             this.el.addClass(this.validClass);
32856         } else {
32857             this.el.removeClass('is-invalid');
32858             this.el.addClass('is-valid');
32859         }
32860         
32861         
32862         this.fireEvent('valid', this);
32863     },
32864     
32865     /**
32866      * Mark this field as invalid
32867      * @param {String} msg The validation message
32868      */
32869     markInvalid : function(msg)
32870     {
32871         if(this.indicator){
32872             this.indicator.removeClass('invisible');
32873             this.indicator.addClass('visible');
32874         }
32875           if (Roo.bootstrap.version == 3) {
32876             this.el.removeClass(this.validClass);
32877             this.el.addClass(this.invalidClass);
32878         } else {
32879             this.el.removeClass('is-valid');
32880             this.el.addClass('is-invalid');
32881         }
32882         
32883         
32884         this.fireEvent('invalid', this, msg);
32885     }
32886     
32887    
32888 });
32889
32890 Roo.apply(Roo.bootstrap.FieldLabel, {
32891     
32892     groups: {},
32893     
32894      /**
32895     * register a FieldLabel Group
32896     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32897     */
32898     register : function(label)
32899     {
32900         if(this.groups.hasOwnProperty(label.target)){
32901             return;
32902         }
32903      
32904         this.groups[label.target] = label;
32905         
32906     },
32907     /**
32908     * fetch a FieldLabel Group based on the target
32909     * @param {string} target
32910     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32911     */
32912     get: function(target) {
32913         if (typeof(this.groups[target]) == 'undefined') {
32914             return false;
32915         }
32916         
32917         return this.groups[target] ;
32918     }
32919 });
32920
32921  
32922
32923  /*
32924  * - LGPL
32925  *
32926  * page DateSplitField.
32927  * 
32928  */
32929
32930
32931 /**
32932  * @class Roo.bootstrap.DateSplitField
32933  * @extends Roo.bootstrap.Component
32934  * Bootstrap DateSplitField class
32935  * @cfg {string} fieldLabel - the label associated
32936  * @cfg {Number} labelWidth set the width of label (0-12)
32937  * @cfg {String} labelAlign (top|left)
32938  * @cfg {Boolean} dayAllowBlank (true|false) default false
32939  * @cfg {Boolean} monthAllowBlank (true|false) default false
32940  * @cfg {Boolean} yearAllowBlank (true|false) default false
32941  * @cfg {string} dayPlaceholder 
32942  * @cfg {string} monthPlaceholder
32943  * @cfg {string} yearPlaceholder
32944  * @cfg {string} dayFormat default 'd'
32945  * @cfg {string} monthFormat default 'm'
32946  * @cfg {string} yearFormat default 'Y'
32947  * @cfg {Number} labellg set the width of label (1-12)
32948  * @cfg {Number} labelmd set the width of label (1-12)
32949  * @cfg {Number} labelsm set the width of label (1-12)
32950  * @cfg {Number} labelxs set the width of label (1-12)
32951
32952  *     
32953  * @constructor
32954  * Create a new DateSplitField
32955  * @param {Object} config The config object
32956  */
32957
32958 Roo.bootstrap.DateSplitField = function(config){
32959     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32960     
32961     this.addEvents({
32962         // raw events
32963          /**
32964          * @event years
32965          * getting the data of years
32966          * @param {Roo.bootstrap.DateSplitField} this
32967          * @param {Object} years
32968          */
32969         "years" : true,
32970         /**
32971          * @event days
32972          * getting the data of days
32973          * @param {Roo.bootstrap.DateSplitField} this
32974          * @param {Object} days
32975          */
32976         "days" : true,
32977         /**
32978          * @event invalid
32979          * Fires after the field has been marked as invalid.
32980          * @param {Roo.form.Field} this
32981          * @param {String} msg The validation message
32982          */
32983         invalid : true,
32984        /**
32985          * @event valid
32986          * Fires after the field has been validated with no errors.
32987          * @param {Roo.form.Field} this
32988          */
32989         valid : true
32990     });
32991 };
32992
32993 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32994     
32995     fieldLabel : '',
32996     labelAlign : 'top',
32997     labelWidth : 3,
32998     dayAllowBlank : false,
32999     monthAllowBlank : false,
33000     yearAllowBlank : false,
33001     dayPlaceholder : '',
33002     monthPlaceholder : '',
33003     yearPlaceholder : '',
33004     dayFormat : 'd',
33005     monthFormat : 'm',
33006     yearFormat : 'Y',
33007     isFormField : true,
33008     labellg : 0,
33009     labelmd : 0,
33010     labelsm : 0,
33011     labelxs : 0,
33012     
33013     getAutoCreate : function()
33014     {
33015         var cfg = {
33016             tag : 'div',
33017             cls : 'row roo-date-split-field-group',
33018             cn : [
33019                 {
33020                     tag : 'input',
33021                     type : 'hidden',
33022                     cls : 'form-hidden-field roo-date-split-field-group-value',
33023                     name : this.name
33024                 }
33025             ]
33026         };
33027         
33028         var labelCls = 'col-md-12';
33029         var contentCls = 'col-md-4';
33030         
33031         if(this.fieldLabel){
33032             
33033             var label = {
33034                 tag : 'div',
33035                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33036                 cn : [
33037                     {
33038                         tag : 'label',
33039                         html : this.fieldLabel
33040                     }
33041                 ]
33042             };
33043             
33044             if(this.labelAlign == 'left'){
33045             
33046                 if(this.labelWidth > 12){
33047                     label.style = "width: " + this.labelWidth + 'px';
33048                 }
33049
33050                 if(this.labelWidth < 13 && this.labelmd == 0){
33051                     this.labelmd = this.labelWidth;
33052                 }
33053
33054                 if(this.labellg > 0){
33055                     labelCls = ' col-lg-' + this.labellg;
33056                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33057                 }
33058
33059                 if(this.labelmd > 0){
33060                     labelCls = ' col-md-' + this.labelmd;
33061                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33062                 }
33063
33064                 if(this.labelsm > 0){
33065                     labelCls = ' col-sm-' + this.labelsm;
33066                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33067                 }
33068
33069                 if(this.labelxs > 0){
33070                     labelCls = ' col-xs-' + this.labelxs;
33071                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33072                 }
33073             }
33074             
33075             label.cls += ' ' + labelCls;
33076             
33077             cfg.cn.push(label);
33078         }
33079         
33080         Roo.each(['day', 'month', 'year'], function(t){
33081             cfg.cn.push({
33082                 tag : 'div',
33083                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33084             });
33085         }, this);
33086         
33087         return cfg;
33088     },
33089     
33090     inputEl: function ()
33091     {
33092         return this.el.select('.roo-date-split-field-group-value', true).first();
33093     },
33094     
33095     onRender : function(ct, position) 
33096     {
33097         var _this = this;
33098         
33099         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33100         
33101         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33102         
33103         this.dayField = new Roo.bootstrap.ComboBox({
33104             allowBlank : this.dayAllowBlank,
33105             alwaysQuery : true,
33106             displayField : 'value',
33107             editable : false,
33108             fieldLabel : '',
33109             forceSelection : true,
33110             mode : 'local',
33111             placeholder : this.dayPlaceholder,
33112             selectOnFocus : true,
33113             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33114             triggerAction : 'all',
33115             typeAhead : true,
33116             valueField : 'value',
33117             store : new Roo.data.SimpleStore({
33118                 data : (function() {    
33119                     var days = [];
33120                     _this.fireEvent('days', _this, days);
33121                     return days;
33122                 })(),
33123                 fields : [ 'value' ]
33124             }),
33125             listeners : {
33126                 select : function (_self, record, index)
33127                 {
33128                     _this.setValue(_this.getValue());
33129                 }
33130             }
33131         });
33132
33133         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33134         
33135         this.monthField = new Roo.bootstrap.MonthField({
33136             after : '<i class=\"fa fa-calendar\"></i>',
33137             allowBlank : this.monthAllowBlank,
33138             placeholder : this.monthPlaceholder,
33139             readOnly : true,
33140             listeners : {
33141                 render : function (_self)
33142                 {
33143                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33144                         e.preventDefault();
33145                         _self.focus();
33146                     });
33147                 },
33148                 select : function (_self, oldvalue, newvalue)
33149                 {
33150                     _this.setValue(_this.getValue());
33151                 }
33152             }
33153         });
33154         
33155         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33156         
33157         this.yearField = new Roo.bootstrap.ComboBox({
33158             allowBlank : this.yearAllowBlank,
33159             alwaysQuery : true,
33160             displayField : 'value',
33161             editable : false,
33162             fieldLabel : '',
33163             forceSelection : true,
33164             mode : 'local',
33165             placeholder : this.yearPlaceholder,
33166             selectOnFocus : true,
33167             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33168             triggerAction : 'all',
33169             typeAhead : true,
33170             valueField : 'value',
33171             store : new Roo.data.SimpleStore({
33172                 data : (function() {
33173                     var years = [];
33174                     _this.fireEvent('years', _this, years);
33175                     return years;
33176                 })(),
33177                 fields : [ 'value' ]
33178             }),
33179             listeners : {
33180                 select : function (_self, record, index)
33181                 {
33182                     _this.setValue(_this.getValue());
33183                 }
33184             }
33185         });
33186
33187         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33188     },
33189     
33190     setValue : function(v, format)
33191     {
33192         this.inputEl.dom.value = v;
33193         
33194         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33195         
33196         var d = Date.parseDate(v, f);
33197         
33198         if(!d){
33199             this.validate();
33200             return;
33201         }
33202         
33203         this.setDay(d.format(this.dayFormat));
33204         this.setMonth(d.format(this.monthFormat));
33205         this.setYear(d.format(this.yearFormat));
33206         
33207         this.validate();
33208         
33209         return;
33210     },
33211     
33212     setDay : function(v)
33213     {
33214         this.dayField.setValue(v);
33215         this.inputEl.dom.value = this.getValue();
33216         this.validate();
33217         return;
33218     },
33219     
33220     setMonth : function(v)
33221     {
33222         this.monthField.setValue(v, true);
33223         this.inputEl.dom.value = this.getValue();
33224         this.validate();
33225         return;
33226     },
33227     
33228     setYear : function(v)
33229     {
33230         this.yearField.setValue(v);
33231         this.inputEl.dom.value = this.getValue();
33232         this.validate();
33233         return;
33234     },
33235     
33236     getDay : function()
33237     {
33238         return this.dayField.getValue();
33239     },
33240     
33241     getMonth : function()
33242     {
33243         return this.monthField.getValue();
33244     },
33245     
33246     getYear : function()
33247     {
33248         return this.yearField.getValue();
33249     },
33250     
33251     getValue : function()
33252     {
33253         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33254         
33255         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33256         
33257         return date;
33258     },
33259     
33260     reset : function()
33261     {
33262         this.setDay('');
33263         this.setMonth('');
33264         this.setYear('');
33265         this.inputEl.dom.value = '';
33266         this.validate();
33267         return;
33268     },
33269     
33270     validate : function()
33271     {
33272         var d = this.dayField.validate();
33273         var m = this.monthField.validate();
33274         var y = this.yearField.validate();
33275         
33276         var valid = true;
33277         
33278         if(
33279                 (!this.dayAllowBlank && !d) ||
33280                 (!this.monthAllowBlank && !m) ||
33281                 (!this.yearAllowBlank && !y)
33282         ){
33283             valid = false;
33284         }
33285         
33286         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33287             return valid;
33288         }
33289         
33290         if(valid){
33291             this.markValid();
33292             return valid;
33293         }
33294         
33295         this.markInvalid();
33296         
33297         return valid;
33298     },
33299     
33300     markValid : function()
33301     {
33302         
33303         var label = this.el.select('label', true).first();
33304         var icon = this.el.select('i.fa-star', true).first();
33305
33306         if(label && icon){
33307             icon.remove();
33308         }
33309         
33310         this.fireEvent('valid', this);
33311     },
33312     
33313      /**
33314      * Mark this field as invalid
33315      * @param {String} msg The validation message
33316      */
33317     markInvalid : function(msg)
33318     {
33319         
33320         var label = this.el.select('label', true).first();
33321         var icon = this.el.select('i.fa-star', true).first();
33322
33323         if(label && !icon){
33324             this.el.select('.roo-date-split-field-label', true).createChild({
33325                 tag : 'i',
33326                 cls : 'text-danger fa fa-lg fa-star',
33327                 tooltip : 'This field is required',
33328                 style : 'margin-right:5px;'
33329             }, label, true);
33330         }
33331         
33332         this.fireEvent('invalid', this, msg);
33333     },
33334     
33335     clearInvalid : function()
33336     {
33337         var label = this.el.select('label', true).first();
33338         var icon = this.el.select('i.fa-star', true).first();
33339
33340         if(label && icon){
33341             icon.remove();
33342         }
33343         
33344         this.fireEvent('valid', this);
33345     },
33346     
33347     getName: function()
33348     {
33349         return this.name;
33350     }
33351     
33352 });
33353
33354  /**
33355  *
33356  * This is based on 
33357  * http://masonry.desandro.com
33358  *
33359  * The idea is to render all the bricks based on vertical width...
33360  *
33361  * The original code extends 'outlayer' - we might need to use that....
33362  * 
33363  */
33364
33365
33366 /**
33367  * @class Roo.bootstrap.LayoutMasonry
33368  * @extends Roo.bootstrap.Component
33369  * Bootstrap Layout Masonry class
33370  * 
33371  * @constructor
33372  * Create a new Element
33373  * @param {Object} config The config object
33374  */
33375
33376 Roo.bootstrap.LayoutMasonry = function(config){
33377     
33378     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33379     
33380     this.bricks = [];
33381     
33382     Roo.bootstrap.LayoutMasonry.register(this);
33383     
33384     this.addEvents({
33385         // raw events
33386         /**
33387          * @event layout
33388          * Fire after layout the items
33389          * @param {Roo.bootstrap.LayoutMasonry} this
33390          * @param {Roo.EventObject} e
33391          */
33392         "layout" : true
33393     });
33394     
33395 };
33396
33397 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33398     
33399     /**
33400      * @cfg {Boolean} isLayoutInstant = no animation?
33401      */   
33402     isLayoutInstant : false, // needed?
33403    
33404     /**
33405      * @cfg {Number} boxWidth  width of the columns
33406      */   
33407     boxWidth : 450,
33408     
33409       /**
33410      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33411      */   
33412     boxHeight : 0,
33413     
33414     /**
33415      * @cfg {Number} padWidth padding below box..
33416      */   
33417     padWidth : 10, 
33418     
33419     /**
33420      * @cfg {Number} gutter gutter width..
33421      */   
33422     gutter : 10,
33423     
33424      /**
33425      * @cfg {Number} maxCols maximum number of columns
33426      */   
33427     
33428     maxCols: 0,
33429     
33430     /**
33431      * @cfg {Boolean} isAutoInitial defalut true
33432      */   
33433     isAutoInitial : true, 
33434     
33435     containerWidth: 0,
33436     
33437     /**
33438      * @cfg {Boolean} isHorizontal defalut false
33439      */   
33440     isHorizontal : false, 
33441
33442     currentSize : null,
33443     
33444     tag: 'div',
33445     
33446     cls: '',
33447     
33448     bricks: null, //CompositeElement
33449     
33450     cols : 1,
33451     
33452     _isLayoutInited : false,
33453     
33454 //    isAlternative : false, // only use for vertical layout...
33455     
33456     /**
33457      * @cfg {Number} alternativePadWidth padding below box..
33458      */   
33459     alternativePadWidth : 50,
33460     
33461     selectedBrick : [],
33462     
33463     getAutoCreate : function(){
33464         
33465         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33466         
33467         var cfg = {
33468             tag: this.tag,
33469             cls: 'blog-masonary-wrapper ' + this.cls,
33470             cn : {
33471                 cls : 'mas-boxes masonary'
33472             }
33473         };
33474         
33475         return cfg;
33476     },
33477     
33478     getChildContainer: function( )
33479     {
33480         if (this.boxesEl) {
33481             return this.boxesEl;
33482         }
33483         
33484         this.boxesEl = this.el.select('.mas-boxes').first();
33485         
33486         return this.boxesEl;
33487     },
33488     
33489     
33490     initEvents : function()
33491     {
33492         var _this = this;
33493         
33494         if(this.isAutoInitial){
33495             Roo.log('hook children rendered');
33496             this.on('childrenrendered', function() {
33497                 Roo.log('children rendered');
33498                 _this.initial();
33499             } ,this);
33500         }
33501     },
33502     
33503     initial : function()
33504     {
33505         this.selectedBrick = [];
33506         
33507         this.currentSize = this.el.getBox(true);
33508         
33509         Roo.EventManager.onWindowResize(this.resize, this); 
33510
33511         if(!this.isAutoInitial){
33512             this.layout();
33513             return;
33514         }
33515         
33516         this.layout();
33517         
33518         return;
33519         //this.layout.defer(500,this);
33520         
33521     },
33522     
33523     resize : function()
33524     {
33525         var cs = this.el.getBox(true);
33526         
33527         if (
33528                 this.currentSize.width == cs.width && 
33529                 this.currentSize.x == cs.x && 
33530                 this.currentSize.height == cs.height && 
33531                 this.currentSize.y == cs.y 
33532         ) {
33533             Roo.log("no change in with or X or Y");
33534             return;
33535         }
33536         
33537         this.currentSize = cs;
33538         
33539         this.layout();
33540         
33541     },
33542     
33543     layout : function()
33544     {   
33545         this._resetLayout();
33546         
33547         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33548         
33549         this.layoutItems( isInstant );
33550       
33551         this._isLayoutInited = true;
33552         
33553         this.fireEvent('layout', this);
33554         
33555     },
33556     
33557     _resetLayout : function()
33558     {
33559         if(this.isHorizontal){
33560             this.horizontalMeasureColumns();
33561             return;
33562         }
33563         
33564         this.verticalMeasureColumns();
33565         
33566     },
33567     
33568     verticalMeasureColumns : function()
33569     {
33570         this.getContainerWidth();
33571         
33572 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33573 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33574 //            return;
33575 //        }
33576         
33577         var boxWidth = this.boxWidth + this.padWidth;
33578         
33579         if(this.containerWidth < this.boxWidth){
33580             boxWidth = this.containerWidth
33581         }
33582         
33583         var containerWidth = this.containerWidth;
33584         
33585         var cols = Math.floor(containerWidth / boxWidth);
33586         
33587         this.cols = Math.max( cols, 1 );
33588         
33589         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33590         
33591         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33592         
33593         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33594         
33595         this.colWidth = boxWidth + avail - this.padWidth;
33596         
33597         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33598         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33599     },
33600     
33601     horizontalMeasureColumns : function()
33602     {
33603         this.getContainerWidth();
33604         
33605         var boxWidth = this.boxWidth;
33606         
33607         if(this.containerWidth < boxWidth){
33608             boxWidth = this.containerWidth;
33609         }
33610         
33611         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33612         
33613         this.el.setHeight(boxWidth);
33614         
33615     },
33616     
33617     getContainerWidth : function()
33618     {
33619         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33620     },
33621     
33622     layoutItems : function( isInstant )
33623     {
33624         Roo.log(this.bricks);
33625         
33626         var items = Roo.apply([], this.bricks);
33627         
33628         if(this.isHorizontal){
33629             this._horizontalLayoutItems( items , isInstant );
33630             return;
33631         }
33632         
33633 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33634 //            this._verticalAlternativeLayoutItems( items , isInstant );
33635 //            return;
33636 //        }
33637         
33638         this._verticalLayoutItems( items , isInstant );
33639         
33640     },
33641     
33642     _verticalLayoutItems : function ( items , isInstant)
33643     {
33644         if ( !items || !items.length ) {
33645             return;
33646         }
33647         
33648         var standard = [
33649             ['xs', 'xs', 'xs', 'tall'],
33650             ['xs', 'xs', 'tall'],
33651             ['xs', 'xs', 'sm'],
33652             ['xs', 'xs', 'xs'],
33653             ['xs', 'tall'],
33654             ['xs', 'sm'],
33655             ['xs', 'xs'],
33656             ['xs'],
33657             
33658             ['sm', 'xs', 'xs'],
33659             ['sm', 'xs'],
33660             ['sm'],
33661             
33662             ['tall', 'xs', 'xs', 'xs'],
33663             ['tall', 'xs', 'xs'],
33664             ['tall', 'xs'],
33665             ['tall']
33666             
33667         ];
33668         
33669         var queue = [];
33670         
33671         var boxes = [];
33672         
33673         var box = [];
33674         
33675         Roo.each(items, function(item, k){
33676             
33677             switch (item.size) {
33678                 // these layouts take up a full box,
33679                 case 'md' :
33680                 case 'md-left' :
33681                 case 'md-right' :
33682                 case 'wide' :
33683                     
33684                     if(box.length){
33685                         boxes.push(box);
33686                         box = [];
33687                     }
33688                     
33689                     boxes.push([item]);
33690                     
33691                     break;
33692                     
33693                 case 'xs' :
33694                 case 'sm' :
33695                 case 'tall' :
33696                     
33697                     box.push(item);
33698                     
33699                     break;
33700                 default :
33701                     break;
33702                     
33703             }
33704             
33705         }, this);
33706         
33707         if(box.length){
33708             boxes.push(box);
33709             box = [];
33710         }
33711         
33712         var filterPattern = function(box, length)
33713         {
33714             if(!box.length){
33715                 return;
33716             }
33717             
33718             var match = false;
33719             
33720             var pattern = box.slice(0, length);
33721             
33722             var format = [];
33723             
33724             Roo.each(pattern, function(i){
33725                 format.push(i.size);
33726             }, this);
33727             
33728             Roo.each(standard, function(s){
33729                 
33730                 if(String(s) != String(format)){
33731                     return;
33732                 }
33733                 
33734                 match = true;
33735                 return false;
33736                 
33737             }, this);
33738             
33739             if(!match && length == 1){
33740                 return;
33741             }
33742             
33743             if(!match){
33744                 filterPattern(box, length - 1);
33745                 return;
33746             }
33747                 
33748             queue.push(pattern);
33749
33750             box = box.slice(length, box.length);
33751
33752             filterPattern(box, 4);
33753
33754             return;
33755             
33756         }
33757         
33758         Roo.each(boxes, function(box, k){
33759             
33760             if(!box.length){
33761                 return;
33762             }
33763             
33764             if(box.length == 1){
33765                 queue.push(box);
33766                 return;
33767             }
33768             
33769             filterPattern(box, 4);
33770             
33771         }, this);
33772         
33773         this._processVerticalLayoutQueue( queue, isInstant );
33774         
33775     },
33776     
33777 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33778 //    {
33779 //        if ( !items || !items.length ) {
33780 //            return;
33781 //        }
33782 //
33783 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33784 //        
33785 //    },
33786     
33787     _horizontalLayoutItems : function ( items , isInstant)
33788     {
33789         if ( !items || !items.length || items.length < 3) {
33790             return;
33791         }
33792         
33793         items.reverse();
33794         
33795         var eItems = items.slice(0, 3);
33796         
33797         items = items.slice(3, items.length);
33798         
33799         var standard = [
33800             ['xs', 'xs', 'xs', 'wide'],
33801             ['xs', 'xs', 'wide'],
33802             ['xs', 'xs', 'sm'],
33803             ['xs', 'xs', 'xs'],
33804             ['xs', 'wide'],
33805             ['xs', 'sm'],
33806             ['xs', 'xs'],
33807             ['xs'],
33808             
33809             ['sm', 'xs', 'xs'],
33810             ['sm', 'xs'],
33811             ['sm'],
33812             
33813             ['wide', 'xs', 'xs', 'xs'],
33814             ['wide', 'xs', 'xs'],
33815             ['wide', 'xs'],
33816             ['wide'],
33817             
33818             ['wide-thin']
33819         ];
33820         
33821         var queue = [];
33822         
33823         var boxes = [];
33824         
33825         var box = [];
33826         
33827         Roo.each(items, function(item, k){
33828             
33829             switch (item.size) {
33830                 case 'md' :
33831                 case 'md-left' :
33832                 case 'md-right' :
33833                 case 'tall' :
33834                     
33835                     if(box.length){
33836                         boxes.push(box);
33837                         box = [];
33838                     }
33839                     
33840                     boxes.push([item]);
33841                     
33842                     break;
33843                     
33844                 case 'xs' :
33845                 case 'sm' :
33846                 case 'wide' :
33847                 case 'wide-thin' :
33848                     
33849                     box.push(item);
33850                     
33851                     break;
33852                 default :
33853                     break;
33854                     
33855             }
33856             
33857         }, this);
33858         
33859         if(box.length){
33860             boxes.push(box);
33861             box = [];
33862         }
33863         
33864         var filterPattern = function(box, length)
33865         {
33866             if(!box.length){
33867                 return;
33868             }
33869             
33870             var match = false;
33871             
33872             var pattern = box.slice(0, length);
33873             
33874             var format = [];
33875             
33876             Roo.each(pattern, function(i){
33877                 format.push(i.size);
33878             }, this);
33879             
33880             Roo.each(standard, function(s){
33881                 
33882                 if(String(s) != String(format)){
33883                     return;
33884                 }
33885                 
33886                 match = true;
33887                 return false;
33888                 
33889             }, this);
33890             
33891             if(!match && length == 1){
33892                 return;
33893             }
33894             
33895             if(!match){
33896                 filterPattern(box, length - 1);
33897                 return;
33898             }
33899                 
33900             queue.push(pattern);
33901
33902             box = box.slice(length, box.length);
33903
33904             filterPattern(box, 4);
33905
33906             return;
33907             
33908         }
33909         
33910         Roo.each(boxes, function(box, k){
33911             
33912             if(!box.length){
33913                 return;
33914             }
33915             
33916             if(box.length == 1){
33917                 queue.push(box);
33918                 return;
33919             }
33920             
33921             filterPattern(box, 4);
33922             
33923         }, this);
33924         
33925         
33926         var prune = [];
33927         
33928         var pos = this.el.getBox(true);
33929         
33930         var minX = pos.x;
33931         
33932         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33933         
33934         var hit_end = false;
33935         
33936         Roo.each(queue, function(box){
33937             
33938             if(hit_end){
33939                 
33940                 Roo.each(box, function(b){
33941                 
33942                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33943                     b.el.hide();
33944
33945                 }, this);
33946
33947                 return;
33948             }
33949             
33950             var mx = 0;
33951             
33952             Roo.each(box, function(b){
33953                 
33954                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33955                 b.el.show();
33956
33957                 mx = Math.max(mx, b.x);
33958                 
33959             }, this);
33960             
33961             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33962             
33963             if(maxX < minX){
33964                 
33965                 Roo.each(box, function(b){
33966                 
33967                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33968                     b.el.hide();
33969                     
33970                 }, this);
33971                 
33972                 hit_end = true;
33973                 
33974                 return;
33975             }
33976             
33977             prune.push(box);
33978             
33979         }, this);
33980         
33981         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33982     },
33983     
33984     /** Sets position of item in DOM
33985     * @param {Element} item
33986     * @param {Number} x - horizontal position
33987     * @param {Number} y - vertical position
33988     * @param {Boolean} isInstant - disables transitions
33989     */
33990     _processVerticalLayoutQueue : function( queue, isInstant )
33991     {
33992         var pos = this.el.getBox(true);
33993         var x = pos.x;
33994         var y = pos.y;
33995         var maxY = [];
33996         
33997         for (var i = 0; i < this.cols; i++){
33998             maxY[i] = pos.y;
33999         }
34000         
34001         Roo.each(queue, function(box, k){
34002             
34003             var col = k % this.cols;
34004             
34005             Roo.each(box, function(b,kk){
34006                 
34007                 b.el.position('absolute');
34008                 
34009                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34010                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34011                 
34012                 if(b.size == 'md-left' || b.size == 'md-right'){
34013                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34014                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34015                 }
34016                 
34017                 b.el.setWidth(width);
34018                 b.el.setHeight(height);
34019                 // iframe?
34020                 b.el.select('iframe',true).setSize(width,height);
34021                 
34022             }, this);
34023             
34024             for (var i = 0; i < this.cols; i++){
34025                 
34026                 if(maxY[i] < maxY[col]){
34027                     col = i;
34028                     continue;
34029                 }
34030                 
34031                 col = Math.min(col, i);
34032                 
34033             }
34034             
34035             x = pos.x + col * (this.colWidth + this.padWidth);
34036             
34037             y = maxY[col];
34038             
34039             var positions = [];
34040             
34041             switch (box.length){
34042                 case 1 :
34043                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34044                     break;
34045                 case 2 :
34046                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34047                     break;
34048                 case 3 :
34049                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34050                     break;
34051                 case 4 :
34052                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34053                     break;
34054                 default :
34055                     break;
34056             }
34057             
34058             Roo.each(box, function(b,kk){
34059                 
34060                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34061                 
34062                 var sz = b.el.getSize();
34063                 
34064                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34065                 
34066             }, this);
34067             
34068         }, this);
34069         
34070         var mY = 0;
34071         
34072         for (var i = 0; i < this.cols; i++){
34073             mY = Math.max(mY, maxY[i]);
34074         }
34075         
34076         this.el.setHeight(mY - pos.y);
34077         
34078     },
34079     
34080 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34081 //    {
34082 //        var pos = this.el.getBox(true);
34083 //        var x = pos.x;
34084 //        var y = pos.y;
34085 //        var maxX = pos.right;
34086 //        
34087 //        var maxHeight = 0;
34088 //        
34089 //        Roo.each(items, function(item, k){
34090 //            
34091 //            var c = k % 2;
34092 //            
34093 //            item.el.position('absolute');
34094 //                
34095 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34096 //
34097 //            item.el.setWidth(width);
34098 //
34099 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34100 //
34101 //            item.el.setHeight(height);
34102 //            
34103 //            if(c == 0){
34104 //                item.el.setXY([x, y], isInstant ? false : true);
34105 //            } else {
34106 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34107 //            }
34108 //            
34109 //            y = y + height + this.alternativePadWidth;
34110 //            
34111 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34112 //            
34113 //        }, this);
34114 //        
34115 //        this.el.setHeight(maxHeight);
34116 //        
34117 //    },
34118     
34119     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34120     {
34121         var pos = this.el.getBox(true);
34122         
34123         var minX = pos.x;
34124         var minY = pos.y;
34125         
34126         var maxX = pos.right;
34127         
34128         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34129         
34130         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34131         
34132         Roo.each(queue, function(box, k){
34133             
34134             Roo.each(box, function(b, kk){
34135                 
34136                 b.el.position('absolute');
34137                 
34138                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34139                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34140                 
34141                 if(b.size == 'md-left' || b.size == 'md-right'){
34142                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34143                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34144                 }
34145                 
34146                 b.el.setWidth(width);
34147                 b.el.setHeight(height);
34148                 
34149             }, this);
34150             
34151             if(!box.length){
34152                 return;
34153             }
34154             
34155             var positions = [];
34156             
34157             switch (box.length){
34158                 case 1 :
34159                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34160                     break;
34161                 case 2 :
34162                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34163                     break;
34164                 case 3 :
34165                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34166                     break;
34167                 case 4 :
34168                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34169                     break;
34170                 default :
34171                     break;
34172             }
34173             
34174             Roo.each(box, function(b,kk){
34175                 
34176                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34177                 
34178                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34179                 
34180             }, this);
34181             
34182         }, this);
34183         
34184     },
34185     
34186     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34187     {
34188         Roo.each(eItems, function(b,k){
34189             
34190             b.size = (k == 0) ? 'sm' : 'xs';
34191             b.x = (k == 0) ? 2 : 1;
34192             b.y = (k == 0) ? 2 : 1;
34193             
34194             b.el.position('absolute');
34195             
34196             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34197                 
34198             b.el.setWidth(width);
34199             
34200             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34201             
34202             b.el.setHeight(height);
34203             
34204         }, this);
34205
34206         var positions = [];
34207         
34208         positions.push({
34209             x : maxX - this.unitWidth * 2 - this.gutter,
34210             y : minY
34211         });
34212         
34213         positions.push({
34214             x : maxX - this.unitWidth,
34215             y : minY + (this.unitWidth + this.gutter) * 2
34216         });
34217         
34218         positions.push({
34219             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34220             y : minY
34221         });
34222         
34223         Roo.each(eItems, function(b,k){
34224             
34225             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34226
34227         }, this);
34228         
34229     },
34230     
34231     getVerticalOneBoxColPositions : function(x, y, box)
34232     {
34233         var pos = [];
34234         
34235         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34236         
34237         if(box[0].size == 'md-left'){
34238             rand = 0;
34239         }
34240         
34241         if(box[0].size == 'md-right'){
34242             rand = 1;
34243         }
34244         
34245         pos.push({
34246             x : x + (this.unitWidth + this.gutter) * rand,
34247             y : y
34248         });
34249         
34250         return pos;
34251     },
34252     
34253     getVerticalTwoBoxColPositions : function(x, y, box)
34254     {
34255         var pos = [];
34256         
34257         if(box[0].size == 'xs'){
34258             
34259             pos.push({
34260                 x : x,
34261                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34262             });
34263
34264             pos.push({
34265                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34266                 y : y
34267             });
34268             
34269             return pos;
34270             
34271         }
34272         
34273         pos.push({
34274             x : x,
34275             y : y
34276         });
34277
34278         pos.push({
34279             x : x + (this.unitWidth + this.gutter) * 2,
34280             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34281         });
34282         
34283         return pos;
34284         
34285     },
34286     
34287     getVerticalThreeBoxColPositions : function(x, y, box)
34288     {
34289         var pos = [];
34290         
34291         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34292             
34293             pos.push({
34294                 x : x,
34295                 y : y
34296             });
34297
34298             pos.push({
34299                 x : x + (this.unitWidth + this.gutter) * 1,
34300                 y : y
34301             });
34302             
34303             pos.push({
34304                 x : x + (this.unitWidth + this.gutter) * 2,
34305                 y : y
34306             });
34307             
34308             return pos;
34309             
34310         }
34311         
34312         if(box[0].size == 'xs' && box[1].size == 'xs'){
34313             
34314             pos.push({
34315                 x : x,
34316                 y : y
34317             });
34318
34319             pos.push({
34320                 x : x,
34321                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34322             });
34323             
34324             pos.push({
34325                 x : x + (this.unitWidth + this.gutter) * 1,
34326                 y : y
34327             });
34328             
34329             return pos;
34330             
34331         }
34332         
34333         pos.push({
34334             x : x,
34335             y : y
34336         });
34337
34338         pos.push({
34339             x : x + (this.unitWidth + this.gutter) * 2,
34340             y : y
34341         });
34342
34343         pos.push({
34344             x : x + (this.unitWidth + this.gutter) * 2,
34345             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34346         });
34347             
34348         return pos;
34349         
34350     },
34351     
34352     getVerticalFourBoxColPositions : function(x, y, box)
34353     {
34354         var pos = [];
34355         
34356         if(box[0].size == 'xs'){
34357             
34358             pos.push({
34359                 x : x,
34360                 y : y
34361             });
34362
34363             pos.push({
34364                 x : x,
34365                 y : y + (this.unitHeight + this.gutter) * 1
34366             });
34367             
34368             pos.push({
34369                 x : x,
34370                 y : y + (this.unitHeight + this.gutter) * 2
34371             });
34372             
34373             pos.push({
34374                 x : x + (this.unitWidth + this.gutter) * 1,
34375                 y : y
34376             });
34377             
34378             return pos;
34379             
34380         }
34381         
34382         pos.push({
34383             x : x,
34384             y : y
34385         });
34386
34387         pos.push({
34388             x : x + (this.unitWidth + this.gutter) * 2,
34389             y : y
34390         });
34391
34392         pos.push({
34393             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34394             y : y + (this.unitHeight + this.gutter) * 1
34395         });
34396
34397         pos.push({
34398             x : x + (this.unitWidth + this.gutter) * 2,
34399             y : y + (this.unitWidth + this.gutter) * 2
34400         });
34401
34402         return pos;
34403         
34404     },
34405     
34406     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34407     {
34408         var pos = [];
34409         
34410         if(box[0].size == 'md-left'){
34411             pos.push({
34412                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34413                 y : minY
34414             });
34415             
34416             return pos;
34417         }
34418         
34419         if(box[0].size == 'md-right'){
34420             pos.push({
34421                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34422                 y : minY + (this.unitWidth + this.gutter) * 1
34423             });
34424             
34425             return pos;
34426         }
34427         
34428         var rand = Math.floor(Math.random() * (4 - box[0].y));
34429         
34430         pos.push({
34431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34432             y : minY + (this.unitWidth + this.gutter) * rand
34433         });
34434         
34435         return pos;
34436         
34437     },
34438     
34439     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34440     {
34441         var pos = [];
34442         
34443         if(box[0].size == 'xs'){
34444             
34445             pos.push({
34446                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34447                 y : minY
34448             });
34449
34450             pos.push({
34451                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34452                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34453             });
34454             
34455             return pos;
34456             
34457         }
34458         
34459         pos.push({
34460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34461             y : minY
34462         });
34463
34464         pos.push({
34465             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34466             y : minY + (this.unitWidth + this.gutter) * 2
34467         });
34468         
34469         return pos;
34470         
34471     },
34472     
34473     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34474     {
34475         var pos = [];
34476         
34477         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34478             
34479             pos.push({
34480                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34481                 y : minY
34482             });
34483
34484             pos.push({
34485                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34486                 y : minY + (this.unitWidth + this.gutter) * 1
34487             });
34488             
34489             pos.push({
34490                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34491                 y : minY + (this.unitWidth + this.gutter) * 2
34492             });
34493             
34494             return pos;
34495             
34496         }
34497         
34498         if(box[0].size == 'xs' && box[1].size == 'xs'){
34499             
34500             pos.push({
34501                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34502                 y : minY
34503             });
34504
34505             pos.push({
34506                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34507                 y : minY
34508             });
34509             
34510             pos.push({
34511                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34512                 y : minY + (this.unitWidth + this.gutter) * 1
34513             });
34514             
34515             return pos;
34516             
34517         }
34518         
34519         pos.push({
34520             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34521             y : minY
34522         });
34523
34524         pos.push({
34525             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34526             y : minY + (this.unitWidth + this.gutter) * 2
34527         });
34528
34529         pos.push({
34530             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34531             y : minY + (this.unitWidth + this.gutter) * 2
34532         });
34533             
34534         return pos;
34535         
34536     },
34537     
34538     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34539     {
34540         var pos = [];
34541         
34542         if(box[0].size == 'xs'){
34543             
34544             pos.push({
34545                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34546                 y : minY
34547             });
34548
34549             pos.push({
34550                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34551                 y : minY
34552             });
34553             
34554             pos.push({
34555                 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),
34556                 y : minY
34557             });
34558             
34559             pos.push({
34560                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34561                 y : minY + (this.unitWidth + this.gutter) * 1
34562             });
34563             
34564             return pos;
34565             
34566         }
34567         
34568         pos.push({
34569             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34570             y : minY
34571         });
34572         
34573         pos.push({
34574             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34575             y : minY + (this.unitWidth + this.gutter) * 2
34576         });
34577         
34578         pos.push({
34579             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34580             y : minY + (this.unitWidth + this.gutter) * 2
34581         });
34582         
34583         pos.push({
34584             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),
34585             y : minY + (this.unitWidth + this.gutter) * 2
34586         });
34587
34588         return pos;
34589         
34590     },
34591     
34592     /**
34593     * remove a Masonry Brick
34594     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34595     */
34596     removeBrick : function(brick_id)
34597     {
34598         if (!brick_id) {
34599             return;
34600         }
34601         
34602         for (var i = 0; i<this.bricks.length; i++) {
34603             if (this.bricks[i].id == brick_id) {
34604                 this.bricks.splice(i,1);
34605                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34606                 this.initial();
34607             }
34608         }
34609     },
34610     
34611     /**
34612     * adds a Masonry Brick
34613     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34614     */
34615     addBrick : function(cfg)
34616     {
34617         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34618         //this.register(cn);
34619         cn.parentId = this.id;
34620         cn.render(this.el);
34621         return cn;
34622     },
34623     
34624     /**
34625     * register a Masonry Brick
34626     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34627     */
34628     
34629     register : function(brick)
34630     {
34631         this.bricks.push(brick);
34632         brick.masonryId = this.id;
34633     },
34634     
34635     /**
34636     * clear all the Masonry Brick
34637     */
34638     clearAll : function()
34639     {
34640         this.bricks = [];
34641         //this.getChildContainer().dom.innerHTML = "";
34642         this.el.dom.innerHTML = '';
34643     },
34644     
34645     getSelected : function()
34646     {
34647         if (!this.selectedBrick) {
34648             return false;
34649         }
34650         
34651         return this.selectedBrick;
34652     }
34653 });
34654
34655 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34656     
34657     groups: {},
34658      /**
34659     * register a Masonry Layout
34660     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34661     */
34662     
34663     register : function(layout)
34664     {
34665         this.groups[layout.id] = layout;
34666     },
34667     /**
34668     * fetch a  Masonry Layout based on the masonry layout ID
34669     * @param {string} the masonry layout to add
34670     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34671     */
34672     
34673     get: function(layout_id) {
34674         if (typeof(this.groups[layout_id]) == 'undefined') {
34675             return false;
34676         }
34677         return this.groups[layout_id] ;
34678     }
34679     
34680     
34681     
34682 });
34683
34684  
34685
34686  /**
34687  *
34688  * This is based on 
34689  * http://masonry.desandro.com
34690  *
34691  * The idea is to render all the bricks based on vertical width...
34692  *
34693  * The original code extends 'outlayer' - we might need to use that....
34694  * 
34695  */
34696
34697
34698 /**
34699  * @class Roo.bootstrap.LayoutMasonryAuto
34700  * @extends Roo.bootstrap.Component
34701  * Bootstrap Layout Masonry class
34702  * 
34703  * @constructor
34704  * Create a new Element
34705  * @param {Object} config The config object
34706  */
34707
34708 Roo.bootstrap.LayoutMasonryAuto = function(config){
34709     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34710 };
34711
34712 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34713     
34714       /**
34715      * @cfg {Boolean} isFitWidth  - resize the width..
34716      */   
34717     isFitWidth : false,  // options..
34718     /**
34719      * @cfg {Boolean} isOriginLeft = left align?
34720      */   
34721     isOriginLeft : true,
34722     /**
34723      * @cfg {Boolean} isOriginTop = top align?
34724      */   
34725     isOriginTop : false,
34726     /**
34727      * @cfg {Boolean} isLayoutInstant = no animation?
34728      */   
34729     isLayoutInstant : false, // needed?
34730     /**
34731      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34732      */   
34733     isResizingContainer : true,
34734     /**
34735      * @cfg {Number} columnWidth  width of the columns 
34736      */   
34737     
34738     columnWidth : 0,
34739     
34740     /**
34741      * @cfg {Number} maxCols maximum number of columns
34742      */   
34743     
34744     maxCols: 0,
34745     /**
34746      * @cfg {Number} padHeight padding below box..
34747      */   
34748     
34749     padHeight : 10, 
34750     
34751     /**
34752      * @cfg {Boolean} isAutoInitial defalut true
34753      */   
34754     
34755     isAutoInitial : true, 
34756     
34757     // private?
34758     gutter : 0,
34759     
34760     containerWidth: 0,
34761     initialColumnWidth : 0,
34762     currentSize : null,
34763     
34764     colYs : null, // array.
34765     maxY : 0,
34766     padWidth: 10,
34767     
34768     
34769     tag: 'div',
34770     cls: '',
34771     bricks: null, //CompositeElement
34772     cols : 0, // array?
34773     // element : null, // wrapped now this.el
34774     _isLayoutInited : null, 
34775     
34776     
34777     getAutoCreate : function(){
34778         
34779         var cfg = {
34780             tag: this.tag,
34781             cls: 'blog-masonary-wrapper ' + this.cls,
34782             cn : {
34783                 cls : 'mas-boxes masonary'
34784             }
34785         };
34786         
34787         return cfg;
34788     },
34789     
34790     getChildContainer: function( )
34791     {
34792         if (this.boxesEl) {
34793             return this.boxesEl;
34794         }
34795         
34796         this.boxesEl = this.el.select('.mas-boxes').first();
34797         
34798         return this.boxesEl;
34799     },
34800     
34801     
34802     initEvents : function()
34803     {
34804         var _this = this;
34805         
34806         if(this.isAutoInitial){
34807             Roo.log('hook children rendered');
34808             this.on('childrenrendered', function() {
34809                 Roo.log('children rendered');
34810                 _this.initial();
34811             } ,this);
34812         }
34813         
34814     },
34815     
34816     initial : function()
34817     {
34818         this.reloadItems();
34819
34820         this.currentSize = this.el.getBox(true);
34821
34822         /// was window resize... - let's see if this works..
34823         Roo.EventManager.onWindowResize(this.resize, this); 
34824
34825         if(!this.isAutoInitial){
34826             this.layout();
34827             return;
34828         }
34829         
34830         this.layout.defer(500,this);
34831     },
34832     
34833     reloadItems: function()
34834     {
34835         this.bricks = this.el.select('.masonry-brick', true);
34836         
34837         this.bricks.each(function(b) {
34838             //Roo.log(b.getSize());
34839             if (!b.attr('originalwidth')) {
34840                 b.attr('originalwidth',  b.getSize().width);
34841             }
34842             
34843         });
34844         
34845         Roo.log(this.bricks.elements.length);
34846     },
34847     
34848     resize : function()
34849     {
34850         Roo.log('resize');
34851         var cs = this.el.getBox(true);
34852         
34853         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34854             Roo.log("no change in with or X");
34855             return;
34856         }
34857         this.currentSize = cs;
34858         this.layout();
34859     },
34860     
34861     layout : function()
34862     {
34863          Roo.log('layout');
34864         this._resetLayout();
34865         //this._manageStamps();
34866       
34867         // don't animate first layout
34868         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34869         this.layoutItems( isInstant );
34870       
34871         // flag for initalized
34872         this._isLayoutInited = true;
34873     },
34874     
34875     layoutItems : function( isInstant )
34876     {
34877         //var items = this._getItemsForLayout( this.items );
34878         // original code supports filtering layout items.. we just ignore it..
34879         
34880         this._layoutItems( this.bricks , isInstant );
34881       
34882         this._postLayout();
34883     },
34884     _layoutItems : function ( items , isInstant)
34885     {
34886        //this.fireEvent( 'layout', this, items );
34887     
34888
34889         if ( !items || !items.elements.length ) {
34890           // no items, emit event with empty array
34891             return;
34892         }
34893
34894         var queue = [];
34895         items.each(function(item) {
34896             Roo.log("layout item");
34897             Roo.log(item);
34898             // get x/y object from method
34899             var position = this._getItemLayoutPosition( item );
34900             // enqueue
34901             position.item = item;
34902             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34903             queue.push( position );
34904         }, this);
34905       
34906         this._processLayoutQueue( queue );
34907     },
34908     /** Sets position of item in DOM
34909     * @param {Element} item
34910     * @param {Number} x - horizontal position
34911     * @param {Number} y - vertical position
34912     * @param {Boolean} isInstant - disables transitions
34913     */
34914     _processLayoutQueue : function( queue )
34915     {
34916         for ( var i=0, len = queue.length; i < len; i++ ) {
34917             var obj = queue[i];
34918             obj.item.position('absolute');
34919             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34920         }
34921     },
34922       
34923     
34924     /**
34925     * Any logic you want to do after each layout,
34926     * i.e. size the container
34927     */
34928     _postLayout : function()
34929     {
34930         this.resizeContainer();
34931     },
34932     
34933     resizeContainer : function()
34934     {
34935         if ( !this.isResizingContainer ) {
34936             return;
34937         }
34938         var size = this._getContainerSize();
34939         if ( size ) {
34940             this.el.setSize(size.width,size.height);
34941             this.boxesEl.setSize(size.width,size.height);
34942         }
34943     },
34944     
34945     
34946     
34947     _resetLayout : function()
34948     {
34949         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34950         this.colWidth = this.el.getWidth();
34951         //this.gutter = this.el.getWidth(); 
34952         
34953         this.measureColumns();
34954
34955         // reset column Y
34956         var i = this.cols;
34957         this.colYs = [];
34958         while (i--) {
34959             this.colYs.push( 0 );
34960         }
34961     
34962         this.maxY = 0;
34963     },
34964
34965     measureColumns : function()
34966     {
34967         this.getContainerWidth();
34968       // if columnWidth is 0, default to outerWidth of first item
34969         if ( !this.columnWidth ) {
34970             var firstItem = this.bricks.first();
34971             Roo.log(firstItem);
34972             this.columnWidth  = this.containerWidth;
34973             if (firstItem && firstItem.attr('originalwidth') ) {
34974                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34975             }
34976             // columnWidth fall back to item of first element
34977             Roo.log("set column width?");
34978                         this.initialColumnWidth = this.columnWidth  ;
34979
34980             // if first elem has no width, default to size of container
34981             
34982         }
34983         
34984         
34985         if (this.initialColumnWidth) {
34986             this.columnWidth = this.initialColumnWidth;
34987         }
34988         
34989         
34990             
34991         // column width is fixed at the top - however if container width get's smaller we should
34992         // reduce it...
34993         
34994         // this bit calcs how man columns..
34995             
34996         var columnWidth = this.columnWidth += this.gutter;
34997       
34998         // calculate columns
34999         var containerWidth = this.containerWidth + this.gutter;
35000         
35001         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35002         // fix rounding errors, typically with gutters
35003         var excess = columnWidth - containerWidth % columnWidth;
35004         
35005         
35006         // if overshoot is less than a pixel, round up, otherwise floor it
35007         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35008         cols = Math[ mathMethod ]( cols );
35009         this.cols = Math.max( cols, 1 );
35010         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35011         
35012          // padding positioning..
35013         var totalColWidth = this.cols * this.columnWidth;
35014         var padavail = this.containerWidth - totalColWidth;
35015         // so for 2 columns - we need 3 'pads'
35016         
35017         var padNeeded = (1+this.cols) * this.padWidth;
35018         
35019         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35020         
35021         this.columnWidth += padExtra
35022         //this.padWidth = Math.floor(padavail /  ( this.cols));
35023         
35024         // adjust colum width so that padding is fixed??
35025         
35026         // we have 3 columns ... total = width * 3
35027         // we have X left over... that should be used by 
35028         
35029         //if (this.expandC) {
35030             
35031         //}
35032         
35033         
35034         
35035     },
35036     
35037     getContainerWidth : function()
35038     {
35039        /* // container is parent if fit width
35040         var container = this.isFitWidth ? this.element.parentNode : this.element;
35041         // check that this.size and size are there
35042         // IE8 triggers resize on body size change, so they might not be
35043         
35044         var size = getSize( container );  //FIXME
35045         this.containerWidth = size && size.innerWidth; //FIXME
35046         */
35047          
35048         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35049         
35050     },
35051     
35052     _getItemLayoutPosition : function( item )  // what is item?
35053     {
35054         // we resize the item to our columnWidth..
35055       
35056         item.setWidth(this.columnWidth);
35057         item.autoBoxAdjust  = false;
35058         
35059         var sz = item.getSize();
35060  
35061         // how many columns does this brick span
35062         var remainder = this.containerWidth % this.columnWidth;
35063         
35064         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35065         // round if off by 1 pixel, otherwise use ceil
35066         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35067         colSpan = Math.min( colSpan, this.cols );
35068         
35069         // normally this should be '1' as we dont' currently allow multi width columns..
35070         
35071         var colGroup = this._getColGroup( colSpan );
35072         // get the minimum Y value from the columns
35073         var minimumY = Math.min.apply( Math, colGroup );
35074         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35075         
35076         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35077          
35078         // position the brick
35079         var position = {
35080             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35081             y: this.currentSize.y + minimumY + this.padHeight
35082         };
35083         
35084         Roo.log(position);
35085         // apply setHeight to necessary columns
35086         var setHeight = minimumY + sz.height + this.padHeight;
35087         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35088         
35089         var setSpan = this.cols + 1 - colGroup.length;
35090         for ( var i = 0; i < setSpan; i++ ) {
35091           this.colYs[ shortColIndex + i ] = setHeight ;
35092         }
35093       
35094         return position;
35095     },
35096     
35097     /**
35098      * @param {Number} colSpan - number of columns the element spans
35099      * @returns {Array} colGroup
35100      */
35101     _getColGroup : function( colSpan )
35102     {
35103         if ( colSpan < 2 ) {
35104           // if brick spans only one column, use all the column Ys
35105           return this.colYs;
35106         }
35107       
35108         var colGroup = [];
35109         // how many different places could this brick fit horizontally
35110         var groupCount = this.cols + 1 - colSpan;
35111         // for each group potential horizontal position
35112         for ( var i = 0; i < groupCount; i++ ) {
35113           // make an array of colY values for that one group
35114           var groupColYs = this.colYs.slice( i, i + colSpan );
35115           // and get the max value of the array
35116           colGroup[i] = Math.max.apply( Math, groupColYs );
35117         }
35118         return colGroup;
35119     },
35120     /*
35121     _manageStamp : function( stamp )
35122     {
35123         var stampSize =  stamp.getSize();
35124         var offset = stamp.getBox();
35125         // get the columns that this stamp affects
35126         var firstX = this.isOriginLeft ? offset.x : offset.right;
35127         var lastX = firstX + stampSize.width;
35128         var firstCol = Math.floor( firstX / this.columnWidth );
35129         firstCol = Math.max( 0, firstCol );
35130         
35131         var lastCol = Math.floor( lastX / this.columnWidth );
35132         // lastCol should not go over if multiple of columnWidth #425
35133         lastCol -= lastX % this.columnWidth ? 0 : 1;
35134         lastCol = Math.min( this.cols - 1, lastCol );
35135         
35136         // set colYs to bottom of the stamp
35137         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35138             stampSize.height;
35139             
35140         for ( var i = firstCol; i <= lastCol; i++ ) {
35141           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35142         }
35143     },
35144     */
35145     
35146     _getContainerSize : function()
35147     {
35148         this.maxY = Math.max.apply( Math, this.colYs );
35149         var size = {
35150             height: this.maxY
35151         };
35152       
35153         if ( this.isFitWidth ) {
35154             size.width = this._getContainerFitWidth();
35155         }
35156       
35157         return size;
35158     },
35159     
35160     _getContainerFitWidth : function()
35161     {
35162         var unusedCols = 0;
35163         // count unused columns
35164         var i = this.cols;
35165         while ( --i ) {
35166           if ( this.colYs[i] !== 0 ) {
35167             break;
35168           }
35169           unusedCols++;
35170         }
35171         // fit container to columns that have been used
35172         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35173     },
35174     
35175     needsResizeLayout : function()
35176     {
35177         var previousWidth = this.containerWidth;
35178         this.getContainerWidth();
35179         return previousWidth !== this.containerWidth;
35180     }
35181  
35182 });
35183
35184  
35185
35186  /*
35187  * - LGPL
35188  *
35189  * element
35190  * 
35191  */
35192
35193 /**
35194  * @class Roo.bootstrap.MasonryBrick
35195  * @extends Roo.bootstrap.Component
35196  * Bootstrap MasonryBrick class
35197  * 
35198  * @constructor
35199  * Create a new MasonryBrick
35200  * @param {Object} config The config object
35201  */
35202
35203 Roo.bootstrap.MasonryBrick = function(config){
35204     
35205     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35206     
35207     Roo.bootstrap.MasonryBrick.register(this);
35208     
35209     this.addEvents({
35210         // raw events
35211         /**
35212          * @event click
35213          * When a MasonryBrick is clcik
35214          * @param {Roo.bootstrap.MasonryBrick} this
35215          * @param {Roo.EventObject} e
35216          */
35217         "click" : true
35218     });
35219 };
35220
35221 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35222     
35223     /**
35224      * @cfg {String} title
35225      */   
35226     title : '',
35227     /**
35228      * @cfg {String} html
35229      */   
35230     html : '',
35231     /**
35232      * @cfg {String} bgimage
35233      */   
35234     bgimage : '',
35235     /**
35236      * @cfg {String} videourl
35237      */   
35238     videourl : '',
35239     /**
35240      * @cfg {String} cls
35241      */   
35242     cls : '',
35243     /**
35244      * @cfg {String} href
35245      */   
35246     href : '',
35247     /**
35248      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35249      */   
35250     size : 'xs',
35251     
35252     /**
35253      * @cfg {String} placetitle (center|bottom)
35254      */   
35255     placetitle : '',
35256     
35257     /**
35258      * @cfg {Boolean} isFitContainer defalut true
35259      */   
35260     isFitContainer : true, 
35261     
35262     /**
35263      * @cfg {Boolean} preventDefault defalut false
35264      */   
35265     preventDefault : false, 
35266     
35267     /**
35268      * @cfg {Boolean} inverse defalut false
35269      */   
35270     maskInverse : false, 
35271     
35272     getAutoCreate : function()
35273     {
35274         if(!this.isFitContainer){
35275             return this.getSplitAutoCreate();
35276         }
35277         
35278         var cls = 'masonry-brick masonry-brick-full';
35279         
35280         if(this.href.length){
35281             cls += ' masonry-brick-link';
35282         }
35283         
35284         if(this.bgimage.length){
35285             cls += ' masonry-brick-image';
35286         }
35287         
35288         if(this.maskInverse){
35289             cls += ' mask-inverse';
35290         }
35291         
35292         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35293             cls += ' enable-mask';
35294         }
35295         
35296         if(this.size){
35297             cls += ' masonry-' + this.size + '-brick';
35298         }
35299         
35300         if(this.placetitle.length){
35301             
35302             switch (this.placetitle) {
35303                 case 'center' :
35304                     cls += ' masonry-center-title';
35305                     break;
35306                 case 'bottom' :
35307                     cls += ' masonry-bottom-title';
35308                     break;
35309                 default:
35310                     break;
35311             }
35312             
35313         } else {
35314             if(!this.html.length && !this.bgimage.length){
35315                 cls += ' masonry-center-title';
35316             }
35317
35318             if(!this.html.length && this.bgimage.length){
35319                 cls += ' masonry-bottom-title';
35320             }
35321         }
35322         
35323         if(this.cls){
35324             cls += ' ' + this.cls;
35325         }
35326         
35327         var cfg = {
35328             tag: (this.href.length) ? 'a' : 'div',
35329             cls: cls,
35330             cn: [
35331                 {
35332                     tag: 'div',
35333                     cls: 'masonry-brick-mask'
35334                 },
35335                 {
35336                     tag: 'div',
35337                     cls: 'masonry-brick-paragraph',
35338                     cn: []
35339                 }
35340             ]
35341         };
35342         
35343         if(this.href.length){
35344             cfg.href = this.href;
35345         }
35346         
35347         var cn = cfg.cn[1].cn;
35348         
35349         if(this.title.length){
35350             cn.push({
35351                 tag: 'h4',
35352                 cls: 'masonry-brick-title',
35353                 html: this.title
35354             });
35355         }
35356         
35357         if(this.html.length){
35358             cn.push({
35359                 tag: 'p',
35360                 cls: 'masonry-brick-text',
35361                 html: this.html
35362             });
35363         }
35364         
35365         if (!this.title.length && !this.html.length) {
35366             cfg.cn[1].cls += ' hide';
35367         }
35368         
35369         if(this.bgimage.length){
35370             cfg.cn.push({
35371                 tag: 'img',
35372                 cls: 'masonry-brick-image-view',
35373                 src: this.bgimage
35374             });
35375         }
35376         
35377         if(this.videourl.length){
35378             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35379             // youtube support only?
35380             cfg.cn.push({
35381                 tag: 'iframe',
35382                 cls: 'masonry-brick-image-view',
35383                 src: vurl,
35384                 frameborder : 0,
35385                 allowfullscreen : true
35386             });
35387         }
35388         
35389         return cfg;
35390         
35391     },
35392     
35393     getSplitAutoCreate : function()
35394     {
35395         var cls = 'masonry-brick masonry-brick-split';
35396         
35397         if(this.href.length){
35398             cls += ' masonry-brick-link';
35399         }
35400         
35401         if(this.bgimage.length){
35402             cls += ' masonry-brick-image';
35403         }
35404         
35405         if(this.size){
35406             cls += ' masonry-' + this.size + '-brick';
35407         }
35408         
35409         switch (this.placetitle) {
35410             case 'center' :
35411                 cls += ' masonry-center-title';
35412                 break;
35413             case 'bottom' :
35414                 cls += ' masonry-bottom-title';
35415                 break;
35416             default:
35417                 if(!this.bgimage.length){
35418                     cls += ' masonry-center-title';
35419                 }
35420
35421                 if(this.bgimage.length){
35422                     cls += ' masonry-bottom-title';
35423                 }
35424                 break;
35425         }
35426         
35427         if(this.cls){
35428             cls += ' ' + this.cls;
35429         }
35430         
35431         var cfg = {
35432             tag: (this.href.length) ? 'a' : 'div',
35433             cls: cls,
35434             cn: [
35435                 {
35436                     tag: 'div',
35437                     cls: 'masonry-brick-split-head',
35438                     cn: [
35439                         {
35440                             tag: 'div',
35441                             cls: 'masonry-brick-paragraph',
35442                             cn: []
35443                         }
35444                     ]
35445                 },
35446                 {
35447                     tag: 'div',
35448                     cls: 'masonry-brick-split-body',
35449                     cn: []
35450                 }
35451             ]
35452         };
35453         
35454         if(this.href.length){
35455             cfg.href = this.href;
35456         }
35457         
35458         if(this.title.length){
35459             cfg.cn[0].cn[0].cn.push({
35460                 tag: 'h4',
35461                 cls: 'masonry-brick-title',
35462                 html: this.title
35463             });
35464         }
35465         
35466         if(this.html.length){
35467             cfg.cn[1].cn.push({
35468                 tag: 'p',
35469                 cls: 'masonry-brick-text',
35470                 html: this.html
35471             });
35472         }
35473
35474         if(this.bgimage.length){
35475             cfg.cn[0].cn.push({
35476                 tag: 'img',
35477                 cls: 'masonry-brick-image-view',
35478                 src: this.bgimage
35479             });
35480         }
35481         
35482         if(this.videourl.length){
35483             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35484             // youtube support only?
35485             cfg.cn[0].cn.cn.push({
35486                 tag: 'iframe',
35487                 cls: 'masonry-brick-image-view',
35488                 src: vurl,
35489                 frameborder : 0,
35490                 allowfullscreen : true
35491             });
35492         }
35493         
35494         return cfg;
35495     },
35496     
35497     initEvents: function() 
35498     {
35499         switch (this.size) {
35500             case 'xs' :
35501                 this.x = 1;
35502                 this.y = 1;
35503                 break;
35504             case 'sm' :
35505                 this.x = 2;
35506                 this.y = 2;
35507                 break;
35508             case 'md' :
35509             case 'md-left' :
35510             case 'md-right' :
35511                 this.x = 3;
35512                 this.y = 3;
35513                 break;
35514             case 'tall' :
35515                 this.x = 2;
35516                 this.y = 3;
35517                 break;
35518             case 'wide' :
35519                 this.x = 3;
35520                 this.y = 2;
35521                 break;
35522             case 'wide-thin' :
35523                 this.x = 3;
35524                 this.y = 1;
35525                 break;
35526                         
35527             default :
35528                 break;
35529         }
35530         
35531         if(Roo.isTouch){
35532             this.el.on('touchstart', this.onTouchStart, this);
35533             this.el.on('touchmove', this.onTouchMove, this);
35534             this.el.on('touchend', this.onTouchEnd, this);
35535             this.el.on('contextmenu', this.onContextMenu, this);
35536         } else {
35537             this.el.on('mouseenter'  ,this.enter, this);
35538             this.el.on('mouseleave', this.leave, this);
35539             this.el.on('click', this.onClick, this);
35540         }
35541         
35542         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35543             this.parent().bricks.push(this);   
35544         }
35545         
35546     },
35547     
35548     onClick: function(e, el)
35549     {
35550         var time = this.endTimer - this.startTimer;
35551         // Roo.log(e.preventDefault());
35552         if(Roo.isTouch){
35553             if(time > 1000){
35554                 e.preventDefault();
35555                 return;
35556             }
35557         }
35558         
35559         if(!this.preventDefault){
35560             return;
35561         }
35562         
35563         e.preventDefault();
35564         
35565         if (this.activeClass != '') {
35566             this.selectBrick();
35567         }
35568         
35569         this.fireEvent('click', this, e);
35570     },
35571     
35572     enter: function(e, el)
35573     {
35574         e.preventDefault();
35575         
35576         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35577             return;
35578         }
35579         
35580         if(this.bgimage.length && this.html.length){
35581             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35582         }
35583     },
35584     
35585     leave: function(e, el)
35586     {
35587         e.preventDefault();
35588         
35589         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35590             return;
35591         }
35592         
35593         if(this.bgimage.length && this.html.length){
35594             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35595         }
35596     },
35597     
35598     onTouchStart: function(e, el)
35599     {
35600 //        e.preventDefault();
35601         
35602         this.touchmoved = false;
35603         
35604         if(!this.isFitContainer){
35605             return;
35606         }
35607         
35608         if(!this.bgimage.length || !this.html.length){
35609             return;
35610         }
35611         
35612         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35613         
35614         this.timer = new Date().getTime();
35615         
35616     },
35617     
35618     onTouchMove: function(e, el)
35619     {
35620         this.touchmoved = true;
35621     },
35622     
35623     onContextMenu : function(e,el)
35624     {
35625         e.preventDefault();
35626         e.stopPropagation();
35627         return false;
35628     },
35629     
35630     onTouchEnd: function(e, el)
35631     {
35632 //        e.preventDefault();
35633         
35634         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35635         
35636             this.leave(e,el);
35637             
35638             return;
35639         }
35640         
35641         if(!this.bgimage.length || !this.html.length){
35642             
35643             if(this.href.length){
35644                 window.location.href = this.href;
35645             }
35646             
35647             return;
35648         }
35649         
35650         if(!this.isFitContainer){
35651             return;
35652         }
35653         
35654         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35655         
35656         window.location.href = this.href;
35657     },
35658     
35659     //selection on single brick only
35660     selectBrick : function() {
35661         
35662         if (!this.parentId) {
35663             return;
35664         }
35665         
35666         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35667         var index = m.selectedBrick.indexOf(this.id);
35668         
35669         if ( index > -1) {
35670             m.selectedBrick.splice(index,1);
35671             this.el.removeClass(this.activeClass);
35672             return;
35673         }
35674         
35675         for(var i = 0; i < m.selectedBrick.length; i++) {
35676             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35677             b.el.removeClass(b.activeClass);
35678         }
35679         
35680         m.selectedBrick = [];
35681         
35682         m.selectedBrick.push(this.id);
35683         this.el.addClass(this.activeClass);
35684         return;
35685     },
35686     
35687     isSelected : function(){
35688         return this.el.hasClass(this.activeClass);
35689         
35690     }
35691 });
35692
35693 Roo.apply(Roo.bootstrap.MasonryBrick, {
35694     
35695     //groups: {},
35696     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35697      /**
35698     * register a Masonry Brick
35699     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35700     */
35701     
35702     register : function(brick)
35703     {
35704         //this.groups[brick.id] = brick;
35705         this.groups.add(brick.id, brick);
35706     },
35707     /**
35708     * fetch a  masonry brick based on the masonry brick ID
35709     * @param {string} the masonry brick to add
35710     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35711     */
35712     
35713     get: function(brick_id) 
35714     {
35715         // if (typeof(this.groups[brick_id]) == 'undefined') {
35716         //     return false;
35717         // }
35718         // return this.groups[brick_id] ;
35719         
35720         if(this.groups.key(brick_id)) {
35721             return this.groups.key(brick_id);
35722         }
35723         
35724         return false;
35725     }
35726     
35727     
35728     
35729 });
35730
35731  /*
35732  * - LGPL
35733  *
35734  * element
35735  * 
35736  */
35737
35738 /**
35739  * @class Roo.bootstrap.Brick
35740  * @extends Roo.bootstrap.Component
35741  * Bootstrap Brick class
35742  * 
35743  * @constructor
35744  * Create a new Brick
35745  * @param {Object} config The config object
35746  */
35747
35748 Roo.bootstrap.Brick = function(config){
35749     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35750     
35751     this.addEvents({
35752         // raw events
35753         /**
35754          * @event click
35755          * When a Brick is click
35756          * @param {Roo.bootstrap.Brick} this
35757          * @param {Roo.EventObject} e
35758          */
35759         "click" : true
35760     });
35761 };
35762
35763 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35764     
35765     /**
35766      * @cfg {String} title
35767      */   
35768     title : '',
35769     /**
35770      * @cfg {String} html
35771      */   
35772     html : '',
35773     /**
35774      * @cfg {String} bgimage
35775      */   
35776     bgimage : '',
35777     /**
35778      * @cfg {String} cls
35779      */   
35780     cls : '',
35781     /**
35782      * @cfg {String} href
35783      */   
35784     href : '',
35785     /**
35786      * @cfg {String} video
35787      */   
35788     video : '',
35789     /**
35790      * @cfg {Boolean} square
35791      */   
35792     square : true,
35793     
35794     getAutoCreate : function()
35795     {
35796         var cls = 'roo-brick';
35797         
35798         if(this.href.length){
35799             cls += ' roo-brick-link';
35800         }
35801         
35802         if(this.bgimage.length){
35803             cls += ' roo-brick-image';
35804         }
35805         
35806         if(!this.html.length && !this.bgimage.length){
35807             cls += ' roo-brick-center-title';
35808         }
35809         
35810         if(!this.html.length && this.bgimage.length){
35811             cls += ' roo-brick-bottom-title';
35812         }
35813         
35814         if(this.cls){
35815             cls += ' ' + this.cls;
35816         }
35817         
35818         var cfg = {
35819             tag: (this.href.length) ? 'a' : 'div',
35820             cls: cls,
35821             cn: [
35822                 {
35823                     tag: 'div',
35824                     cls: 'roo-brick-paragraph',
35825                     cn: []
35826                 }
35827             ]
35828         };
35829         
35830         if(this.href.length){
35831             cfg.href = this.href;
35832         }
35833         
35834         var cn = cfg.cn[0].cn;
35835         
35836         if(this.title.length){
35837             cn.push({
35838                 tag: 'h4',
35839                 cls: 'roo-brick-title',
35840                 html: this.title
35841             });
35842         }
35843         
35844         if(this.html.length){
35845             cn.push({
35846                 tag: 'p',
35847                 cls: 'roo-brick-text',
35848                 html: this.html
35849             });
35850         } else {
35851             cn.cls += ' hide';
35852         }
35853         
35854         if(this.bgimage.length){
35855             cfg.cn.push({
35856                 tag: 'img',
35857                 cls: 'roo-brick-image-view',
35858                 src: this.bgimage
35859             });
35860         }
35861         
35862         return cfg;
35863     },
35864     
35865     initEvents: function() 
35866     {
35867         if(this.title.length || this.html.length){
35868             this.el.on('mouseenter'  ,this.enter, this);
35869             this.el.on('mouseleave', this.leave, this);
35870         }
35871         
35872         Roo.EventManager.onWindowResize(this.resize, this); 
35873         
35874         if(this.bgimage.length){
35875             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35876             this.imageEl.on('load', this.onImageLoad, this);
35877             return;
35878         }
35879         
35880         this.resize();
35881     },
35882     
35883     onImageLoad : function()
35884     {
35885         this.resize();
35886     },
35887     
35888     resize : function()
35889     {
35890         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35891         
35892         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35893         
35894         if(this.bgimage.length){
35895             var image = this.el.select('.roo-brick-image-view', true).first();
35896             
35897             image.setWidth(paragraph.getWidth());
35898             
35899             if(this.square){
35900                 image.setHeight(paragraph.getWidth());
35901             }
35902             
35903             this.el.setHeight(image.getHeight());
35904             paragraph.setHeight(image.getHeight());
35905             
35906         }
35907         
35908     },
35909     
35910     enter: function(e, el)
35911     {
35912         e.preventDefault();
35913         
35914         if(this.bgimage.length){
35915             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35916             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35917         }
35918     },
35919     
35920     leave: function(e, el)
35921     {
35922         e.preventDefault();
35923         
35924         if(this.bgimage.length){
35925             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35926             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35927         }
35928     }
35929     
35930 });
35931
35932  
35933
35934  /*
35935  * - LGPL
35936  *
35937  * Number field 
35938  */
35939
35940 /**
35941  * @class Roo.bootstrap.NumberField
35942  * @extends Roo.bootstrap.Input
35943  * Bootstrap NumberField class
35944  * 
35945  * 
35946  * 
35947  * 
35948  * @constructor
35949  * Create a new NumberField
35950  * @param {Object} config The config object
35951  */
35952
35953 Roo.bootstrap.NumberField = function(config){
35954     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35955 };
35956
35957 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35958     
35959     /**
35960      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35961      */
35962     allowDecimals : true,
35963     /**
35964      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35965      */
35966     decimalSeparator : ".",
35967     /**
35968      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35969      */
35970     decimalPrecision : 2,
35971     /**
35972      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35973      */
35974     allowNegative : true,
35975     
35976     /**
35977      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35978      */
35979     allowZero: true,
35980     /**
35981      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35982      */
35983     minValue : Number.NEGATIVE_INFINITY,
35984     /**
35985      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35986      */
35987     maxValue : Number.MAX_VALUE,
35988     /**
35989      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35990      */
35991     minText : "The minimum value for this field is {0}",
35992     /**
35993      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35994      */
35995     maxText : "The maximum value for this field is {0}",
35996     /**
35997      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35998      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35999      */
36000     nanText : "{0} is not a valid number",
36001     /**
36002      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36003      */
36004     thousandsDelimiter : false,
36005     /**
36006      * @cfg {String} valueAlign alignment of value
36007      */
36008     valueAlign : "left",
36009
36010     getAutoCreate : function()
36011     {
36012         var hiddenInput = {
36013             tag: 'input',
36014             type: 'hidden',
36015             id: Roo.id(),
36016             cls: 'hidden-number-input'
36017         };
36018         
36019         if (this.name) {
36020             hiddenInput.name = this.name;
36021         }
36022         
36023         this.name = '';
36024         
36025         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36026         
36027         this.name = hiddenInput.name;
36028         
36029         if(cfg.cn.length > 0) {
36030             cfg.cn.push(hiddenInput);
36031         }
36032         
36033         return cfg;
36034     },
36035
36036     // private
36037     initEvents : function()
36038     {   
36039         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36040         
36041         var allowed = "0123456789";
36042         
36043         if(this.allowDecimals){
36044             allowed += this.decimalSeparator;
36045         }
36046         
36047         if(this.allowNegative){
36048             allowed += "-";
36049         }
36050         
36051         if(this.thousandsDelimiter) {
36052             allowed += ",";
36053         }
36054         
36055         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36056         
36057         var keyPress = function(e){
36058             
36059             var k = e.getKey();
36060             
36061             var c = e.getCharCode();
36062             
36063             if(
36064                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36065                     allowed.indexOf(String.fromCharCode(c)) === -1
36066             ){
36067                 e.stopEvent();
36068                 return;
36069             }
36070             
36071             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36072                 return;
36073             }
36074             
36075             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36076                 e.stopEvent();
36077             }
36078         };
36079         
36080         this.el.on("keypress", keyPress, this);
36081     },
36082     
36083     validateValue : function(value)
36084     {
36085         
36086         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36087             return false;
36088         }
36089         
36090         var num = this.parseValue(value);
36091         
36092         if(isNaN(num)){
36093             this.markInvalid(String.format(this.nanText, value));
36094             return false;
36095         }
36096         
36097         if(num < this.minValue){
36098             this.markInvalid(String.format(this.minText, this.minValue));
36099             return false;
36100         }
36101         
36102         if(num > this.maxValue){
36103             this.markInvalid(String.format(this.maxText, this.maxValue));
36104             return false;
36105         }
36106         
36107         return true;
36108     },
36109
36110     getValue : function()
36111     {
36112         var v = this.hiddenEl().getValue();
36113         
36114         return this.fixPrecision(this.parseValue(v));
36115     },
36116
36117     parseValue : function(value)
36118     {
36119         if(this.thousandsDelimiter) {
36120             value += "";
36121             r = new RegExp(",", "g");
36122             value = value.replace(r, "");
36123         }
36124         
36125         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36126         return isNaN(value) ? '' : value;
36127     },
36128
36129     fixPrecision : function(value)
36130     {
36131         if(this.thousandsDelimiter) {
36132             value += "";
36133             r = new RegExp(",", "g");
36134             value = value.replace(r, "");
36135         }
36136         
36137         var nan = isNaN(value);
36138         
36139         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36140             return nan ? '' : value;
36141         }
36142         return parseFloat(value).toFixed(this.decimalPrecision);
36143     },
36144
36145     setValue : function(v)
36146     {
36147         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36148         
36149         this.value = v;
36150         
36151         if(this.rendered){
36152             
36153             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36154             
36155             this.inputEl().dom.value = (v == '') ? '' :
36156                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36157             
36158             if(!this.allowZero && v === '0') {
36159                 this.hiddenEl().dom.value = '';
36160                 this.inputEl().dom.value = '';
36161             }
36162             
36163             this.validate();
36164         }
36165     },
36166
36167     decimalPrecisionFcn : function(v)
36168     {
36169         return Math.floor(v);
36170     },
36171
36172     beforeBlur : function()
36173     {
36174         var v = this.parseValue(this.getRawValue());
36175         
36176         if(v || v === 0 || v === ''){
36177             this.setValue(v);
36178         }
36179     },
36180     
36181     hiddenEl : function()
36182     {
36183         return this.el.select('input.hidden-number-input',true).first();
36184     }
36185     
36186 });
36187
36188  
36189
36190 /*
36191 * Licence: LGPL
36192 */
36193
36194 /**
36195  * @class Roo.bootstrap.DocumentSlider
36196  * @extends Roo.bootstrap.Component
36197  * Bootstrap DocumentSlider class
36198  * 
36199  * @constructor
36200  * Create a new DocumentViewer
36201  * @param {Object} config The config object
36202  */
36203
36204 Roo.bootstrap.DocumentSlider = function(config){
36205     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36206     
36207     this.files = [];
36208     
36209     this.addEvents({
36210         /**
36211          * @event initial
36212          * Fire after initEvent
36213          * @param {Roo.bootstrap.DocumentSlider} this
36214          */
36215         "initial" : true,
36216         /**
36217          * @event update
36218          * Fire after update
36219          * @param {Roo.bootstrap.DocumentSlider} this
36220          */
36221         "update" : true,
36222         /**
36223          * @event click
36224          * Fire after click
36225          * @param {Roo.bootstrap.DocumentSlider} this
36226          */
36227         "click" : true
36228     });
36229 };
36230
36231 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36232     
36233     files : false,
36234     
36235     indicator : 0,
36236     
36237     getAutoCreate : function()
36238     {
36239         var cfg = {
36240             tag : 'div',
36241             cls : 'roo-document-slider',
36242             cn : [
36243                 {
36244                     tag : 'div',
36245                     cls : 'roo-document-slider-header',
36246                     cn : [
36247                         {
36248                             tag : 'div',
36249                             cls : 'roo-document-slider-header-title'
36250                         }
36251                     ]
36252                 },
36253                 {
36254                     tag : 'div',
36255                     cls : 'roo-document-slider-body',
36256                     cn : [
36257                         {
36258                             tag : 'div',
36259                             cls : 'roo-document-slider-prev',
36260                             cn : [
36261                                 {
36262                                     tag : 'i',
36263                                     cls : 'fa fa-chevron-left'
36264                                 }
36265                             ]
36266                         },
36267                         {
36268                             tag : 'div',
36269                             cls : 'roo-document-slider-thumb',
36270                             cn : [
36271                                 {
36272                                     tag : 'img',
36273                                     cls : 'roo-document-slider-image'
36274                                 }
36275                             ]
36276                         },
36277                         {
36278                             tag : 'div',
36279                             cls : 'roo-document-slider-next',
36280                             cn : [
36281                                 {
36282                                     tag : 'i',
36283                                     cls : 'fa fa-chevron-right'
36284                                 }
36285                             ]
36286                         }
36287                     ]
36288                 }
36289             ]
36290         };
36291         
36292         return cfg;
36293     },
36294     
36295     initEvents : function()
36296     {
36297         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36298         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36299         
36300         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36301         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36302         
36303         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36304         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36305         
36306         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36307         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36308         
36309         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36310         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36311         
36312         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36313         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36314         
36315         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36316         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36317         
36318         this.thumbEl.on('click', this.onClick, this);
36319         
36320         this.prevIndicator.on('click', this.prev, this);
36321         
36322         this.nextIndicator.on('click', this.next, this);
36323         
36324     },
36325     
36326     initial : function()
36327     {
36328         if(this.files.length){
36329             this.indicator = 1;
36330             this.update()
36331         }
36332         
36333         this.fireEvent('initial', this);
36334     },
36335     
36336     update : function()
36337     {
36338         this.imageEl.attr('src', this.files[this.indicator - 1]);
36339         
36340         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36341         
36342         this.prevIndicator.show();
36343         
36344         if(this.indicator == 1){
36345             this.prevIndicator.hide();
36346         }
36347         
36348         this.nextIndicator.show();
36349         
36350         if(this.indicator == this.files.length){
36351             this.nextIndicator.hide();
36352         }
36353         
36354         this.thumbEl.scrollTo('top');
36355         
36356         this.fireEvent('update', this);
36357     },
36358     
36359     onClick : function(e)
36360     {
36361         e.preventDefault();
36362         
36363         this.fireEvent('click', this);
36364     },
36365     
36366     prev : function(e)
36367     {
36368         e.preventDefault();
36369         
36370         this.indicator = Math.max(1, this.indicator - 1);
36371         
36372         this.update();
36373     },
36374     
36375     next : function(e)
36376     {
36377         e.preventDefault();
36378         
36379         this.indicator = Math.min(this.files.length, this.indicator + 1);
36380         
36381         this.update();
36382     }
36383 });
36384 /*
36385  * - LGPL
36386  *
36387  * RadioSet
36388  *
36389  *
36390  */
36391
36392 /**
36393  * @class Roo.bootstrap.RadioSet
36394  * @extends Roo.bootstrap.Input
36395  * Bootstrap RadioSet class
36396  * @cfg {String} indicatorpos (left|right) default left
36397  * @cfg {Boolean} inline (true|false) inline the element (default true)
36398  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36399  * @constructor
36400  * Create a new RadioSet
36401  * @param {Object} config The config object
36402  */
36403
36404 Roo.bootstrap.RadioSet = function(config){
36405     
36406     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36407     
36408     this.radioes = [];
36409     
36410     Roo.bootstrap.RadioSet.register(this);
36411     
36412     this.addEvents({
36413         /**
36414         * @event check
36415         * Fires when the element is checked or unchecked.
36416         * @param {Roo.bootstrap.RadioSet} this This radio
36417         * @param {Roo.bootstrap.Radio} item The checked item
36418         */
36419        check : true,
36420        /**
36421         * @event click
36422         * Fires when the element is click.
36423         * @param {Roo.bootstrap.RadioSet} this This radio set
36424         * @param {Roo.bootstrap.Radio} item The checked item
36425         * @param {Roo.EventObject} e The event object
36426         */
36427        click : true
36428     });
36429     
36430 };
36431
36432 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36433
36434     radioes : false,
36435     
36436     inline : true,
36437     
36438     weight : '',
36439     
36440     indicatorpos : 'left',
36441     
36442     getAutoCreate : function()
36443     {
36444         var label = {
36445             tag : 'label',
36446             cls : 'roo-radio-set-label',
36447             cn : [
36448                 {
36449                     tag : 'span',
36450                     html : this.fieldLabel
36451                 }
36452             ]
36453         };
36454         if (Roo.bootstrap.version == 3) {
36455             
36456             
36457             if(this.indicatorpos == 'left'){
36458                 label.cn.unshift({
36459                     tag : 'i',
36460                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36461                     tooltip : 'This field is required'
36462                 });
36463             } else {
36464                 label.cn.push({
36465                     tag : 'i',
36466                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36467                     tooltip : 'This field is required'
36468                 });
36469             }
36470         }
36471         var items = {
36472             tag : 'div',
36473             cls : 'roo-radio-set-items'
36474         };
36475         
36476         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36477         
36478         if (align === 'left' && this.fieldLabel.length) {
36479             
36480             items = {
36481                 cls : "roo-radio-set-right", 
36482                 cn: [
36483                     items
36484                 ]
36485             };
36486             
36487             if(this.labelWidth > 12){
36488                 label.style = "width: " + this.labelWidth + 'px';
36489             }
36490             
36491             if(this.labelWidth < 13 && this.labelmd == 0){
36492                 this.labelmd = this.labelWidth;
36493             }
36494             
36495             if(this.labellg > 0){
36496                 label.cls += ' col-lg-' + this.labellg;
36497                 items.cls += ' col-lg-' + (12 - this.labellg);
36498             }
36499             
36500             if(this.labelmd > 0){
36501                 label.cls += ' col-md-' + this.labelmd;
36502                 items.cls += ' col-md-' + (12 - this.labelmd);
36503             }
36504             
36505             if(this.labelsm > 0){
36506                 label.cls += ' col-sm-' + this.labelsm;
36507                 items.cls += ' col-sm-' + (12 - this.labelsm);
36508             }
36509             
36510             if(this.labelxs > 0){
36511                 label.cls += ' col-xs-' + this.labelxs;
36512                 items.cls += ' col-xs-' + (12 - this.labelxs);
36513             }
36514         }
36515         
36516         var cfg = {
36517             tag : 'div',
36518             cls : 'roo-radio-set',
36519             cn : [
36520                 {
36521                     tag : 'input',
36522                     cls : 'roo-radio-set-input',
36523                     type : 'hidden',
36524                     name : this.name,
36525                     value : this.value ? this.value :  ''
36526                 },
36527                 label,
36528                 items
36529             ]
36530         };
36531         
36532         if(this.weight.length){
36533             cfg.cls += ' roo-radio-' + this.weight;
36534         }
36535         
36536         if(this.inline) {
36537             cfg.cls += ' roo-radio-set-inline';
36538         }
36539         
36540         var settings=this;
36541         ['xs','sm','md','lg'].map(function(size){
36542             if (settings[size]) {
36543                 cfg.cls += ' col-' + size + '-' + settings[size];
36544             }
36545         });
36546         
36547         return cfg;
36548         
36549     },
36550
36551     initEvents : function()
36552     {
36553         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36554         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36555         
36556         if(!this.fieldLabel.length){
36557             this.labelEl.hide();
36558         }
36559         
36560         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36561         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36562         
36563         this.indicator = this.indicatorEl();
36564         
36565         if(this.indicator){
36566             this.indicator.addClass('invisible');
36567         }
36568         
36569         this.originalValue = this.getValue();
36570         
36571     },
36572     
36573     inputEl: function ()
36574     {
36575         return this.el.select('.roo-radio-set-input', true).first();
36576     },
36577     
36578     getChildContainer : function()
36579     {
36580         return this.itemsEl;
36581     },
36582     
36583     register : function(item)
36584     {
36585         this.radioes.push(item);
36586         
36587     },
36588     
36589     validate : function()
36590     {   
36591         if(this.getVisibilityEl().hasClass('hidden')){
36592             return true;
36593         }
36594         
36595         var valid = false;
36596         
36597         Roo.each(this.radioes, function(i){
36598             if(!i.checked){
36599                 return;
36600             }
36601             
36602             valid = true;
36603             return false;
36604         });
36605         
36606         if(this.allowBlank) {
36607             return true;
36608         }
36609         
36610         if(this.disabled || valid){
36611             this.markValid();
36612             return true;
36613         }
36614         
36615         this.markInvalid();
36616         return false;
36617         
36618     },
36619     
36620     markValid : function()
36621     {
36622         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36623             this.indicatorEl().removeClass('visible');
36624             this.indicatorEl().addClass('invisible');
36625         }
36626         
36627         
36628         if (Roo.bootstrap.version == 3) {
36629             this.el.removeClass([this.invalidClass, this.validClass]);
36630             this.el.addClass(this.validClass);
36631         } else {
36632             this.el.removeClass(['is-invalid','is-valid']);
36633             this.el.addClass(['is-valid']);
36634         }
36635         this.fireEvent('valid', this);
36636     },
36637     
36638     markInvalid : function(msg)
36639     {
36640         if(this.allowBlank || this.disabled){
36641             return;
36642         }
36643         
36644         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36645             this.indicatorEl().removeClass('invisible');
36646             this.indicatorEl().addClass('visible');
36647         }
36648         if (Roo.bootstrap.version == 3) {
36649             this.el.removeClass([this.invalidClass, this.validClass]);
36650             this.el.addClass(this.invalidClass);
36651         } else {
36652             this.el.removeClass(['is-invalid','is-valid']);
36653             this.el.addClass(['is-invalid']);
36654         }
36655         
36656         this.fireEvent('invalid', this, msg);
36657         
36658     },
36659     
36660     setValue : function(v, suppressEvent)
36661     {   
36662         if(this.value === v){
36663             return;
36664         }
36665         
36666         this.value = v;
36667         
36668         if(this.rendered){
36669             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36670         }
36671         
36672         Roo.each(this.radioes, function(i){
36673             i.checked = false;
36674             i.el.removeClass('checked');
36675         });
36676         
36677         Roo.each(this.radioes, function(i){
36678             
36679             if(i.value === v || i.value.toString() === v.toString()){
36680                 i.checked = true;
36681                 i.el.addClass('checked');
36682                 
36683                 if(suppressEvent !== true){
36684                     this.fireEvent('check', this, i);
36685                 }
36686                 
36687                 return false;
36688             }
36689             
36690         }, this);
36691         
36692         this.validate();
36693     },
36694     
36695     clearInvalid : function(){
36696         
36697         if(!this.el || this.preventMark){
36698             return;
36699         }
36700         
36701         this.el.removeClass([this.invalidClass]);
36702         
36703         this.fireEvent('valid', this);
36704     }
36705     
36706 });
36707
36708 Roo.apply(Roo.bootstrap.RadioSet, {
36709     
36710     groups: {},
36711     
36712     register : function(set)
36713     {
36714         this.groups[set.name] = set;
36715     },
36716     
36717     get: function(name) 
36718     {
36719         if (typeof(this.groups[name]) == 'undefined') {
36720             return false;
36721         }
36722         
36723         return this.groups[name] ;
36724     }
36725     
36726 });
36727 /*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737
36738
36739 /**
36740  * @class Roo.bootstrap.SplitBar
36741  * @extends Roo.util.Observable
36742  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36743  * <br><br>
36744  * Usage:
36745  * <pre><code>
36746 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36747                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36748 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36749 split.minSize = 100;
36750 split.maxSize = 600;
36751 split.animate = true;
36752 split.on('moved', splitterMoved);
36753 </code></pre>
36754  * @constructor
36755  * Create a new SplitBar
36756  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36757  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36758  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36759  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36760                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36761                         position of the SplitBar).
36762  */
36763 Roo.bootstrap.SplitBar = function(cfg){
36764     
36765     /** @private */
36766     
36767     //{
36768     //  dragElement : elm
36769     //  resizingElement: el,
36770         // optional..
36771     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36772     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36773         // existingProxy ???
36774     //}
36775     
36776     this.el = Roo.get(cfg.dragElement, true);
36777     this.el.dom.unselectable = "on";
36778     /** @private */
36779     this.resizingEl = Roo.get(cfg.resizingElement, true);
36780
36781     /**
36782      * @private
36783      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36784      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36785      * @type Number
36786      */
36787     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36788     
36789     /**
36790      * The minimum size of the resizing element. (Defaults to 0)
36791      * @type Number
36792      */
36793     this.minSize = 0;
36794     
36795     /**
36796      * The maximum size of the resizing element. (Defaults to 2000)
36797      * @type Number
36798      */
36799     this.maxSize = 2000;
36800     
36801     /**
36802      * Whether to animate the transition to the new size
36803      * @type Boolean
36804      */
36805     this.animate = false;
36806     
36807     /**
36808      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36809      * @type Boolean
36810      */
36811     this.useShim = false;
36812     
36813     /** @private */
36814     this.shim = null;
36815     
36816     if(!cfg.existingProxy){
36817         /** @private */
36818         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36819     }else{
36820         this.proxy = Roo.get(cfg.existingProxy).dom;
36821     }
36822     /** @private */
36823     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36824     
36825     /** @private */
36826     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36827     
36828     /** @private */
36829     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36830     
36831     /** @private */
36832     this.dragSpecs = {};
36833     
36834     /**
36835      * @private The adapter to use to positon and resize elements
36836      */
36837     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36838     this.adapter.init(this);
36839     
36840     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36841         /** @private */
36842         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36843         this.el.addClass("roo-splitbar-h");
36844     }else{
36845         /** @private */
36846         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36847         this.el.addClass("roo-splitbar-v");
36848     }
36849     
36850     this.addEvents({
36851         /**
36852          * @event resize
36853          * Fires when the splitter is moved (alias for {@link #event-moved})
36854          * @param {Roo.bootstrap.SplitBar} this
36855          * @param {Number} newSize the new width or height
36856          */
36857         "resize" : true,
36858         /**
36859          * @event moved
36860          * Fires when the splitter is moved
36861          * @param {Roo.bootstrap.SplitBar} this
36862          * @param {Number} newSize the new width or height
36863          */
36864         "moved" : true,
36865         /**
36866          * @event beforeresize
36867          * Fires before the splitter is dragged
36868          * @param {Roo.bootstrap.SplitBar} this
36869          */
36870         "beforeresize" : true,
36871
36872         "beforeapply" : true
36873     });
36874
36875     Roo.util.Observable.call(this);
36876 };
36877
36878 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36879     onStartProxyDrag : function(x, y){
36880         this.fireEvent("beforeresize", this);
36881         if(!this.overlay){
36882             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36883             o.unselectable();
36884             o.enableDisplayMode("block");
36885             // all splitbars share the same overlay
36886             Roo.bootstrap.SplitBar.prototype.overlay = o;
36887         }
36888         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36889         this.overlay.show();
36890         Roo.get(this.proxy).setDisplayed("block");
36891         var size = this.adapter.getElementSize(this);
36892         this.activeMinSize = this.getMinimumSize();;
36893         this.activeMaxSize = this.getMaximumSize();;
36894         var c1 = size - this.activeMinSize;
36895         var c2 = Math.max(this.activeMaxSize - size, 0);
36896         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36897             this.dd.resetConstraints();
36898             this.dd.setXConstraint(
36899                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36900                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36901             );
36902             this.dd.setYConstraint(0, 0);
36903         }else{
36904             this.dd.resetConstraints();
36905             this.dd.setXConstraint(0, 0);
36906             this.dd.setYConstraint(
36907                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36908                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36909             );
36910          }
36911         this.dragSpecs.startSize = size;
36912         this.dragSpecs.startPoint = [x, y];
36913         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36914     },
36915     
36916     /** 
36917      * @private Called after the drag operation by the DDProxy
36918      */
36919     onEndProxyDrag : function(e){
36920         Roo.get(this.proxy).setDisplayed(false);
36921         var endPoint = Roo.lib.Event.getXY(e);
36922         if(this.overlay){
36923             this.overlay.hide();
36924         }
36925         var newSize;
36926         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36927             newSize = this.dragSpecs.startSize + 
36928                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36929                     endPoint[0] - this.dragSpecs.startPoint[0] :
36930                     this.dragSpecs.startPoint[0] - endPoint[0]
36931                 );
36932         }else{
36933             newSize = this.dragSpecs.startSize + 
36934                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36935                     endPoint[1] - this.dragSpecs.startPoint[1] :
36936                     this.dragSpecs.startPoint[1] - endPoint[1]
36937                 );
36938         }
36939         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36940         if(newSize != this.dragSpecs.startSize){
36941             if(this.fireEvent('beforeapply', this, newSize) !== false){
36942                 this.adapter.setElementSize(this, newSize);
36943                 this.fireEvent("moved", this, newSize);
36944                 this.fireEvent("resize", this, newSize);
36945             }
36946         }
36947     },
36948     
36949     /**
36950      * Get the adapter this SplitBar uses
36951      * @return The adapter object
36952      */
36953     getAdapter : function(){
36954         return this.adapter;
36955     },
36956     
36957     /**
36958      * Set the adapter this SplitBar uses
36959      * @param {Object} adapter A SplitBar adapter object
36960      */
36961     setAdapter : function(adapter){
36962         this.adapter = adapter;
36963         this.adapter.init(this);
36964     },
36965     
36966     /**
36967      * Gets the minimum size for the resizing element
36968      * @return {Number} The minimum size
36969      */
36970     getMinimumSize : function(){
36971         return this.minSize;
36972     },
36973     
36974     /**
36975      * Sets the minimum size for the resizing element
36976      * @param {Number} minSize The minimum size
36977      */
36978     setMinimumSize : function(minSize){
36979         this.minSize = minSize;
36980     },
36981     
36982     /**
36983      * Gets the maximum size for the resizing element
36984      * @return {Number} The maximum size
36985      */
36986     getMaximumSize : function(){
36987         return this.maxSize;
36988     },
36989     
36990     /**
36991      * Sets the maximum size for the resizing element
36992      * @param {Number} maxSize The maximum size
36993      */
36994     setMaximumSize : function(maxSize){
36995         this.maxSize = maxSize;
36996     },
36997     
36998     /**
36999      * Sets the initialize size for the resizing element
37000      * @param {Number} size The initial size
37001      */
37002     setCurrentSize : function(size){
37003         var oldAnimate = this.animate;
37004         this.animate = false;
37005         this.adapter.setElementSize(this, size);
37006         this.animate = oldAnimate;
37007     },
37008     
37009     /**
37010      * Destroy this splitbar. 
37011      * @param {Boolean} removeEl True to remove the element
37012      */
37013     destroy : function(removeEl){
37014         if(this.shim){
37015             this.shim.remove();
37016         }
37017         this.dd.unreg();
37018         this.proxy.parentNode.removeChild(this.proxy);
37019         if(removeEl){
37020             this.el.remove();
37021         }
37022     }
37023 });
37024
37025 /**
37026  * @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.
37027  */
37028 Roo.bootstrap.SplitBar.createProxy = function(dir){
37029     var proxy = new Roo.Element(document.createElement("div"));
37030     proxy.unselectable();
37031     var cls = 'roo-splitbar-proxy';
37032     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37033     document.body.appendChild(proxy.dom);
37034     return proxy.dom;
37035 };
37036
37037 /** 
37038  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37039  * Default Adapter. It assumes the splitter and resizing element are not positioned
37040  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37041  */
37042 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37043 };
37044
37045 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37046     // do nothing for now
37047     init : function(s){
37048     
37049     },
37050     /**
37051      * Called before drag operations to get the current size of the resizing element. 
37052      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37053      */
37054      getElementSize : function(s){
37055         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37056             return s.resizingEl.getWidth();
37057         }else{
37058             return s.resizingEl.getHeight();
37059         }
37060     },
37061     
37062     /**
37063      * Called after drag operations to set the size of the resizing element.
37064      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37065      * @param {Number} newSize The new size to set
37066      * @param {Function} onComplete A function to be invoked when resizing is complete
37067      */
37068     setElementSize : function(s, newSize, onComplete){
37069         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37070             if(!s.animate){
37071                 s.resizingEl.setWidth(newSize);
37072                 if(onComplete){
37073                     onComplete(s, newSize);
37074                 }
37075             }else{
37076                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37077             }
37078         }else{
37079             
37080             if(!s.animate){
37081                 s.resizingEl.setHeight(newSize);
37082                 if(onComplete){
37083                     onComplete(s, newSize);
37084                 }
37085             }else{
37086                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37087             }
37088         }
37089     }
37090 };
37091
37092 /** 
37093  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37094  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37095  * Adapter that  moves the splitter element to align with the resized sizing element. 
37096  * Used with an absolute positioned SplitBar.
37097  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37098  * document.body, make sure you assign an id to the body element.
37099  */
37100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37101     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37102     this.container = Roo.get(container);
37103 };
37104
37105 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37106     init : function(s){
37107         this.basic.init(s);
37108     },
37109     
37110     getElementSize : function(s){
37111         return this.basic.getElementSize(s);
37112     },
37113     
37114     setElementSize : function(s, newSize, onComplete){
37115         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37116     },
37117     
37118     moveSplitter : function(s){
37119         var yes = Roo.bootstrap.SplitBar;
37120         switch(s.placement){
37121             case yes.LEFT:
37122                 s.el.setX(s.resizingEl.getRight());
37123                 break;
37124             case yes.RIGHT:
37125                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37126                 break;
37127             case yes.TOP:
37128                 s.el.setY(s.resizingEl.getBottom());
37129                 break;
37130             case yes.BOTTOM:
37131                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37132                 break;
37133         }
37134     }
37135 };
37136
37137 /**
37138  * Orientation constant - Create a vertical SplitBar
37139  * @static
37140  * @type Number
37141  */
37142 Roo.bootstrap.SplitBar.VERTICAL = 1;
37143
37144 /**
37145  * Orientation constant - Create a horizontal SplitBar
37146  * @static
37147  * @type Number
37148  */
37149 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37150
37151 /**
37152  * Placement constant - The resizing element is to the left of the splitter element
37153  * @static
37154  * @type Number
37155  */
37156 Roo.bootstrap.SplitBar.LEFT = 1;
37157
37158 /**
37159  * Placement constant - The resizing element is to the right of the splitter element
37160  * @static
37161  * @type Number
37162  */
37163 Roo.bootstrap.SplitBar.RIGHT = 2;
37164
37165 /**
37166  * Placement constant - The resizing element is positioned above the splitter element
37167  * @static
37168  * @type Number
37169  */
37170 Roo.bootstrap.SplitBar.TOP = 3;
37171
37172 /**
37173  * Placement constant - The resizing element is positioned under splitter element
37174  * @static
37175  * @type Number
37176  */
37177 Roo.bootstrap.SplitBar.BOTTOM = 4;
37178 Roo.namespace("Roo.bootstrap.layout");/*
37179  * Based on:
37180  * Ext JS Library 1.1.1
37181  * Copyright(c) 2006-2007, Ext JS, LLC.
37182  *
37183  * Originally Released Under LGPL - original licence link has changed is not relivant.
37184  *
37185  * Fork - LGPL
37186  * <script type="text/javascript">
37187  */
37188
37189 /**
37190  * @class Roo.bootstrap.layout.Manager
37191  * @extends Roo.bootstrap.Component
37192  * Base class for layout managers.
37193  */
37194 Roo.bootstrap.layout.Manager = function(config)
37195 {
37196     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37197
37198
37199
37200
37201
37202     /** false to disable window resize monitoring @type Boolean */
37203     this.monitorWindowResize = true;
37204     this.regions = {};
37205     this.addEvents({
37206         /**
37207          * @event layout
37208          * Fires when a layout is performed.
37209          * @param {Roo.LayoutManager} this
37210          */
37211         "layout" : true,
37212         /**
37213          * @event regionresized
37214          * Fires when the user resizes a region.
37215          * @param {Roo.LayoutRegion} region The resized region
37216          * @param {Number} newSize The new size (width for east/west, height for north/south)
37217          */
37218         "regionresized" : true,
37219         /**
37220          * @event regioncollapsed
37221          * Fires when a region is collapsed.
37222          * @param {Roo.LayoutRegion} region The collapsed region
37223          */
37224         "regioncollapsed" : true,
37225         /**
37226          * @event regionexpanded
37227          * Fires when a region is expanded.
37228          * @param {Roo.LayoutRegion} region The expanded region
37229          */
37230         "regionexpanded" : true
37231     });
37232     this.updating = false;
37233
37234     if (config.el) {
37235         this.el = Roo.get(config.el);
37236         this.initEvents();
37237     }
37238
37239 };
37240
37241 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37242
37243
37244     regions : null,
37245
37246     monitorWindowResize : true,
37247
37248
37249     updating : false,
37250
37251
37252     onRender : function(ct, position)
37253     {
37254         if(!this.el){
37255             this.el = Roo.get(ct);
37256             this.initEvents();
37257         }
37258         //this.fireEvent('render',this);
37259     },
37260
37261
37262     initEvents: function()
37263     {
37264
37265
37266         // ie scrollbar fix
37267         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37268             document.body.scroll = "no";
37269         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37270             this.el.position('relative');
37271         }
37272         this.id = this.el.id;
37273         this.el.addClass("roo-layout-container");
37274         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37275         if(this.el.dom != document.body ) {
37276             this.el.on('resize', this.layout,this);
37277             this.el.on('show', this.layout,this);
37278         }
37279
37280     },
37281
37282     /**
37283      * Returns true if this layout is currently being updated
37284      * @return {Boolean}
37285      */
37286     isUpdating : function(){
37287         return this.updating;
37288     },
37289
37290     /**
37291      * Suspend the LayoutManager from doing auto-layouts while
37292      * making multiple add or remove calls
37293      */
37294     beginUpdate : function(){
37295         this.updating = true;
37296     },
37297
37298     /**
37299      * Restore auto-layouts and optionally disable the manager from performing a layout
37300      * @param {Boolean} noLayout true to disable a layout update
37301      */
37302     endUpdate : function(noLayout){
37303         this.updating = false;
37304         if(!noLayout){
37305             this.layout();
37306         }
37307     },
37308
37309     layout: function(){
37310         // abstract...
37311     },
37312
37313     onRegionResized : function(region, newSize){
37314         this.fireEvent("regionresized", region, newSize);
37315         this.layout();
37316     },
37317
37318     onRegionCollapsed : function(region){
37319         this.fireEvent("regioncollapsed", region);
37320     },
37321
37322     onRegionExpanded : function(region){
37323         this.fireEvent("regionexpanded", region);
37324     },
37325
37326     /**
37327      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37328      * performs box-model adjustments.
37329      * @return {Object} The size as an object {width: (the width), height: (the height)}
37330      */
37331     getViewSize : function()
37332     {
37333         var size;
37334         if(this.el.dom != document.body){
37335             size = this.el.getSize();
37336         }else{
37337             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37338         }
37339         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37340         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37341         return size;
37342     },
37343
37344     /**
37345      * Returns the Element this layout is bound to.
37346      * @return {Roo.Element}
37347      */
37348     getEl : function(){
37349         return this.el;
37350     },
37351
37352     /**
37353      * Returns the specified region.
37354      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37355      * @return {Roo.LayoutRegion}
37356      */
37357     getRegion : function(target){
37358         return this.regions[target.toLowerCase()];
37359     },
37360
37361     onWindowResize : function(){
37362         if(this.monitorWindowResize){
37363             this.layout();
37364         }
37365     }
37366 });
37367 /*
37368  * Based on:
37369  * Ext JS Library 1.1.1
37370  * Copyright(c) 2006-2007, Ext JS, LLC.
37371  *
37372  * Originally Released Under LGPL - original licence link has changed is not relivant.
37373  *
37374  * Fork - LGPL
37375  * <script type="text/javascript">
37376  */
37377 /**
37378  * @class Roo.bootstrap.layout.Border
37379  * @extends Roo.bootstrap.layout.Manager
37380  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37381  * please see: examples/bootstrap/nested.html<br><br>
37382  
37383 <b>The container the layout is rendered into can be either the body element or any other element.
37384 If it is not the body element, the container needs to either be an absolute positioned element,
37385 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37386 the container size if it is not the body element.</b>
37387
37388 * @constructor
37389 * Create a new Border
37390 * @param {Object} config Configuration options
37391  */
37392 Roo.bootstrap.layout.Border = function(config){
37393     config = config || {};
37394     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37395     
37396     
37397     
37398     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37399         if(config[region]){
37400             config[region].region = region;
37401             this.addRegion(config[region]);
37402         }
37403     },this);
37404     
37405 };
37406
37407 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37408
37409 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37410     
37411     parent : false, // this might point to a 'nest' or a ???
37412     
37413     /**
37414      * Creates and adds a new region if it doesn't already exist.
37415      * @param {String} target The target region key (north, south, east, west or center).
37416      * @param {Object} config The regions config object
37417      * @return {BorderLayoutRegion} The new region
37418      */
37419     addRegion : function(config)
37420     {
37421         if(!this.regions[config.region]){
37422             var r = this.factory(config);
37423             this.bindRegion(r);
37424         }
37425         return this.regions[config.region];
37426     },
37427
37428     // private (kinda)
37429     bindRegion : function(r){
37430         this.regions[r.config.region] = r;
37431         
37432         r.on("visibilitychange",    this.layout, this);
37433         r.on("paneladded",          this.layout, this);
37434         r.on("panelremoved",        this.layout, this);
37435         r.on("invalidated",         this.layout, this);
37436         r.on("resized",             this.onRegionResized, this);
37437         r.on("collapsed",           this.onRegionCollapsed, this);
37438         r.on("expanded",            this.onRegionExpanded, this);
37439     },
37440
37441     /**
37442      * Performs a layout update.
37443      */
37444     layout : function()
37445     {
37446         if(this.updating) {
37447             return;
37448         }
37449         
37450         // render all the rebions if they have not been done alreayd?
37451         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37452             if(this.regions[region] && !this.regions[region].bodyEl){
37453                 this.regions[region].onRender(this.el)
37454             }
37455         },this);
37456         
37457         var size = this.getViewSize();
37458         var w = size.width;
37459         var h = size.height;
37460         var centerW = w;
37461         var centerH = h;
37462         var centerY = 0;
37463         var centerX = 0;
37464         //var x = 0, y = 0;
37465
37466         var rs = this.regions;
37467         var north = rs["north"];
37468         var south = rs["south"]; 
37469         var west = rs["west"];
37470         var east = rs["east"];
37471         var center = rs["center"];
37472         //if(this.hideOnLayout){ // not supported anymore
37473             //c.el.setStyle("display", "none");
37474         //}
37475         if(north && north.isVisible()){
37476             var b = north.getBox();
37477             var m = north.getMargins();
37478             b.width = w - (m.left+m.right);
37479             b.x = m.left;
37480             b.y = m.top;
37481             centerY = b.height + b.y + m.bottom;
37482             centerH -= centerY;
37483             north.updateBox(this.safeBox(b));
37484         }
37485         if(south && south.isVisible()){
37486             var b = south.getBox();
37487             var m = south.getMargins();
37488             b.width = w - (m.left+m.right);
37489             b.x = m.left;
37490             var totalHeight = (b.height + m.top + m.bottom);
37491             b.y = h - totalHeight + m.top;
37492             centerH -= totalHeight;
37493             south.updateBox(this.safeBox(b));
37494         }
37495         if(west && west.isVisible()){
37496             var b = west.getBox();
37497             var m = west.getMargins();
37498             b.height = centerH - (m.top+m.bottom);
37499             b.x = m.left;
37500             b.y = centerY + m.top;
37501             var totalWidth = (b.width + m.left + m.right);
37502             centerX += totalWidth;
37503             centerW -= totalWidth;
37504             west.updateBox(this.safeBox(b));
37505         }
37506         if(east && east.isVisible()){
37507             var b = east.getBox();
37508             var m = east.getMargins();
37509             b.height = centerH - (m.top+m.bottom);
37510             var totalWidth = (b.width + m.left + m.right);
37511             b.x = w - totalWidth + m.left;
37512             b.y = centerY + m.top;
37513             centerW -= totalWidth;
37514             east.updateBox(this.safeBox(b));
37515         }
37516         if(center){
37517             var m = center.getMargins();
37518             var centerBox = {
37519                 x: centerX + m.left,
37520                 y: centerY + m.top,
37521                 width: centerW - (m.left+m.right),
37522                 height: centerH - (m.top+m.bottom)
37523             };
37524             //if(this.hideOnLayout){
37525                 //center.el.setStyle("display", "block");
37526             //}
37527             center.updateBox(this.safeBox(centerBox));
37528         }
37529         this.el.repaint();
37530         this.fireEvent("layout", this);
37531     },
37532
37533     // private
37534     safeBox : function(box){
37535         box.width = Math.max(0, box.width);
37536         box.height = Math.max(0, box.height);
37537         return box;
37538     },
37539
37540     /**
37541      * Adds a ContentPanel (or subclass) to this layout.
37542      * @param {String} target The target region key (north, south, east, west or center).
37543      * @param {Roo.ContentPanel} panel The panel to add
37544      * @return {Roo.ContentPanel} The added panel
37545      */
37546     add : function(target, panel){
37547          
37548         target = target.toLowerCase();
37549         return this.regions[target].add(panel);
37550     },
37551
37552     /**
37553      * Remove a ContentPanel (or subclass) to this layout.
37554      * @param {String} target The target region key (north, south, east, west or center).
37555      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37556      * @return {Roo.ContentPanel} The removed panel
37557      */
37558     remove : function(target, panel){
37559         target = target.toLowerCase();
37560         return this.regions[target].remove(panel);
37561     },
37562
37563     /**
37564      * Searches all regions for a panel with the specified id
37565      * @param {String} panelId
37566      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37567      */
37568     findPanel : function(panelId){
37569         var rs = this.regions;
37570         for(var target in rs){
37571             if(typeof rs[target] != "function"){
37572                 var p = rs[target].getPanel(panelId);
37573                 if(p){
37574                     return p;
37575                 }
37576             }
37577         }
37578         return null;
37579     },
37580
37581     /**
37582      * Searches all regions for a panel with the specified id and activates (shows) it.
37583      * @param {String/ContentPanel} panelId The panels id or the panel itself
37584      * @return {Roo.ContentPanel} The shown panel or null
37585      */
37586     showPanel : function(panelId) {
37587       var rs = this.regions;
37588       for(var target in rs){
37589          var r = rs[target];
37590          if(typeof r != "function"){
37591             if(r.hasPanel(panelId)){
37592                return r.showPanel(panelId);
37593             }
37594          }
37595       }
37596       return null;
37597    },
37598
37599    /**
37600      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37601      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37602      */
37603    /*
37604     restoreState : function(provider){
37605         if(!provider){
37606             provider = Roo.state.Manager;
37607         }
37608         var sm = new Roo.LayoutStateManager();
37609         sm.init(this, provider);
37610     },
37611 */
37612  
37613  
37614     /**
37615      * Adds a xtype elements to the layout.
37616      * <pre><code>
37617
37618 layout.addxtype({
37619        xtype : 'ContentPanel',
37620        region: 'west',
37621        items: [ .... ]
37622    }
37623 );
37624
37625 layout.addxtype({
37626         xtype : 'NestedLayoutPanel',
37627         region: 'west',
37628         layout: {
37629            center: { },
37630            west: { }   
37631         },
37632         items : [ ... list of content panels or nested layout panels.. ]
37633    }
37634 );
37635 </code></pre>
37636      * @param {Object} cfg Xtype definition of item to add.
37637      */
37638     addxtype : function(cfg)
37639     {
37640         // basically accepts a pannel...
37641         // can accept a layout region..!?!?
37642         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37643         
37644         
37645         // theory?  children can only be panels??
37646         
37647         //if (!cfg.xtype.match(/Panel$/)) {
37648         //    return false;
37649         //}
37650         var ret = false;
37651         
37652         if (typeof(cfg.region) == 'undefined') {
37653             Roo.log("Failed to add Panel, region was not set");
37654             Roo.log(cfg);
37655             return false;
37656         }
37657         var region = cfg.region;
37658         delete cfg.region;
37659         
37660           
37661         var xitems = [];
37662         if (cfg.items) {
37663             xitems = cfg.items;
37664             delete cfg.items;
37665         }
37666         var nb = false;
37667         
37668         if ( region == 'center') {
37669             Roo.log("Center: " + cfg.title);
37670         }
37671         
37672         
37673         switch(cfg.xtype) 
37674         {
37675             case 'Content':  // ContentPanel (el, cfg)
37676             case 'Scroll':  // ContentPanel (el, cfg)
37677             case 'View': 
37678                 cfg.autoCreate = cfg.autoCreate || true;
37679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37680                 //} else {
37681                 //    var el = this.el.createChild();
37682                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37683                 //}
37684                 
37685                 this.add(region, ret);
37686                 break;
37687             
37688             /*
37689             case 'TreePanel': // our new panel!
37690                 cfg.el = this.el.createChild();
37691                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37692                 this.add(region, ret);
37693                 break;
37694             */
37695             
37696             case 'Nest': 
37697                 // create a new Layout (which is  a Border Layout...
37698                 
37699                 var clayout = cfg.layout;
37700                 clayout.el  = this.el.createChild();
37701                 clayout.items   = clayout.items  || [];
37702                 
37703                 delete cfg.layout;
37704                 
37705                 // replace this exitems with the clayout ones..
37706                 xitems = clayout.items;
37707                  
37708                 // force background off if it's in center...
37709                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37710                     cfg.background = false;
37711                 }
37712                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37713                 
37714                 
37715                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37716                 //console.log('adding nested layout panel '  + cfg.toSource());
37717                 this.add(region, ret);
37718                 nb = {}; /// find first...
37719                 break;
37720             
37721             case 'Grid':
37722                 
37723                 // needs grid and region
37724                 
37725                 //var el = this.getRegion(region).el.createChild();
37726                 /*
37727                  *var el = this.el.createChild();
37728                 // create the grid first...
37729                 cfg.grid.container = el;
37730                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37731                 */
37732                 
37733                 if (region == 'center' && this.active ) {
37734                     cfg.background = false;
37735                 }
37736                 
37737                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37738                 
37739                 this.add(region, ret);
37740                 /*
37741                 if (cfg.background) {
37742                     // render grid on panel activation (if panel background)
37743                     ret.on('activate', function(gp) {
37744                         if (!gp.grid.rendered) {
37745                     //        gp.grid.render(el);
37746                         }
37747                     });
37748                 } else {
37749                   //  cfg.grid.render(el);
37750                 }
37751                 */
37752                 break;
37753            
37754            
37755             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37756                 // it was the old xcomponent building that caused this before.
37757                 // espeically if border is the top element in the tree.
37758                 ret = this;
37759                 break; 
37760                 
37761                     
37762                 
37763                 
37764                 
37765             default:
37766                 /*
37767                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37768                     
37769                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37770                     this.add(region, ret);
37771                 } else {
37772                 */
37773                     Roo.log(cfg);
37774                     throw "Can not add '" + cfg.xtype + "' to Border";
37775                     return null;
37776              
37777                                 
37778              
37779         }
37780         this.beginUpdate();
37781         // add children..
37782         var region = '';
37783         var abn = {};
37784         Roo.each(xitems, function(i)  {
37785             region = nb && i.region ? i.region : false;
37786             
37787             var add = ret.addxtype(i);
37788            
37789             if (region) {
37790                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37791                 if (!i.background) {
37792                     abn[region] = nb[region] ;
37793                 }
37794             }
37795             
37796         });
37797         this.endUpdate();
37798
37799         // make the last non-background panel active..
37800         //if (nb) { Roo.log(abn); }
37801         if (nb) {
37802             
37803             for(var r in abn) {
37804                 region = this.getRegion(r);
37805                 if (region) {
37806                     // tried using nb[r], but it does not work..
37807                      
37808                     region.showPanel(abn[r]);
37809                    
37810                 }
37811             }
37812         }
37813         return ret;
37814         
37815     },
37816     
37817     
37818 // private
37819     factory : function(cfg)
37820     {
37821         
37822         var validRegions = Roo.bootstrap.layout.Border.regions;
37823
37824         var target = cfg.region;
37825         cfg.mgr = this;
37826         
37827         var r = Roo.bootstrap.layout;
37828         Roo.log(target);
37829         switch(target){
37830             case "north":
37831                 return new r.North(cfg);
37832             case "south":
37833                 return new r.South(cfg);
37834             case "east":
37835                 return new r.East(cfg);
37836             case "west":
37837                 return new r.West(cfg);
37838             case "center":
37839                 return new r.Center(cfg);
37840         }
37841         throw 'Layout region "'+target+'" not supported.';
37842     }
37843     
37844     
37845 });
37846  /*
37847  * Based on:
37848  * Ext JS Library 1.1.1
37849  * Copyright(c) 2006-2007, Ext JS, LLC.
37850  *
37851  * Originally Released Under LGPL - original licence link has changed is not relivant.
37852  *
37853  * Fork - LGPL
37854  * <script type="text/javascript">
37855  */
37856  
37857 /**
37858  * @class Roo.bootstrap.layout.Basic
37859  * @extends Roo.util.Observable
37860  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37861  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37862  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37863  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37864  * @cfg {string}   region  the region that it inhabits..
37865  * @cfg {bool}   skipConfig skip config?
37866  * 
37867
37868  */
37869 Roo.bootstrap.layout.Basic = function(config){
37870     
37871     this.mgr = config.mgr;
37872     
37873     this.position = config.region;
37874     
37875     var skipConfig = config.skipConfig;
37876     
37877     this.events = {
37878         /**
37879          * @scope Roo.BasicLayoutRegion
37880          */
37881         
37882         /**
37883          * @event beforeremove
37884          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37885          * @param {Roo.LayoutRegion} this
37886          * @param {Roo.ContentPanel} panel The panel
37887          * @param {Object} e The cancel event object
37888          */
37889         "beforeremove" : true,
37890         /**
37891          * @event invalidated
37892          * Fires when the layout for this region is changed.
37893          * @param {Roo.LayoutRegion} this
37894          */
37895         "invalidated" : true,
37896         /**
37897          * @event visibilitychange
37898          * Fires when this region is shown or hidden 
37899          * @param {Roo.LayoutRegion} this
37900          * @param {Boolean} visibility true or false
37901          */
37902         "visibilitychange" : true,
37903         /**
37904          * @event paneladded
37905          * Fires when a panel is added. 
37906          * @param {Roo.LayoutRegion} this
37907          * @param {Roo.ContentPanel} panel The panel
37908          */
37909         "paneladded" : true,
37910         /**
37911          * @event panelremoved
37912          * Fires when a panel is removed. 
37913          * @param {Roo.LayoutRegion} this
37914          * @param {Roo.ContentPanel} panel The panel
37915          */
37916         "panelremoved" : true,
37917         /**
37918          * @event beforecollapse
37919          * Fires when this region before collapse.
37920          * @param {Roo.LayoutRegion} this
37921          */
37922         "beforecollapse" : true,
37923         /**
37924          * @event collapsed
37925          * Fires when this region is collapsed.
37926          * @param {Roo.LayoutRegion} this
37927          */
37928         "collapsed" : true,
37929         /**
37930          * @event expanded
37931          * Fires when this region is expanded.
37932          * @param {Roo.LayoutRegion} this
37933          */
37934         "expanded" : true,
37935         /**
37936          * @event slideshow
37937          * Fires when this region is slid into view.
37938          * @param {Roo.LayoutRegion} this
37939          */
37940         "slideshow" : true,
37941         /**
37942          * @event slidehide
37943          * Fires when this region slides out of view. 
37944          * @param {Roo.LayoutRegion} this
37945          */
37946         "slidehide" : true,
37947         /**
37948          * @event panelactivated
37949          * Fires when a panel is activated. 
37950          * @param {Roo.LayoutRegion} this
37951          * @param {Roo.ContentPanel} panel The activated panel
37952          */
37953         "panelactivated" : true,
37954         /**
37955          * @event resized
37956          * Fires when the user resizes this region. 
37957          * @param {Roo.LayoutRegion} this
37958          * @param {Number} newSize The new size (width for east/west, height for north/south)
37959          */
37960         "resized" : true
37961     };
37962     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37963     this.panels = new Roo.util.MixedCollection();
37964     this.panels.getKey = this.getPanelId.createDelegate(this);
37965     this.box = null;
37966     this.activePanel = null;
37967     // ensure listeners are added...
37968     
37969     if (config.listeners || config.events) {
37970         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37971             listeners : config.listeners || {},
37972             events : config.events || {}
37973         });
37974     }
37975     
37976     if(skipConfig !== true){
37977         this.applyConfig(config);
37978     }
37979 };
37980
37981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37982 {
37983     getPanelId : function(p){
37984         return p.getId();
37985     },
37986     
37987     applyConfig : function(config){
37988         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37989         this.config = config;
37990         
37991     },
37992     
37993     /**
37994      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37995      * the width, for horizontal (north, south) the height.
37996      * @param {Number} newSize The new width or height
37997      */
37998     resizeTo : function(newSize){
37999         var el = this.el ? this.el :
38000                  (this.activePanel ? this.activePanel.getEl() : null);
38001         if(el){
38002             switch(this.position){
38003                 case "east":
38004                 case "west":
38005                     el.setWidth(newSize);
38006                     this.fireEvent("resized", this, newSize);
38007                 break;
38008                 case "north":
38009                 case "south":
38010                     el.setHeight(newSize);
38011                     this.fireEvent("resized", this, newSize);
38012                 break;                
38013             }
38014         }
38015     },
38016     
38017     getBox : function(){
38018         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38019     },
38020     
38021     getMargins : function(){
38022         return this.margins;
38023     },
38024     
38025     updateBox : function(box){
38026         this.box = box;
38027         var el = this.activePanel.getEl();
38028         el.dom.style.left = box.x + "px";
38029         el.dom.style.top = box.y + "px";
38030         this.activePanel.setSize(box.width, box.height);
38031     },
38032     
38033     /**
38034      * Returns the container element for this region.
38035      * @return {Roo.Element}
38036      */
38037     getEl : function(){
38038         return this.activePanel;
38039     },
38040     
38041     /**
38042      * Returns true if this region is currently visible.
38043      * @return {Boolean}
38044      */
38045     isVisible : function(){
38046         return this.activePanel ? true : false;
38047     },
38048     
38049     setActivePanel : function(panel){
38050         panel = this.getPanel(panel);
38051         if(this.activePanel && this.activePanel != panel){
38052             this.activePanel.setActiveState(false);
38053             this.activePanel.getEl().setLeftTop(-10000,-10000);
38054         }
38055         this.activePanel = panel;
38056         panel.setActiveState(true);
38057         if(this.box){
38058             panel.setSize(this.box.width, this.box.height);
38059         }
38060         this.fireEvent("panelactivated", this, panel);
38061         this.fireEvent("invalidated");
38062     },
38063     
38064     /**
38065      * Show the specified panel.
38066      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38067      * @return {Roo.ContentPanel} The shown panel or null
38068      */
38069     showPanel : function(panel){
38070         panel = this.getPanel(panel);
38071         if(panel){
38072             this.setActivePanel(panel);
38073         }
38074         return panel;
38075     },
38076     
38077     /**
38078      * Get the active panel for this region.
38079      * @return {Roo.ContentPanel} The active panel or null
38080      */
38081     getActivePanel : function(){
38082         return this.activePanel;
38083     },
38084     
38085     /**
38086      * Add the passed ContentPanel(s)
38087      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38088      * @return {Roo.ContentPanel} The panel added (if only one was added)
38089      */
38090     add : function(panel){
38091         if(arguments.length > 1){
38092             for(var i = 0, len = arguments.length; i < len; i++) {
38093                 this.add(arguments[i]);
38094             }
38095             return null;
38096         }
38097         if(this.hasPanel(panel)){
38098             this.showPanel(panel);
38099             return panel;
38100         }
38101         var el = panel.getEl();
38102         if(el.dom.parentNode != this.mgr.el.dom){
38103             this.mgr.el.dom.appendChild(el.dom);
38104         }
38105         if(panel.setRegion){
38106             panel.setRegion(this);
38107         }
38108         this.panels.add(panel);
38109         el.setStyle("position", "absolute");
38110         if(!panel.background){
38111             this.setActivePanel(panel);
38112             if(this.config.initialSize && this.panels.getCount()==1){
38113                 this.resizeTo(this.config.initialSize);
38114             }
38115         }
38116         this.fireEvent("paneladded", this, panel);
38117         return panel;
38118     },
38119     
38120     /**
38121      * Returns true if the panel is in this region.
38122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38123      * @return {Boolean}
38124      */
38125     hasPanel : function(panel){
38126         if(typeof panel == "object"){ // must be panel obj
38127             panel = panel.getId();
38128         }
38129         return this.getPanel(panel) ? true : false;
38130     },
38131     
38132     /**
38133      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38135      * @param {Boolean} preservePanel Overrides the config preservePanel option
38136      * @return {Roo.ContentPanel} The panel that was removed
38137      */
38138     remove : function(panel, preservePanel){
38139         panel = this.getPanel(panel);
38140         if(!panel){
38141             return null;
38142         }
38143         var e = {};
38144         this.fireEvent("beforeremove", this, panel, e);
38145         if(e.cancel === true){
38146             return null;
38147         }
38148         var panelId = panel.getId();
38149         this.panels.removeKey(panelId);
38150         return panel;
38151     },
38152     
38153     /**
38154      * Returns the panel specified or null if it's not in this region.
38155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38156      * @return {Roo.ContentPanel}
38157      */
38158     getPanel : function(id){
38159         if(typeof id == "object"){ // must be panel obj
38160             return id;
38161         }
38162         return this.panels.get(id);
38163     },
38164     
38165     /**
38166      * Returns this regions position (north/south/east/west/center).
38167      * @return {String} 
38168      */
38169     getPosition: function(){
38170         return this.position;    
38171     }
38172 });/*
38173  * Based on:
38174  * Ext JS Library 1.1.1
38175  * Copyright(c) 2006-2007, Ext JS, LLC.
38176  *
38177  * Originally Released Under LGPL - original licence link has changed is not relivant.
38178  *
38179  * Fork - LGPL
38180  * <script type="text/javascript">
38181  */
38182  
38183 /**
38184  * @class Roo.bootstrap.layout.Region
38185  * @extends Roo.bootstrap.layout.Basic
38186  * This class represents a region in a layout manager.
38187  
38188  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38189  * @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})
38190  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38191  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38192  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38193  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38194  * @cfg {String}    title           The title for the region (overrides panel titles)
38195  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38196  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38197  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38198  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38199  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38200  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38201  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38202  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38203  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38204  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38205
38206  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38207  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38208  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38209  * @cfg {Number}    width           For East/West panels
38210  * @cfg {Number}    height          For North/South panels
38211  * @cfg {Boolean}   split           To show the splitter
38212  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38213  * 
38214  * @cfg {string}   cls             Extra CSS classes to add to region
38215  * 
38216  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38217  * @cfg {string}   region  the region that it inhabits..
38218  *
38219
38220  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38221  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38222
38223  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38224  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38225  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38226  */
38227 Roo.bootstrap.layout.Region = function(config)
38228 {
38229     this.applyConfig(config);
38230
38231     var mgr = config.mgr;
38232     var pos = config.region;
38233     config.skipConfig = true;
38234     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38235     
38236     if (mgr.el) {
38237         this.onRender(mgr.el);   
38238     }
38239      
38240     this.visible = true;
38241     this.collapsed = false;
38242     this.unrendered_panels = [];
38243 };
38244
38245 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38246
38247     position: '', // set by wrapper (eg. north/south etc..)
38248     unrendered_panels : null,  // unrendered panels.
38249     
38250     tabPosition : false,
38251     
38252     mgr: false, // points to 'Border'
38253     
38254     
38255     createBody : function(){
38256         /** This region's body element 
38257         * @type Roo.Element */
38258         this.bodyEl = this.el.createChild({
38259                 tag: "div",
38260                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38261         });
38262     },
38263
38264     onRender: function(ctr, pos)
38265     {
38266         var dh = Roo.DomHelper;
38267         /** This region's container element 
38268         * @type Roo.Element */
38269         this.el = dh.append(ctr.dom, {
38270                 tag: "div",
38271                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38272             }, true);
38273         /** This region's title element 
38274         * @type Roo.Element */
38275     
38276         this.titleEl = dh.append(this.el.dom,  {
38277                 tag: "div",
38278                 unselectable: "on",
38279                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38280                 children:[
38281                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38282                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38283                 ]
38284             }, true);
38285         
38286         this.titleEl.enableDisplayMode();
38287         /** This region's title text element 
38288         * @type HTMLElement */
38289         this.titleTextEl = this.titleEl.dom.firstChild;
38290         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38291         /*
38292         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38293         this.closeBtn.enableDisplayMode();
38294         this.closeBtn.on("click", this.closeClicked, this);
38295         this.closeBtn.hide();
38296     */
38297         this.createBody(this.config);
38298         if(this.config.hideWhenEmpty){
38299             this.hide();
38300             this.on("paneladded", this.validateVisibility, this);
38301             this.on("panelremoved", this.validateVisibility, this);
38302         }
38303         if(this.autoScroll){
38304             this.bodyEl.setStyle("overflow", "auto");
38305         }else{
38306             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38307         }
38308         //if(c.titlebar !== false){
38309             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38310                 this.titleEl.hide();
38311             }else{
38312                 this.titleEl.show();
38313                 if(this.config.title){
38314                     this.titleTextEl.innerHTML = this.config.title;
38315                 }
38316             }
38317         //}
38318         if(this.config.collapsed){
38319             this.collapse(true);
38320         }
38321         if(this.config.hidden){
38322             this.hide();
38323         }
38324         
38325         if (this.unrendered_panels && this.unrendered_panels.length) {
38326             for (var i =0;i< this.unrendered_panels.length; i++) {
38327                 this.add(this.unrendered_panels[i]);
38328             }
38329             this.unrendered_panels = null;
38330             
38331         }
38332         
38333     },
38334     
38335     applyConfig : function(c)
38336     {
38337         /*
38338          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38339             var dh = Roo.DomHelper;
38340             if(c.titlebar !== false){
38341                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38342                 this.collapseBtn.on("click", this.collapse, this);
38343                 this.collapseBtn.enableDisplayMode();
38344                 /*
38345                 if(c.showPin === true || this.showPin){
38346                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38347                     this.stickBtn.enableDisplayMode();
38348                     this.stickBtn.on("click", this.expand, this);
38349                     this.stickBtn.hide();
38350                 }
38351                 
38352             }
38353             */
38354             /** This region's collapsed element
38355             * @type Roo.Element */
38356             /*
38357              *
38358             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38359                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38360             ]}, true);
38361             
38362             if(c.floatable !== false){
38363                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38364                this.collapsedEl.on("click", this.collapseClick, this);
38365             }
38366
38367             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38368                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38369                    id: "message", unselectable: "on", style:{"float":"left"}});
38370                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38371              }
38372             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38373             this.expandBtn.on("click", this.expand, this);
38374             
38375         }
38376         
38377         if(this.collapseBtn){
38378             this.collapseBtn.setVisible(c.collapsible == true);
38379         }
38380         
38381         this.cmargins = c.cmargins || this.cmargins ||
38382                          (this.position == "west" || this.position == "east" ?
38383                              {top: 0, left: 2, right:2, bottom: 0} :
38384                              {top: 2, left: 0, right:0, bottom: 2});
38385         */
38386         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38387         
38388         
38389         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38390         
38391         this.autoScroll = c.autoScroll || false;
38392         
38393         
38394        
38395         
38396         this.duration = c.duration || .30;
38397         this.slideDuration = c.slideDuration || .45;
38398         this.config = c;
38399        
38400     },
38401     /**
38402      * Returns true if this region is currently visible.
38403      * @return {Boolean}
38404      */
38405     isVisible : function(){
38406         return this.visible;
38407     },
38408
38409     /**
38410      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38411      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38412      */
38413     //setCollapsedTitle : function(title){
38414     //    title = title || "&#160;";
38415      //   if(this.collapsedTitleTextEl){
38416       //      this.collapsedTitleTextEl.innerHTML = title;
38417        // }
38418     //},
38419
38420     getBox : function(){
38421         var b;
38422       //  if(!this.collapsed){
38423             b = this.el.getBox(false, true);
38424        // }else{
38425           //  b = this.collapsedEl.getBox(false, true);
38426         //}
38427         return b;
38428     },
38429
38430     getMargins : function(){
38431         return this.margins;
38432         //return this.collapsed ? this.cmargins : this.margins;
38433     },
38434 /*
38435     highlight : function(){
38436         this.el.addClass("x-layout-panel-dragover");
38437     },
38438
38439     unhighlight : function(){
38440         this.el.removeClass("x-layout-panel-dragover");
38441     },
38442 */
38443     updateBox : function(box)
38444     {
38445         if (!this.bodyEl) {
38446             return; // not rendered yet..
38447         }
38448         
38449         this.box = box;
38450         if(!this.collapsed){
38451             this.el.dom.style.left = box.x + "px";
38452             this.el.dom.style.top = box.y + "px";
38453             this.updateBody(box.width, box.height);
38454         }else{
38455             this.collapsedEl.dom.style.left = box.x + "px";
38456             this.collapsedEl.dom.style.top = box.y + "px";
38457             this.collapsedEl.setSize(box.width, box.height);
38458         }
38459         if(this.tabs){
38460             this.tabs.autoSizeTabs();
38461         }
38462     },
38463
38464     updateBody : function(w, h)
38465     {
38466         if(w !== null){
38467             this.el.setWidth(w);
38468             w -= this.el.getBorderWidth("rl");
38469             if(this.config.adjustments){
38470                 w += this.config.adjustments[0];
38471             }
38472         }
38473         if(h !== null && h > 0){
38474             this.el.setHeight(h);
38475             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38476             h -= this.el.getBorderWidth("tb");
38477             if(this.config.adjustments){
38478                 h += this.config.adjustments[1];
38479             }
38480             this.bodyEl.setHeight(h);
38481             if(this.tabs){
38482                 h = this.tabs.syncHeight(h);
38483             }
38484         }
38485         if(this.panelSize){
38486             w = w !== null ? w : this.panelSize.width;
38487             h = h !== null ? h : this.panelSize.height;
38488         }
38489         if(this.activePanel){
38490             var el = this.activePanel.getEl();
38491             w = w !== null ? w : el.getWidth();
38492             h = h !== null ? h : el.getHeight();
38493             this.panelSize = {width: w, height: h};
38494             this.activePanel.setSize(w, h);
38495         }
38496         if(Roo.isIE && this.tabs){
38497             this.tabs.el.repaint();
38498         }
38499     },
38500
38501     /**
38502      * Returns the container element for this region.
38503      * @return {Roo.Element}
38504      */
38505     getEl : function(){
38506         return this.el;
38507     },
38508
38509     /**
38510      * Hides this region.
38511      */
38512     hide : function(){
38513         //if(!this.collapsed){
38514             this.el.dom.style.left = "-2000px";
38515             this.el.hide();
38516         //}else{
38517          //   this.collapsedEl.dom.style.left = "-2000px";
38518          //   this.collapsedEl.hide();
38519        // }
38520         this.visible = false;
38521         this.fireEvent("visibilitychange", this, false);
38522     },
38523
38524     /**
38525      * Shows this region if it was previously hidden.
38526      */
38527     show : function(){
38528         //if(!this.collapsed){
38529             this.el.show();
38530         //}else{
38531         //    this.collapsedEl.show();
38532        // }
38533         this.visible = true;
38534         this.fireEvent("visibilitychange", this, true);
38535     },
38536 /*
38537     closeClicked : function(){
38538         if(this.activePanel){
38539             this.remove(this.activePanel);
38540         }
38541     },
38542
38543     collapseClick : function(e){
38544         if(this.isSlid){
38545            e.stopPropagation();
38546            this.slideIn();
38547         }else{
38548            e.stopPropagation();
38549            this.slideOut();
38550         }
38551     },
38552 */
38553     /**
38554      * Collapses this region.
38555      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38556      */
38557     /*
38558     collapse : function(skipAnim, skipCheck = false){
38559         if(this.collapsed) {
38560             return;
38561         }
38562         
38563         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38564             
38565             this.collapsed = true;
38566             if(this.split){
38567                 this.split.el.hide();
38568             }
38569             if(this.config.animate && skipAnim !== true){
38570                 this.fireEvent("invalidated", this);
38571                 this.animateCollapse();
38572             }else{
38573                 this.el.setLocation(-20000,-20000);
38574                 this.el.hide();
38575                 this.collapsedEl.show();
38576                 this.fireEvent("collapsed", this);
38577                 this.fireEvent("invalidated", this);
38578             }
38579         }
38580         
38581     },
38582 */
38583     animateCollapse : function(){
38584         // overridden
38585     },
38586
38587     /**
38588      * Expands this region if it was previously collapsed.
38589      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38590      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38591      */
38592     /*
38593     expand : function(e, skipAnim){
38594         if(e) {
38595             e.stopPropagation();
38596         }
38597         if(!this.collapsed || this.el.hasActiveFx()) {
38598             return;
38599         }
38600         if(this.isSlid){
38601             this.afterSlideIn();
38602             skipAnim = true;
38603         }
38604         this.collapsed = false;
38605         if(this.config.animate && skipAnim !== true){
38606             this.animateExpand();
38607         }else{
38608             this.el.show();
38609             if(this.split){
38610                 this.split.el.show();
38611             }
38612             this.collapsedEl.setLocation(-2000,-2000);
38613             this.collapsedEl.hide();
38614             this.fireEvent("invalidated", this);
38615             this.fireEvent("expanded", this);
38616         }
38617     },
38618 */
38619     animateExpand : function(){
38620         // overridden
38621     },
38622
38623     initTabs : function()
38624     {
38625         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38626         
38627         var ts = new Roo.bootstrap.panel.Tabs({
38628             el: this.bodyEl.dom,
38629             region : this,
38630             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38631             disableTooltips: this.config.disableTabTips,
38632             toolbar : this.config.toolbar
38633         });
38634         
38635         if(this.config.hideTabs){
38636             ts.stripWrap.setDisplayed(false);
38637         }
38638         this.tabs = ts;
38639         ts.resizeTabs = this.config.resizeTabs === true;
38640         ts.minTabWidth = this.config.minTabWidth || 40;
38641         ts.maxTabWidth = this.config.maxTabWidth || 250;
38642         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38643         ts.monitorResize = false;
38644         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38645         ts.bodyEl.addClass('roo-layout-tabs-body');
38646         this.panels.each(this.initPanelAsTab, this);
38647     },
38648
38649     initPanelAsTab : function(panel){
38650         var ti = this.tabs.addTab(
38651             panel.getEl().id,
38652             panel.getTitle(),
38653             null,
38654             this.config.closeOnTab && panel.isClosable(),
38655             panel.tpl
38656         );
38657         if(panel.tabTip !== undefined){
38658             ti.setTooltip(panel.tabTip);
38659         }
38660         ti.on("activate", function(){
38661               this.setActivePanel(panel);
38662         }, this);
38663         
38664         if(this.config.closeOnTab){
38665             ti.on("beforeclose", function(t, e){
38666                 e.cancel = true;
38667                 this.remove(panel);
38668             }, this);
38669         }
38670         
38671         panel.tabItem = ti;
38672         
38673         return ti;
38674     },
38675
38676     updatePanelTitle : function(panel, title)
38677     {
38678         if(this.activePanel == panel){
38679             this.updateTitle(title);
38680         }
38681         if(this.tabs){
38682             var ti = this.tabs.getTab(panel.getEl().id);
38683             ti.setText(title);
38684             if(panel.tabTip !== undefined){
38685                 ti.setTooltip(panel.tabTip);
38686             }
38687         }
38688     },
38689
38690     updateTitle : function(title){
38691         if(this.titleTextEl && !this.config.title){
38692             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38693         }
38694     },
38695
38696     setActivePanel : function(panel)
38697     {
38698         panel = this.getPanel(panel);
38699         if(this.activePanel && this.activePanel != panel){
38700             if(this.activePanel.setActiveState(false) === false){
38701                 return;
38702             }
38703         }
38704         this.activePanel = panel;
38705         panel.setActiveState(true);
38706         if(this.panelSize){
38707             panel.setSize(this.panelSize.width, this.panelSize.height);
38708         }
38709         if(this.closeBtn){
38710             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38711         }
38712         this.updateTitle(panel.getTitle());
38713         if(this.tabs){
38714             this.fireEvent("invalidated", this);
38715         }
38716         this.fireEvent("panelactivated", this, panel);
38717     },
38718
38719     /**
38720      * Shows the specified panel.
38721      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38722      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38723      */
38724     showPanel : function(panel)
38725     {
38726         panel = this.getPanel(panel);
38727         if(panel){
38728             if(this.tabs){
38729                 var tab = this.tabs.getTab(panel.getEl().id);
38730                 if(tab.isHidden()){
38731                     this.tabs.unhideTab(tab.id);
38732                 }
38733                 tab.activate();
38734             }else{
38735                 this.setActivePanel(panel);
38736             }
38737         }
38738         return panel;
38739     },
38740
38741     /**
38742      * Get the active panel for this region.
38743      * @return {Roo.ContentPanel} The active panel or null
38744      */
38745     getActivePanel : function(){
38746         return this.activePanel;
38747     },
38748
38749     validateVisibility : function(){
38750         if(this.panels.getCount() < 1){
38751             this.updateTitle("&#160;");
38752             this.closeBtn.hide();
38753             this.hide();
38754         }else{
38755             if(!this.isVisible()){
38756                 this.show();
38757             }
38758         }
38759     },
38760
38761     /**
38762      * Adds the passed ContentPanel(s) to this region.
38763      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38764      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38765      */
38766     add : function(panel)
38767     {
38768         if(arguments.length > 1){
38769             for(var i = 0, len = arguments.length; i < len; i++) {
38770                 this.add(arguments[i]);
38771             }
38772             return null;
38773         }
38774         
38775         // if we have not been rendered yet, then we can not really do much of this..
38776         if (!this.bodyEl) {
38777             this.unrendered_panels.push(panel);
38778             return panel;
38779         }
38780         
38781         
38782         
38783         
38784         if(this.hasPanel(panel)){
38785             this.showPanel(panel);
38786             return panel;
38787         }
38788         panel.setRegion(this);
38789         this.panels.add(panel);
38790        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38791             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38792             // and hide them... ???
38793             this.bodyEl.dom.appendChild(panel.getEl().dom);
38794             if(panel.background !== true){
38795                 this.setActivePanel(panel);
38796             }
38797             this.fireEvent("paneladded", this, panel);
38798             return panel;
38799         }
38800         */
38801         if(!this.tabs){
38802             this.initTabs();
38803         }else{
38804             this.initPanelAsTab(panel);
38805         }
38806         
38807         
38808         if(panel.background !== true){
38809             this.tabs.activate(panel.getEl().id);
38810         }
38811         this.fireEvent("paneladded", this, panel);
38812         return panel;
38813     },
38814
38815     /**
38816      * Hides the tab for the specified panel.
38817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38818      */
38819     hidePanel : function(panel){
38820         if(this.tabs && (panel = this.getPanel(panel))){
38821             this.tabs.hideTab(panel.getEl().id);
38822         }
38823     },
38824
38825     /**
38826      * Unhides the tab for a previously hidden panel.
38827      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38828      */
38829     unhidePanel : function(panel){
38830         if(this.tabs && (panel = this.getPanel(panel))){
38831             this.tabs.unhideTab(panel.getEl().id);
38832         }
38833     },
38834
38835     clearPanels : function(){
38836         while(this.panels.getCount() > 0){
38837              this.remove(this.panels.first());
38838         }
38839     },
38840
38841     /**
38842      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38843      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38844      * @param {Boolean} preservePanel Overrides the config preservePanel option
38845      * @return {Roo.ContentPanel} The panel that was removed
38846      */
38847     remove : function(panel, preservePanel)
38848     {
38849         panel = this.getPanel(panel);
38850         if(!panel){
38851             return null;
38852         }
38853         var e = {};
38854         this.fireEvent("beforeremove", this, panel, e);
38855         if(e.cancel === true){
38856             return null;
38857         }
38858         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38859         var panelId = panel.getId();
38860         this.panels.removeKey(panelId);
38861         if(preservePanel){
38862             document.body.appendChild(panel.getEl().dom);
38863         }
38864         if(this.tabs){
38865             this.tabs.removeTab(panel.getEl().id);
38866         }else if (!preservePanel){
38867             this.bodyEl.dom.removeChild(panel.getEl().dom);
38868         }
38869         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38870             var p = this.panels.first();
38871             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38872             tempEl.appendChild(p.getEl().dom);
38873             this.bodyEl.update("");
38874             this.bodyEl.dom.appendChild(p.getEl().dom);
38875             tempEl = null;
38876             this.updateTitle(p.getTitle());
38877             this.tabs = null;
38878             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38879             this.setActivePanel(p);
38880         }
38881         panel.setRegion(null);
38882         if(this.activePanel == panel){
38883             this.activePanel = null;
38884         }
38885         if(this.config.autoDestroy !== false && preservePanel !== true){
38886             try{panel.destroy();}catch(e){}
38887         }
38888         this.fireEvent("panelremoved", this, panel);
38889         return panel;
38890     },
38891
38892     /**
38893      * Returns the TabPanel component used by this region
38894      * @return {Roo.TabPanel}
38895      */
38896     getTabs : function(){
38897         return this.tabs;
38898     },
38899
38900     createTool : function(parentEl, className){
38901         var btn = Roo.DomHelper.append(parentEl, {
38902             tag: "div",
38903             cls: "x-layout-tools-button",
38904             children: [ {
38905                 tag: "div",
38906                 cls: "roo-layout-tools-button-inner " + className,
38907                 html: "&#160;"
38908             }]
38909         }, true);
38910         btn.addClassOnOver("roo-layout-tools-button-over");
38911         return btn;
38912     }
38913 });/*
38914  * Based on:
38915  * Ext JS Library 1.1.1
38916  * Copyright(c) 2006-2007, Ext JS, LLC.
38917  *
38918  * Originally Released Under LGPL - original licence link has changed is not relivant.
38919  *
38920  * Fork - LGPL
38921  * <script type="text/javascript">
38922  */
38923  
38924
38925
38926 /**
38927  * @class Roo.SplitLayoutRegion
38928  * @extends Roo.LayoutRegion
38929  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38930  */
38931 Roo.bootstrap.layout.Split = function(config){
38932     this.cursor = config.cursor;
38933     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38934 };
38935
38936 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38937 {
38938     splitTip : "Drag to resize.",
38939     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38940     useSplitTips : false,
38941
38942     applyConfig : function(config){
38943         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38944     },
38945     
38946     onRender : function(ctr,pos) {
38947         
38948         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38949         if(!this.config.split){
38950             return;
38951         }
38952         if(!this.split){
38953             
38954             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38955                             tag: "div",
38956                             id: this.el.id + "-split",
38957                             cls: "roo-layout-split roo-layout-split-"+this.position,
38958                             html: "&#160;"
38959             });
38960             /** The SplitBar for this region 
38961             * @type Roo.SplitBar */
38962             // does not exist yet...
38963             Roo.log([this.position, this.orientation]);
38964             
38965             this.split = new Roo.bootstrap.SplitBar({
38966                 dragElement : splitEl,
38967                 resizingElement: this.el,
38968                 orientation : this.orientation
38969             });
38970             
38971             this.split.on("moved", this.onSplitMove, this);
38972             this.split.useShim = this.config.useShim === true;
38973             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38974             if(this.useSplitTips){
38975                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38976             }
38977             //if(config.collapsible){
38978             //    this.split.el.on("dblclick", this.collapse,  this);
38979             //}
38980         }
38981         if(typeof this.config.minSize != "undefined"){
38982             this.split.minSize = this.config.minSize;
38983         }
38984         if(typeof this.config.maxSize != "undefined"){
38985             this.split.maxSize = this.config.maxSize;
38986         }
38987         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38988             this.hideSplitter();
38989         }
38990         
38991     },
38992
38993     getHMaxSize : function(){
38994          var cmax = this.config.maxSize || 10000;
38995          var center = this.mgr.getRegion("center");
38996          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38997     },
38998
38999     getVMaxSize : function(){
39000          var cmax = this.config.maxSize || 10000;
39001          var center = this.mgr.getRegion("center");
39002          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39003     },
39004
39005     onSplitMove : function(split, newSize){
39006         this.fireEvent("resized", this, newSize);
39007     },
39008     
39009     /** 
39010      * Returns the {@link Roo.SplitBar} for this region.
39011      * @return {Roo.SplitBar}
39012      */
39013     getSplitBar : function(){
39014         return this.split;
39015     },
39016     
39017     hide : function(){
39018         this.hideSplitter();
39019         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39020     },
39021
39022     hideSplitter : function(){
39023         if(this.split){
39024             this.split.el.setLocation(-2000,-2000);
39025             this.split.el.hide();
39026         }
39027     },
39028
39029     show : function(){
39030         if(this.split){
39031             this.split.el.show();
39032         }
39033         Roo.bootstrap.layout.Split.superclass.show.call(this);
39034     },
39035     
39036     beforeSlide: function(){
39037         if(Roo.isGecko){// firefox overflow auto bug workaround
39038             this.bodyEl.clip();
39039             if(this.tabs) {
39040                 this.tabs.bodyEl.clip();
39041             }
39042             if(this.activePanel){
39043                 this.activePanel.getEl().clip();
39044                 
39045                 if(this.activePanel.beforeSlide){
39046                     this.activePanel.beforeSlide();
39047                 }
39048             }
39049         }
39050     },
39051     
39052     afterSlide : function(){
39053         if(Roo.isGecko){// firefox overflow auto bug workaround
39054             this.bodyEl.unclip();
39055             if(this.tabs) {
39056                 this.tabs.bodyEl.unclip();
39057             }
39058             if(this.activePanel){
39059                 this.activePanel.getEl().unclip();
39060                 if(this.activePanel.afterSlide){
39061                     this.activePanel.afterSlide();
39062                 }
39063             }
39064         }
39065     },
39066
39067     initAutoHide : function(){
39068         if(this.autoHide !== false){
39069             if(!this.autoHideHd){
39070                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39071                 this.autoHideHd = {
39072                     "mouseout": function(e){
39073                         if(!e.within(this.el, true)){
39074                             st.delay(500);
39075                         }
39076                     },
39077                     "mouseover" : function(e){
39078                         st.cancel();
39079                     },
39080                     scope : this
39081                 };
39082             }
39083             this.el.on(this.autoHideHd);
39084         }
39085     },
39086
39087     clearAutoHide : function(){
39088         if(this.autoHide !== false){
39089             this.el.un("mouseout", this.autoHideHd.mouseout);
39090             this.el.un("mouseover", this.autoHideHd.mouseover);
39091         }
39092     },
39093
39094     clearMonitor : function(){
39095         Roo.get(document).un("click", this.slideInIf, this);
39096     },
39097
39098     // these names are backwards but not changed for compat
39099     slideOut : function(){
39100         if(this.isSlid || this.el.hasActiveFx()){
39101             return;
39102         }
39103         this.isSlid = true;
39104         if(this.collapseBtn){
39105             this.collapseBtn.hide();
39106         }
39107         this.closeBtnState = this.closeBtn.getStyle('display');
39108         this.closeBtn.hide();
39109         if(this.stickBtn){
39110             this.stickBtn.show();
39111         }
39112         this.el.show();
39113         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39114         this.beforeSlide();
39115         this.el.setStyle("z-index", 10001);
39116         this.el.slideIn(this.getSlideAnchor(), {
39117             callback: function(){
39118                 this.afterSlide();
39119                 this.initAutoHide();
39120                 Roo.get(document).on("click", this.slideInIf, this);
39121                 this.fireEvent("slideshow", this);
39122             },
39123             scope: this,
39124             block: true
39125         });
39126     },
39127
39128     afterSlideIn : function(){
39129         this.clearAutoHide();
39130         this.isSlid = false;
39131         this.clearMonitor();
39132         this.el.setStyle("z-index", "");
39133         if(this.collapseBtn){
39134             this.collapseBtn.show();
39135         }
39136         this.closeBtn.setStyle('display', this.closeBtnState);
39137         if(this.stickBtn){
39138             this.stickBtn.hide();
39139         }
39140         this.fireEvent("slidehide", this);
39141     },
39142
39143     slideIn : function(cb){
39144         if(!this.isSlid || this.el.hasActiveFx()){
39145             Roo.callback(cb);
39146             return;
39147         }
39148         this.isSlid = false;
39149         this.beforeSlide();
39150         this.el.slideOut(this.getSlideAnchor(), {
39151             callback: function(){
39152                 this.el.setLeftTop(-10000, -10000);
39153                 this.afterSlide();
39154                 this.afterSlideIn();
39155                 Roo.callback(cb);
39156             },
39157             scope: this,
39158             block: true
39159         });
39160     },
39161     
39162     slideInIf : function(e){
39163         if(!e.within(this.el)){
39164             this.slideIn();
39165         }
39166     },
39167
39168     animateCollapse : function(){
39169         this.beforeSlide();
39170         this.el.setStyle("z-index", 20000);
39171         var anchor = this.getSlideAnchor();
39172         this.el.slideOut(anchor, {
39173             callback : function(){
39174                 this.el.setStyle("z-index", "");
39175                 this.collapsedEl.slideIn(anchor, {duration:.3});
39176                 this.afterSlide();
39177                 this.el.setLocation(-10000,-10000);
39178                 this.el.hide();
39179                 this.fireEvent("collapsed", this);
39180             },
39181             scope: this,
39182             block: true
39183         });
39184     },
39185
39186     animateExpand : function(){
39187         this.beforeSlide();
39188         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39189         this.el.setStyle("z-index", 20000);
39190         this.collapsedEl.hide({
39191             duration:.1
39192         });
39193         this.el.slideIn(this.getSlideAnchor(), {
39194             callback : function(){
39195                 this.el.setStyle("z-index", "");
39196                 this.afterSlide();
39197                 if(this.split){
39198                     this.split.el.show();
39199                 }
39200                 this.fireEvent("invalidated", this);
39201                 this.fireEvent("expanded", this);
39202             },
39203             scope: this,
39204             block: true
39205         });
39206     },
39207
39208     anchors : {
39209         "west" : "left",
39210         "east" : "right",
39211         "north" : "top",
39212         "south" : "bottom"
39213     },
39214
39215     sanchors : {
39216         "west" : "l",
39217         "east" : "r",
39218         "north" : "t",
39219         "south" : "b"
39220     },
39221
39222     canchors : {
39223         "west" : "tl-tr",
39224         "east" : "tr-tl",
39225         "north" : "tl-bl",
39226         "south" : "bl-tl"
39227     },
39228
39229     getAnchor : function(){
39230         return this.anchors[this.position];
39231     },
39232
39233     getCollapseAnchor : function(){
39234         return this.canchors[this.position];
39235     },
39236
39237     getSlideAnchor : function(){
39238         return this.sanchors[this.position];
39239     },
39240
39241     getAlignAdj : function(){
39242         var cm = this.cmargins;
39243         switch(this.position){
39244             case "west":
39245                 return [0, 0];
39246             break;
39247             case "east":
39248                 return [0, 0];
39249             break;
39250             case "north":
39251                 return [0, 0];
39252             break;
39253             case "south":
39254                 return [0, 0];
39255             break;
39256         }
39257     },
39258
39259     getExpandAdj : function(){
39260         var c = this.collapsedEl, cm = this.cmargins;
39261         switch(this.position){
39262             case "west":
39263                 return [-(cm.right+c.getWidth()+cm.left), 0];
39264             break;
39265             case "east":
39266                 return [cm.right+c.getWidth()+cm.left, 0];
39267             break;
39268             case "north":
39269                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39270             break;
39271             case "south":
39272                 return [0, cm.top+cm.bottom+c.getHeight()];
39273             break;
39274         }
39275     }
39276 });/*
39277  * Based on:
39278  * Ext JS Library 1.1.1
39279  * Copyright(c) 2006-2007, Ext JS, LLC.
39280  *
39281  * Originally Released Under LGPL - original licence link has changed is not relivant.
39282  *
39283  * Fork - LGPL
39284  * <script type="text/javascript">
39285  */
39286 /*
39287  * These classes are private internal classes
39288  */
39289 Roo.bootstrap.layout.Center = function(config){
39290     config.region = "center";
39291     Roo.bootstrap.layout.Region.call(this, config);
39292     this.visible = true;
39293     this.minWidth = config.minWidth || 20;
39294     this.minHeight = config.minHeight || 20;
39295 };
39296
39297 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39298     hide : function(){
39299         // center panel can't be hidden
39300     },
39301     
39302     show : function(){
39303         // center panel can't be hidden
39304     },
39305     
39306     getMinWidth: function(){
39307         return this.minWidth;
39308     },
39309     
39310     getMinHeight: function(){
39311         return this.minHeight;
39312     }
39313 });
39314
39315
39316
39317
39318  
39319
39320
39321
39322
39323
39324
39325 Roo.bootstrap.layout.North = function(config)
39326 {
39327     config.region = 'north';
39328     config.cursor = 'n-resize';
39329     
39330     Roo.bootstrap.layout.Split.call(this, config);
39331     
39332     
39333     if(this.split){
39334         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39335         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39336         this.split.el.addClass("roo-layout-split-v");
39337     }
39338     //var size = config.initialSize || config.height;
39339     //if(this.el && typeof size != "undefined"){
39340     //    this.el.setHeight(size);
39341     //}
39342 };
39343 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39344 {
39345     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39346      
39347      
39348     onRender : function(ctr, pos)
39349     {
39350         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39351         var size = this.config.initialSize || this.config.height;
39352         if(this.el && typeof size != "undefined"){
39353             this.el.setHeight(size);
39354         }
39355     
39356     },
39357     
39358     getBox : function(){
39359         if(this.collapsed){
39360             return this.collapsedEl.getBox();
39361         }
39362         var box = this.el.getBox();
39363         if(this.split){
39364             box.height += this.split.el.getHeight();
39365         }
39366         return box;
39367     },
39368     
39369     updateBox : function(box){
39370         if(this.split && !this.collapsed){
39371             box.height -= this.split.el.getHeight();
39372             this.split.el.setLeft(box.x);
39373             this.split.el.setTop(box.y+box.height);
39374             this.split.el.setWidth(box.width);
39375         }
39376         if(this.collapsed){
39377             this.updateBody(box.width, null);
39378         }
39379         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39380     }
39381 });
39382
39383
39384
39385
39386
39387 Roo.bootstrap.layout.South = function(config){
39388     config.region = 'south';
39389     config.cursor = 's-resize';
39390     Roo.bootstrap.layout.Split.call(this, config);
39391     if(this.split){
39392         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39393         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39394         this.split.el.addClass("roo-layout-split-v");
39395     }
39396     
39397 };
39398
39399 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39400     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39401     
39402     onRender : function(ctr, pos)
39403     {
39404         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39405         var size = this.config.initialSize || this.config.height;
39406         if(this.el && typeof size != "undefined"){
39407             this.el.setHeight(size);
39408         }
39409     
39410     },
39411     
39412     getBox : function(){
39413         if(this.collapsed){
39414             return this.collapsedEl.getBox();
39415         }
39416         var box = this.el.getBox();
39417         if(this.split){
39418             var sh = this.split.el.getHeight();
39419             box.height += sh;
39420             box.y -= sh;
39421         }
39422         return box;
39423     },
39424     
39425     updateBox : function(box){
39426         if(this.split && !this.collapsed){
39427             var sh = this.split.el.getHeight();
39428             box.height -= sh;
39429             box.y += sh;
39430             this.split.el.setLeft(box.x);
39431             this.split.el.setTop(box.y-sh);
39432             this.split.el.setWidth(box.width);
39433         }
39434         if(this.collapsed){
39435             this.updateBody(box.width, null);
39436         }
39437         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39438     }
39439 });
39440
39441 Roo.bootstrap.layout.East = function(config){
39442     config.region = "east";
39443     config.cursor = "e-resize";
39444     Roo.bootstrap.layout.Split.call(this, config);
39445     if(this.split){
39446         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39447         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39448         this.split.el.addClass("roo-layout-split-h");
39449     }
39450     
39451 };
39452 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39453     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39454     
39455     onRender : function(ctr, pos)
39456     {
39457         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39458         var size = this.config.initialSize || this.config.width;
39459         if(this.el && typeof size != "undefined"){
39460             this.el.setWidth(size);
39461         }
39462     
39463     },
39464     
39465     getBox : function(){
39466         if(this.collapsed){
39467             return this.collapsedEl.getBox();
39468         }
39469         var box = this.el.getBox();
39470         if(this.split){
39471             var sw = this.split.el.getWidth();
39472             box.width += sw;
39473             box.x -= sw;
39474         }
39475         return box;
39476     },
39477
39478     updateBox : function(box){
39479         if(this.split && !this.collapsed){
39480             var sw = this.split.el.getWidth();
39481             box.width -= sw;
39482             this.split.el.setLeft(box.x);
39483             this.split.el.setTop(box.y);
39484             this.split.el.setHeight(box.height);
39485             box.x += sw;
39486         }
39487         if(this.collapsed){
39488             this.updateBody(null, box.height);
39489         }
39490         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39491     }
39492 });
39493
39494 Roo.bootstrap.layout.West = function(config){
39495     config.region = "west";
39496     config.cursor = "w-resize";
39497     
39498     Roo.bootstrap.layout.Split.call(this, config);
39499     if(this.split){
39500         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39501         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39502         this.split.el.addClass("roo-layout-split-h");
39503     }
39504     
39505 };
39506 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39507     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39508     
39509     onRender: function(ctr, pos)
39510     {
39511         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39512         var size = this.config.initialSize || this.config.width;
39513         if(typeof size != "undefined"){
39514             this.el.setWidth(size);
39515         }
39516     },
39517     
39518     getBox : function(){
39519         if(this.collapsed){
39520             return this.collapsedEl.getBox();
39521         }
39522         var box = this.el.getBox();
39523         if (box.width == 0) {
39524             box.width = this.config.width; // kludge?
39525         }
39526         if(this.split){
39527             box.width += this.split.el.getWidth();
39528         }
39529         return box;
39530     },
39531     
39532     updateBox : function(box){
39533         if(this.split && !this.collapsed){
39534             var sw = this.split.el.getWidth();
39535             box.width -= sw;
39536             this.split.el.setLeft(box.x+box.width);
39537             this.split.el.setTop(box.y);
39538             this.split.el.setHeight(box.height);
39539         }
39540         if(this.collapsed){
39541             this.updateBody(null, box.height);
39542         }
39543         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39544     }
39545 });Roo.namespace("Roo.bootstrap.panel");/*
39546  * Based on:
39547  * Ext JS Library 1.1.1
39548  * Copyright(c) 2006-2007, Ext JS, LLC.
39549  *
39550  * Originally Released Under LGPL - original licence link has changed is not relivant.
39551  *
39552  * Fork - LGPL
39553  * <script type="text/javascript">
39554  */
39555 /**
39556  * @class Roo.ContentPanel
39557  * @extends Roo.util.Observable
39558  * A basic ContentPanel element.
39559  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39560  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39561  * @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
39562  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39563  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39564  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39565  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39566  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39567  * @cfg {String} title          The title for this panel
39568  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39569  * @cfg {String} url            Calls {@link #setUrl} with this value
39570  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39571  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39572  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39573  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39574  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39575  * @cfg {Boolean} badges render the badges
39576  * @cfg {String} cls  extra classes to use  
39577  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39578
39579  * @constructor
39580  * Create a new ContentPanel.
39581  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39582  * @param {String/Object} config A string to set only the title or a config object
39583  * @param {String} content (optional) Set the HTML content for this panel
39584  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39585  */
39586 Roo.bootstrap.panel.Content = function( config){
39587     
39588     this.tpl = config.tpl || false;
39589     
39590     var el = config.el;
39591     var content = config.content;
39592
39593     if(config.autoCreate){ // xtype is available if this is called from factory
39594         el = Roo.id();
39595     }
39596     this.el = Roo.get(el);
39597     if(!this.el && config && config.autoCreate){
39598         if(typeof config.autoCreate == "object"){
39599             if(!config.autoCreate.id){
39600                 config.autoCreate.id = config.id||el;
39601             }
39602             this.el = Roo.DomHelper.append(document.body,
39603                         config.autoCreate, true);
39604         }else{
39605             var elcfg =  {
39606                 tag: "div",
39607                 cls: (config.cls || '') +
39608                     (config.background ? ' bg-' + config.background : '') +
39609                     " roo-layout-inactive-content",
39610                 id: config.id||el
39611             };
39612             if (config.iframe) {
39613                 elcfg.cn = [
39614                     {
39615                         tag : 'iframe',
39616                         style : 'border: 0px',
39617                         src : 'about:blank'
39618                     }
39619                 ];
39620             }
39621               
39622             if (config.html) {
39623                 elcfg.html = config.html;
39624                 
39625             }
39626                         
39627             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39628             if (config.iframe) {
39629                 this.iframeEl = this.el.select('iframe',true).first();
39630             }
39631             
39632         }
39633     } 
39634     this.closable = false;
39635     this.loaded = false;
39636     this.active = false;
39637    
39638       
39639     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39640         
39641         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39642         
39643         this.wrapEl = this.el; //this.el.wrap();
39644         var ti = [];
39645         if (config.toolbar.items) {
39646             ti = config.toolbar.items ;
39647             delete config.toolbar.items ;
39648         }
39649         
39650         var nitems = [];
39651         this.toolbar.render(this.wrapEl, 'before');
39652         for(var i =0;i < ti.length;i++) {
39653           //  Roo.log(['add child', items[i]]);
39654             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39655         }
39656         this.toolbar.items = nitems;
39657         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39658         delete config.toolbar;
39659         
39660     }
39661     /*
39662     // xtype created footer. - not sure if will work as we normally have to render first..
39663     if (this.footer && !this.footer.el && this.footer.xtype) {
39664         if (!this.wrapEl) {
39665             this.wrapEl = this.el.wrap();
39666         }
39667     
39668         this.footer.container = this.wrapEl.createChild();
39669          
39670         this.footer = Roo.factory(this.footer, Roo);
39671         
39672     }
39673     */
39674     
39675      if(typeof config == "string"){
39676         this.title = config;
39677     }else{
39678         Roo.apply(this, config);
39679     }
39680     
39681     if(this.resizeEl){
39682         this.resizeEl = Roo.get(this.resizeEl, true);
39683     }else{
39684         this.resizeEl = this.el;
39685     }
39686     // handle view.xtype
39687     
39688  
39689     
39690     
39691     this.addEvents({
39692         /**
39693          * @event activate
39694          * Fires when this panel is activated. 
39695          * @param {Roo.ContentPanel} this
39696          */
39697         "activate" : true,
39698         /**
39699          * @event deactivate
39700          * Fires when this panel is activated. 
39701          * @param {Roo.ContentPanel} this
39702          */
39703         "deactivate" : true,
39704
39705         /**
39706          * @event resize
39707          * Fires when this panel is resized if fitToFrame is true.
39708          * @param {Roo.ContentPanel} this
39709          * @param {Number} width The width after any component adjustments
39710          * @param {Number} height The height after any component adjustments
39711          */
39712         "resize" : true,
39713         
39714          /**
39715          * @event render
39716          * Fires when this tab is created
39717          * @param {Roo.ContentPanel} this
39718          */
39719         "render" : true
39720         
39721         
39722         
39723     });
39724     
39725
39726     
39727     
39728     if(this.autoScroll && !this.iframe){
39729         this.resizeEl.setStyle("overflow", "auto");
39730     } else {
39731         // fix randome scrolling
39732         //this.el.on('scroll', function() {
39733         //    Roo.log('fix random scolling');
39734         //    this.scrollTo('top',0); 
39735         //});
39736     }
39737     content = content || this.content;
39738     if(content){
39739         this.setContent(content);
39740     }
39741     if(config && config.url){
39742         this.setUrl(this.url, this.params, this.loadOnce);
39743     }
39744     
39745     
39746     
39747     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39748     
39749     if (this.view && typeof(this.view.xtype) != 'undefined') {
39750         this.view.el = this.el.appendChild(document.createElement("div"));
39751         this.view = Roo.factory(this.view); 
39752         this.view.render  &&  this.view.render(false, '');  
39753     }
39754     
39755     
39756     this.fireEvent('render', this);
39757 };
39758
39759 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39760     
39761     cls : '',
39762     background : '',
39763     
39764     tabTip : '',
39765     
39766     iframe : false,
39767     iframeEl : false,
39768     
39769     setRegion : function(region){
39770         this.region = region;
39771         this.setActiveClass(region && !this.background);
39772     },
39773     
39774     
39775     setActiveClass: function(state)
39776     {
39777         if(state){
39778            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39779            this.el.setStyle('position','relative');
39780         }else{
39781            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39782            this.el.setStyle('position', 'absolute');
39783         } 
39784     },
39785     
39786     /**
39787      * Returns the toolbar for this Panel if one was configured. 
39788      * @return {Roo.Toolbar} 
39789      */
39790     getToolbar : function(){
39791         return this.toolbar;
39792     },
39793     
39794     setActiveState : function(active)
39795     {
39796         this.active = active;
39797         this.setActiveClass(active);
39798         if(!active){
39799             if(this.fireEvent("deactivate", this) === false){
39800                 return false;
39801             }
39802             return true;
39803         }
39804         this.fireEvent("activate", this);
39805         return true;
39806     },
39807     /**
39808      * Updates this panel's element (not for iframe)
39809      * @param {String} content The new content
39810      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39811     */
39812     setContent : function(content, loadScripts){
39813         if (this.iframe) {
39814             return;
39815         }
39816         
39817         this.el.update(content, loadScripts);
39818     },
39819
39820     ignoreResize : function(w, h){
39821         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39822             return true;
39823         }else{
39824             this.lastSize = {width: w, height: h};
39825             return false;
39826         }
39827     },
39828     /**
39829      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39830      * @return {Roo.UpdateManager} The UpdateManager
39831      */
39832     getUpdateManager : function(){
39833         if (this.iframe) {
39834             return false;
39835         }
39836         return this.el.getUpdateManager();
39837     },
39838      /**
39839      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39840      * Does not work with IFRAME contents
39841      * @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:
39842 <pre><code>
39843 panel.load({
39844     url: "your-url.php",
39845     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39846     callback: yourFunction,
39847     scope: yourObject, //(optional scope)
39848     discardUrl: false,
39849     nocache: false,
39850     text: "Loading...",
39851     timeout: 30,
39852     scripts: false
39853 });
39854 </code></pre>
39855      
39856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39857      * 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.
39858      * @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}
39859      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39860      * @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.
39861      * @return {Roo.ContentPanel} this
39862      */
39863     load : function(){
39864         
39865         if (this.iframe) {
39866             return this;
39867         }
39868         
39869         var um = this.el.getUpdateManager();
39870         um.update.apply(um, arguments);
39871         return this;
39872     },
39873
39874
39875     /**
39876      * 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.
39877      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39878      * @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)
39879      * @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)
39880      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39881      */
39882     setUrl : function(url, params, loadOnce){
39883         if (this.iframe) {
39884             this.iframeEl.dom.src = url;
39885             return false;
39886         }
39887         
39888         if(this.refreshDelegate){
39889             this.removeListener("activate", this.refreshDelegate);
39890         }
39891         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39892         this.on("activate", this.refreshDelegate);
39893         return this.el.getUpdateManager();
39894     },
39895     
39896     _handleRefresh : function(url, params, loadOnce){
39897         if(!loadOnce || !this.loaded){
39898             var updater = this.el.getUpdateManager();
39899             updater.update(url, params, this._setLoaded.createDelegate(this));
39900         }
39901     },
39902     
39903     _setLoaded : function(){
39904         this.loaded = true;
39905     }, 
39906     
39907     /**
39908      * Returns this panel's id
39909      * @return {String} 
39910      */
39911     getId : function(){
39912         return this.el.id;
39913     },
39914     
39915     /** 
39916      * Returns this panel's element - used by regiosn to add.
39917      * @return {Roo.Element} 
39918      */
39919     getEl : function(){
39920         return this.wrapEl || this.el;
39921     },
39922     
39923    
39924     
39925     adjustForComponents : function(width, height)
39926     {
39927         //Roo.log('adjustForComponents ');
39928         if(this.resizeEl != this.el){
39929             width -= this.el.getFrameWidth('lr');
39930             height -= this.el.getFrameWidth('tb');
39931         }
39932         if(this.toolbar){
39933             var te = this.toolbar.getEl();
39934             te.setWidth(width);
39935             height -= te.getHeight();
39936         }
39937         if(this.footer){
39938             var te = this.footer.getEl();
39939             te.setWidth(width);
39940             height -= te.getHeight();
39941         }
39942         
39943         
39944         if(this.adjustments){
39945             width += this.adjustments[0];
39946             height += this.adjustments[1];
39947         }
39948         return {"width": width, "height": height};
39949     },
39950     
39951     setSize : function(width, height){
39952         if(this.fitToFrame && !this.ignoreResize(width, height)){
39953             if(this.fitContainer && this.resizeEl != this.el){
39954                 this.el.setSize(width, height);
39955             }
39956             var size = this.adjustForComponents(width, height);
39957             if (this.iframe) {
39958                 this.iframeEl.setSize(width,height);
39959             }
39960             
39961             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39962             this.fireEvent('resize', this, size.width, size.height);
39963             
39964             
39965         }
39966     },
39967     
39968     /**
39969      * Returns this panel's title
39970      * @return {String} 
39971      */
39972     getTitle : function(){
39973         
39974         if (typeof(this.title) != 'object') {
39975             return this.title;
39976         }
39977         
39978         var t = '';
39979         for (var k in this.title) {
39980             if (!this.title.hasOwnProperty(k)) {
39981                 continue;
39982             }
39983             
39984             if (k.indexOf('-') >= 0) {
39985                 var s = k.split('-');
39986                 for (var i = 0; i<s.length; i++) {
39987                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39988                 }
39989             } else {
39990                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39991             }
39992         }
39993         return t;
39994     },
39995     
39996     /**
39997      * Set this panel's title
39998      * @param {String} title
39999      */
40000     setTitle : function(title){
40001         this.title = title;
40002         if(this.region){
40003             this.region.updatePanelTitle(this, title);
40004         }
40005     },
40006     
40007     /**
40008      * Returns true is this panel was configured to be closable
40009      * @return {Boolean} 
40010      */
40011     isClosable : function(){
40012         return this.closable;
40013     },
40014     
40015     beforeSlide : function(){
40016         this.el.clip();
40017         this.resizeEl.clip();
40018     },
40019     
40020     afterSlide : function(){
40021         this.el.unclip();
40022         this.resizeEl.unclip();
40023     },
40024     
40025     /**
40026      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40027      *   Will fail silently if the {@link #setUrl} method has not been called.
40028      *   This does not activate the panel, just updates its content.
40029      */
40030     refresh : function(){
40031         if(this.refreshDelegate){
40032            this.loaded = false;
40033            this.refreshDelegate();
40034         }
40035     },
40036     
40037     /**
40038      * Destroys this panel
40039      */
40040     destroy : function(){
40041         this.el.removeAllListeners();
40042         var tempEl = document.createElement("span");
40043         tempEl.appendChild(this.el.dom);
40044         tempEl.innerHTML = "";
40045         this.el.remove();
40046         this.el = null;
40047     },
40048     
40049     /**
40050      * form - if the content panel contains a form - this is a reference to it.
40051      * @type {Roo.form.Form}
40052      */
40053     form : false,
40054     /**
40055      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40056      *    This contains a reference to it.
40057      * @type {Roo.View}
40058      */
40059     view : false,
40060     
40061       /**
40062      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40063      * <pre><code>
40064
40065 layout.addxtype({
40066        xtype : 'Form',
40067        items: [ .... ]
40068    }
40069 );
40070
40071 </code></pre>
40072      * @param {Object} cfg Xtype definition of item to add.
40073      */
40074     
40075     
40076     getChildContainer: function () {
40077         return this.getEl();
40078     }
40079     
40080     
40081     /*
40082         var  ret = new Roo.factory(cfg);
40083         return ret;
40084         
40085         
40086         // add form..
40087         if (cfg.xtype.match(/^Form$/)) {
40088             
40089             var el;
40090             //if (this.footer) {
40091             //    el = this.footer.container.insertSibling(false, 'before');
40092             //} else {
40093                 el = this.el.createChild();
40094             //}
40095
40096             this.form = new  Roo.form.Form(cfg);
40097             
40098             
40099             if ( this.form.allItems.length) {
40100                 this.form.render(el.dom);
40101             }
40102             return this.form;
40103         }
40104         // should only have one of theses..
40105         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40106             // views.. should not be just added - used named prop 'view''
40107             
40108             cfg.el = this.el.appendChild(document.createElement("div"));
40109             // factory?
40110             
40111             var ret = new Roo.factory(cfg);
40112              
40113              ret.render && ret.render(false, ''); // render blank..
40114             this.view = ret;
40115             return ret;
40116         }
40117         return false;
40118     }
40119     \*/
40120 });
40121  
40122 /**
40123  * @class Roo.bootstrap.panel.Grid
40124  * @extends Roo.bootstrap.panel.Content
40125  * @constructor
40126  * Create a new GridPanel.
40127  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40128  * @param {Object} config A the config object
40129   
40130  */
40131
40132
40133
40134 Roo.bootstrap.panel.Grid = function(config)
40135 {
40136     
40137       
40138     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40139         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40140
40141     config.el = this.wrapper;
40142     //this.el = this.wrapper;
40143     
40144       if (config.container) {
40145         // ctor'ed from a Border/panel.grid
40146         
40147         
40148         this.wrapper.setStyle("overflow", "hidden");
40149         this.wrapper.addClass('roo-grid-container');
40150
40151     }
40152     
40153     
40154     if(config.toolbar){
40155         var tool_el = this.wrapper.createChild();    
40156         this.toolbar = Roo.factory(config.toolbar);
40157         var ti = [];
40158         if (config.toolbar.items) {
40159             ti = config.toolbar.items ;
40160             delete config.toolbar.items ;
40161         }
40162         
40163         var nitems = [];
40164         this.toolbar.render(tool_el);
40165         for(var i =0;i < ti.length;i++) {
40166           //  Roo.log(['add child', items[i]]);
40167             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40168         }
40169         this.toolbar.items = nitems;
40170         
40171         delete config.toolbar;
40172     }
40173     
40174     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40175     config.grid.scrollBody = true;;
40176     config.grid.monitorWindowResize = false; // turn off autosizing
40177     config.grid.autoHeight = false;
40178     config.grid.autoWidth = false;
40179     
40180     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40181     
40182     if (config.background) {
40183         // render grid on panel activation (if panel background)
40184         this.on('activate', function(gp) {
40185             if (!gp.grid.rendered) {
40186                 gp.grid.render(this.wrapper);
40187                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40188             }
40189         });
40190             
40191     } else {
40192         this.grid.render(this.wrapper);
40193         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40194
40195     }
40196     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40197     // ??? needed ??? config.el = this.wrapper;
40198     
40199     
40200     
40201   
40202     // xtype created footer. - not sure if will work as we normally have to render first..
40203     if (this.footer && !this.footer.el && this.footer.xtype) {
40204         
40205         var ctr = this.grid.getView().getFooterPanel(true);
40206         this.footer.dataSource = this.grid.dataSource;
40207         this.footer = Roo.factory(this.footer, Roo);
40208         this.footer.render(ctr);
40209         
40210     }
40211     
40212     
40213     
40214     
40215      
40216 };
40217
40218 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40219     getId : function(){
40220         return this.grid.id;
40221     },
40222     
40223     /**
40224      * Returns the grid for this panel
40225      * @return {Roo.bootstrap.Table} 
40226      */
40227     getGrid : function(){
40228         return this.grid;    
40229     },
40230     
40231     setSize : function(width, height){
40232         if(!this.ignoreResize(width, height)){
40233             var grid = this.grid;
40234             var size = this.adjustForComponents(width, height);
40235             // tfoot is not a footer?
40236           
40237             
40238             var gridel = grid.getGridEl();
40239             gridel.setSize(size.width, size.height);
40240             
40241             var tbd = grid.getGridEl().select('tbody', true).first();
40242             var thd = grid.getGridEl().select('thead',true).first();
40243             var tbf= grid.getGridEl().select('tfoot', true).first();
40244
40245             if (tbf) {
40246                 size.height -= tbf.getHeight();
40247             }
40248             if (thd) {
40249                 size.height -= thd.getHeight();
40250             }
40251             
40252             tbd.setSize(size.width, size.height );
40253             // this is for the account management tab -seems to work there.
40254             var thd = grid.getGridEl().select('thead',true).first();
40255             //if (tbd) {
40256             //    tbd.setSize(size.width, size.height - thd.getHeight());
40257             //}
40258              
40259             grid.autoSize();
40260         }
40261     },
40262      
40263     
40264     
40265     beforeSlide : function(){
40266         this.grid.getView().scroller.clip();
40267     },
40268     
40269     afterSlide : function(){
40270         this.grid.getView().scroller.unclip();
40271     },
40272     
40273     destroy : function(){
40274         this.grid.destroy();
40275         delete this.grid;
40276         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40277     }
40278 });
40279
40280 /**
40281  * @class Roo.bootstrap.panel.Nest
40282  * @extends Roo.bootstrap.panel.Content
40283  * @constructor
40284  * Create a new Panel, that can contain a layout.Border.
40285  * 
40286  * 
40287  * @param {Roo.BorderLayout} layout The layout for this panel
40288  * @param {String/Object} config A string to set only the title or a config object
40289  */
40290 Roo.bootstrap.panel.Nest = function(config)
40291 {
40292     // construct with only one argument..
40293     /* FIXME - implement nicer consturctors
40294     if (layout.layout) {
40295         config = layout;
40296         layout = config.layout;
40297         delete config.layout;
40298     }
40299     if (layout.xtype && !layout.getEl) {
40300         // then layout needs constructing..
40301         layout = Roo.factory(layout, Roo);
40302     }
40303     */
40304     
40305     config.el =  config.layout.getEl();
40306     
40307     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40308     
40309     config.layout.monitorWindowResize = false; // turn off autosizing
40310     this.layout = config.layout;
40311     this.layout.getEl().addClass("roo-layout-nested-layout");
40312     this.layout.parent = this;
40313     
40314     
40315     
40316     
40317 };
40318
40319 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40320
40321     setSize : function(width, height){
40322         if(!this.ignoreResize(width, height)){
40323             var size = this.adjustForComponents(width, height);
40324             var el = this.layout.getEl();
40325             if (size.height < 1) {
40326                 el.setWidth(size.width);   
40327             } else {
40328                 el.setSize(size.width, size.height);
40329             }
40330             var touch = el.dom.offsetWidth;
40331             this.layout.layout();
40332             // ie requires a double layout on the first pass
40333             if(Roo.isIE && !this.initialized){
40334                 this.initialized = true;
40335                 this.layout.layout();
40336             }
40337         }
40338     },
40339     
40340     // activate all subpanels if not currently active..
40341     
40342     setActiveState : function(active){
40343         this.active = active;
40344         this.setActiveClass(active);
40345         
40346         if(!active){
40347             this.fireEvent("deactivate", this);
40348             return;
40349         }
40350         
40351         this.fireEvent("activate", this);
40352         // not sure if this should happen before or after..
40353         if (!this.layout) {
40354             return; // should not happen..
40355         }
40356         var reg = false;
40357         for (var r in this.layout.regions) {
40358             reg = this.layout.getRegion(r);
40359             if (reg.getActivePanel()) {
40360                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40361                 reg.setActivePanel(reg.getActivePanel());
40362                 continue;
40363             }
40364             if (!reg.panels.length) {
40365                 continue;
40366             }
40367             reg.showPanel(reg.getPanel(0));
40368         }
40369         
40370         
40371         
40372         
40373     },
40374     
40375     /**
40376      * Returns the nested BorderLayout for this panel
40377      * @return {Roo.BorderLayout} 
40378      */
40379     getLayout : function(){
40380         return this.layout;
40381     },
40382     
40383      /**
40384      * Adds a xtype elements to the layout of the nested panel
40385      * <pre><code>
40386
40387 panel.addxtype({
40388        xtype : 'ContentPanel',
40389        region: 'west',
40390        items: [ .... ]
40391    }
40392 );
40393
40394 panel.addxtype({
40395         xtype : 'NestedLayoutPanel',
40396         region: 'west',
40397         layout: {
40398            center: { },
40399            west: { }   
40400         },
40401         items : [ ... list of content panels or nested layout panels.. ]
40402    }
40403 );
40404 </code></pre>
40405      * @param {Object} cfg Xtype definition of item to add.
40406      */
40407     addxtype : function(cfg) {
40408         return this.layout.addxtype(cfg);
40409     
40410     }
40411 });/*
40412  * Based on:
40413  * Ext JS Library 1.1.1
40414  * Copyright(c) 2006-2007, Ext JS, LLC.
40415  *
40416  * Originally Released Under LGPL - original licence link has changed is not relivant.
40417  *
40418  * Fork - LGPL
40419  * <script type="text/javascript">
40420  */
40421 /**
40422  * @class Roo.TabPanel
40423  * @extends Roo.util.Observable
40424  * A lightweight tab container.
40425  * <br><br>
40426  * Usage:
40427  * <pre><code>
40428 // basic tabs 1, built from existing content
40429 var tabs = new Roo.TabPanel("tabs1");
40430 tabs.addTab("script", "View Script");
40431 tabs.addTab("markup", "View Markup");
40432 tabs.activate("script");
40433
40434 // more advanced tabs, built from javascript
40435 var jtabs = new Roo.TabPanel("jtabs");
40436 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40437
40438 // set up the UpdateManager
40439 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40440 var updater = tab2.getUpdateManager();
40441 updater.setDefaultUrl("ajax1.htm");
40442 tab2.on('activate', updater.refresh, updater, true);
40443
40444 // Use setUrl for Ajax loading
40445 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40446 tab3.setUrl("ajax2.htm", null, true);
40447
40448 // Disabled tab
40449 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40450 tab4.disable();
40451
40452 jtabs.activate("jtabs-1");
40453  * </code></pre>
40454  * @constructor
40455  * Create a new TabPanel.
40456  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40457  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40458  */
40459 Roo.bootstrap.panel.Tabs = function(config){
40460     /**
40461     * The container element for this TabPanel.
40462     * @type Roo.Element
40463     */
40464     this.el = Roo.get(config.el);
40465     delete config.el;
40466     if(config){
40467         if(typeof config == "boolean"){
40468             this.tabPosition = config ? "bottom" : "top";
40469         }else{
40470             Roo.apply(this, config);
40471         }
40472     }
40473     
40474     if(this.tabPosition == "bottom"){
40475         // if tabs are at the bottom = create the body first.
40476         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40477         this.el.addClass("roo-tabs-bottom");
40478     }
40479     // next create the tabs holders
40480     
40481     if (this.tabPosition == "west"){
40482         
40483         var reg = this.region; // fake it..
40484         while (reg) {
40485             if (!reg.mgr.parent) {
40486                 break;
40487             }
40488             reg = reg.mgr.parent.region;
40489         }
40490         Roo.log("got nest?");
40491         Roo.log(reg);
40492         if (reg.mgr.getRegion('west')) {
40493             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40494             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40495             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40496             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40497             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40498         
40499             
40500         }
40501         
40502         
40503     } else {
40504      
40505         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40506         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40507         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40508         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40509     }
40510     
40511     
40512     if(Roo.isIE){
40513         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40514     }
40515     
40516     // finally - if tabs are at the top, then create the body last..
40517     if(this.tabPosition != "bottom"){
40518         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40519          * @type Roo.Element
40520          */
40521         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40522         this.el.addClass("roo-tabs-top");
40523     }
40524     this.items = [];
40525
40526     this.bodyEl.setStyle("position", "relative");
40527
40528     this.active = null;
40529     this.activateDelegate = this.activate.createDelegate(this);
40530
40531     this.addEvents({
40532         /**
40533          * @event tabchange
40534          * Fires when the active tab changes
40535          * @param {Roo.TabPanel} this
40536          * @param {Roo.TabPanelItem} activePanel The new active tab
40537          */
40538         "tabchange": true,
40539         /**
40540          * @event beforetabchange
40541          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40542          * @param {Roo.TabPanel} this
40543          * @param {Object} e Set cancel to true on this object to cancel the tab change
40544          * @param {Roo.TabPanelItem} tab The tab being changed to
40545          */
40546         "beforetabchange" : true
40547     });
40548
40549     Roo.EventManager.onWindowResize(this.onResize, this);
40550     this.cpad = this.el.getPadding("lr");
40551     this.hiddenCount = 0;
40552
40553
40554     // toolbar on the tabbar support...
40555     if (this.toolbar) {
40556         alert("no toolbar support yet");
40557         this.toolbar  = false;
40558         /*
40559         var tcfg = this.toolbar;
40560         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40561         this.toolbar = new Roo.Toolbar(tcfg);
40562         if (Roo.isSafari) {
40563             var tbl = tcfg.container.child('table', true);
40564             tbl.setAttribute('width', '100%');
40565         }
40566         */
40567         
40568     }
40569    
40570
40571
40572     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40573 };
40574
40575 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40576     /*
40577      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40578      */
40579     tabPosition : "top",
40580     /*
40581      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40582      */
40583     currentTabWidth : 0,
40584     /*
40585      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40586      */
40587     minTabWidth : 40,
40588     /*
40589      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40590      */
40591     maxTabWidth : 250,
40592     /*
40593      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40594      */
40595     preferredTabWidth : 175,
40596     /*
40597      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40598      */
40599     resizeTabs : false,
40600     /*
40601      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40602      */
40603     monitorResize : true,
40604     /*
40605      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40606      */
40607     toolbar : false,  // set by caller..
40608     
40609     region : false, /// set by caller
40610     
40611     disableTooltips : true, // not used yet...
40612
40613     /**
40614      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40615      * @param {String} id The id of the div to use <b>or create</b>
40616      * @param {String} text The text for the tab
40617      * @param {String} content (optional) Content to put in the TabPanelItem body
40618      * @param {Boolean} closable (optional) True to create a close icon on the tab
40619      * @return {Roo.TabPanelItem} The created TabPanelItem
40620      */
40621     addTab : function(id, text, content, closable, tpl)
40622     {
40623         var item = new Roo.bootstrap.panel.TabItem({
40624             panel: this,
40625             id : id,
40626             text : text,
40627             closable : closable,
40628             tpl : tpl
40629         });
40630         this.addTabItem(item);
40631         if(content){
40632             item.setContent(content);
40633         }
40634         return item;
40635     },
40636
40637     /**
40638      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40639      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40640      * @return {Roo.TabPanelItem}
40641      */
40642     getTab : function(id){
40643         return this.items[id];
40644     },
40645
40646     /**
40647      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40648      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40649      */
40650     hideTab : function(id){
40651         var t = this.items[id];
40652         if(!t.isHidden()){
40653            t.setHidden(true);
40654            this.hiddenCount++;
40655            this.autoSizeTabs();
40656         }
40657     },
40658
40659     /**
40660      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40661      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40662      */
40663     unhideTab : function(id){
40664         var t = this.items[id];
40665         if(t.isHidden()){
40666            t.setHidden(false);
40667            this.hiddenCount--;
40668            this.autoSizeTabs();
40669         }
40670     },
40671
40672     /**
40673      * Adds an existing {@link Roo.TabPanelItem}.
40674      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40675      */
40676     addTabItem : function(item)
40677     {
40678         this.items[item.id] = item;
40679         this.items.push(item);
40680         this.autoSizeTabs();
40681       //  if(this.resizeTabs){
40682     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40683   //         this.autoSizeTabs();
40684 //        }else{
40685 //            item.autoSize();
40686        // }
40687     },
40688
40689     /**
40690      * Removes a {@link Roo.TabPanelItem}.
40691      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40692      */
40693     removeTab : function(id){
40694         var items = this.items;
40695         var tab = items[id];
40696         if(!tab) { return; }
40697         var index = items.indexOf(tab);
40698         if(this.active == tab && items.length > 1){
40699             var newTab = this.getNextAvailable(index);
40700             if(newTab) {
40701                 newTab.activate();
40702             }
40703         }
40704         this.stripEl.dom.removeChild(tab.pnode.dom);
40705         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40706             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40707         }
40708         items.splice(index, 1);
40709         delete this.items[tab.id];
40710         tab.fireEvent("close", tab);
40711         tab.purgeListeners();
40712         this.autoSizeTabs();
40713     },
40714
40715     getNextAvailable : function(start){
40716         var items = this.items;
40717         var index = start;
40718         // look for a next tab that will slide over to
40719         // replace the one being removed
40720         while(index < items.length){
40721             var item = items[++index];
40722             if(item && !item.isHidden()){
40723                 return item;
40724             }
40725         }
40726         // if one isn't found select the previous tab (on the left)
40727         index = start;
40728         while(index >= 0){
40729             var item = items[--index];
40730             if(item && !item.isHidden()){
40731                 return item;
40732             }
40733         }
40734         return null;
40735     },
40736
40737     /**
40738      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40739      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40740      */
40741     disableTab : function(id){
40742         var tab = this.items[id];
40743         if(tab && this.active != tab){
40744             tab.disable();
40745         }
40746     },
40747
40748     /**
40749      * Enables a {@link Roo.TabPanelItem} that is disabled.
40750      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40751      */
40752     enableTab : function(id){
40753         var tab = this.items[id];
40754         tab.enable();
40755     },
40756
40757     /**
40758      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40759      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40760      * @return {Roo.TabPanelItem} The TabPanelItem.
40761      */
40762     activate : function(id)
40763     {
40764         //Roo.log('activite:'  + id);
40765         
40766         var tab = this.items[id];
40767         if(!tab){
40768             return null;
40769         }
40770         if(tab == this.active || tab.disabled){
40771             return tab;
40772         }
40773         var e = {};
40774         this.fireEvent("beforetabchange", this, e, tab);
40775         if(e.cancel !== true && !tab.disabled){
40776             if(this.active){
40777                 this.active.hide();
40778             }
40779             this.active = this.items[id];
40780             this.active.show();
40781             this.fireEvent("tabchange", this, this.active);
40782         }
40783         return tab;
40784     },
40785
40786     /**
40787      * Gets the active {@link Roo.TabPanelItem}.
40788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40789      */
40790     getActiveTab : function(){
40791         return this.active;
40792     },
40793
40794     /**
40795      * Updates the tab body element to fit the height of the container element
40796      * for overflow scrolling
40797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40798      */
40799     syncHeight : function(targetHeight){
40800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40801         var bm = this.bodyEl.getMargins();
40802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40803         this.bodyEl.setHeight(newHeight);
40804         return newHeight;
40805     },
40806
40807     onResize : function(){
40808         if(this.monitorResize){
40809             this.autoSizeTabs();
40810         }
40811     },
40812
40813     /**
40814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40815      */
40816     beginUpdate : function(){
40817         this.updating = true;
40818     },
40819
40820     /**
40821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40822      */
40823     endUpdate : function(){
40824         this.updating = false;
40825         this.autoSizeTabs();
40826     },
40827
40828     /**
40829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40830      */
40831     autoSizeTabs : function()
40832     {
40833         var count = this.items.length;
40834         var vcount = count - this.hiddenCount;
40835         
40836         if (vcount < 2) {
40837             this.stripEl.hide();
40838         } else {
40839             this.stripEl.show();
40840         }
40841         
40842         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40843             return;
40844         }
40845         
40846         
40847         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40848         var availWidth = Math.floor(w / vcount);
40849         var b = this.stripBody;
40850         if(b.getWidth() > w){
40851             var tabs = this.items;
40852             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40853             if(availWidth < this.minTabWidth){
40854                 /*if(!this.sleft){    // incomplete scrolling code
40855                     this.createScrollButtons();
40856                 }
40857                 this.showScroll();
40858                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40859             }
40860         }else{
40861             if(this.currentTabWidth < this.preferredTabWidth){
40862                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40863             }
40864         }
40865     },
40866
40867     /**
40868      * Returns the number of tabs in this TabPanel.
40869      * @return {Number}
40870      */
40871      getCount : function(){
40872          return this.items.length;
40873      },
40874
40875     /**
40876      * Resizes all the tabs to the passed width
40877      * @param {Number} The new width
40878      */
40879     setTabWidth : function(width){
40880         this.currentTabWidth = width;
40881         for(var i = 0, len = this.items.length; i < len; i++) {
40882                 if(!this.items[i].isHidden()) {
40883                 this.items[i].setWidth(width);
40884             }
40885         }
40886     },
40887
40888     /**
40889      * Destroys this TabPanel
40890      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40891      */
40892     destroy : function(removeEl){
40893         Roo.EventManager.removeResizeListener(this.onResize, this);
40894         for(var i = 0, len = this.items.length; i < len; i++){
40895             this.items[i].purgeListeners();
40896         }
40897         if(removeEl === true){
40898             this.el.update("");
40899             this.el.remove();
40900         }
40901     },
40902     
40903     createStrip : function(container)
40904     {
40905         var strip = document.createElement("nav");
40906         strip.className = Roo.bootstrap.version == 4 ?
40907             "navbar-light bg-light" : 
40908             "navbar navbar-default"; //"x-tabs-wrap";
40909         container.appendChild(strip);
40910         return strip;
40911     },
40912     
40913     createStripList : function(strip)
40914     {
40915         // div wrapper for retard IE
40916         // returns the "tr" element.
40917         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40918         //'<div class="x-tabs-strip-wrap">'+
40919           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40920           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40921         return strip.firstChild; //.firstChild.firstChild.firstChild;
40922     },
40923     createBody : function(container)
40924     {
40925         var body = document.createElement("div");
40926         Roo.id(body, "tab-body");
40927         //Roo.fly(body).addClass("x-tabs-body");
40928         Roo.fly(body).addClass("tab-content");
40929         container.appendChild(body);
40930         return body;
40931     },
40932     createItemBody :function(bodyEl, id){
40933         var body = Roo.getDom(id);
40934         if(!body){
40935             body = document.createElement("div");
40936             body.id = id;
40937         }
40938         //Roo.fly(body).addClass("x-tabs-item-body");
40939         Roo.fly(body).addClass("tab-pane");
40940          bodyEl.insertBefore(body, bodyEl.firstChild);
40941         return body;
40942     },
40943     /** @private */
40944     createStripElements :  function(stripEl, text, closable, tpl)
40945     {
40946         var td = document.createElement("li"); // was td..
40947         td.className = 'nav-item';
40948         
40949         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40950         
40951         
40952         stripEl.appendChild(td);
40953         /*if(closable){
40954             td.className = "x-tabs-closable";
40955             if(!this.closeTpl){
40956                 this.closeTpl = new Roo.Template(
40957                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40958                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40959                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40960                 );
40961             }
40962             var el = this.closeTpl.overwrite(td, {"text": text});
40963             var close = el.getElementsByTagName("div")[0];
40964             var inner = el.getElementsByTagName("em")[0];
40965             return {"el": el, "close": close, "inner": inner};
40966         } else {
40967         */
40968         // not sure what this is..
40969 //            if(!this.tabTpl){
40970                 //this.tabTpl = new Roo.Template(
40971                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40972                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40973                 //);
40974 //                this.tabTpl = new Roo.Template(
40975 //                   '<a href="#">' +
40976 //                   '<span unselectable="on"' +
40977 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40978 //                            ' >{text}</span></a>'
40979 //                );
40980 //                
40981 //            }
40982
40983
40984             var template = tpl || this.tabTpl || false;
40985             
40986             if(!template){
40987                 template =  new Roo.Template(
40988                         Roo.bootstrap.version == 4 ? 
40989                             (
40990                                 '<a class="nav-link" href="#" unselectable="on"' +
40991                                      (this.disableTooltips ? '' : ' title="{text}"') +
40992                                      ' >{text}</a>'
40993                             ) : (
40994                                 '<a class="nav-link" href="#">' +
40995                                 '<span unselectable="on"' +
40996                                          (this.disableTooltips ? '' : ' title="{text}"') +
40997                                     ' >{text}</span></a>'
40998                             )
40999                 );
41000             }
41001             
41002             switch (typeof(template)) {
41003                 case 'object' :
41004                     break;
41005                 case 'string' :
41006                     template = new Roo.Template(template);
41007                     break;
41008                 default :
41009                     break;
41010             }
41011             
41012             var el = template.overwrite(td, {"text": text});
41013             
41014             var inner = el.getElementsByTagName("span")[0];
41015             
41016             return {"el": el, "inner": inner};
41017             
41018     }
41019         
41020     
41021 });
41022
41023 /**
41024  * @class Roo.TabPanelItem
41025  * @extends Roo.util.Observable
41026  * Represents an individual item (tab plus body) in a TabPanel.
41027  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41028  * @param {String} id The id of this TabPanelItem
41029  * @param {String} text The text for the tab of this TabPanelItem
41030  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41031  */
41032 Roo.bootstrap.panel.TabItem = function(config){
41033     /**
41034      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41035      * @type Roo.TabPanel
41036      */
41037     this.tabPanel = config.panel;
41038     /**
41039      * The id for this TabPanelItem
41040      * @type String
41041      */
41042     this.id = config.id;
41043     /** @private */
41044     this.disabled = false;
41045     /** @private */
41046     this.text = config.text;
41047     /** @private */
41048     this.loaded = false;
41049     this.closable = config.closable;
41050
41051     /**
41052      * The body element for this TabPanelItem.
41053      * @type Roo.Element
41054      */
41055     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41056     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41057     this.bodyEl.setStyle("display", "block");
41058     this.bodyEl.setStyle("zoom", "1");
41059     //this.hideAction();
41060
41061     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41062     /** @private */
41063     this.el = Roo.get(els.el);
41064     this.inner = Roo.get(els.inner, true);
41065      this.textEl = Roo.bootstrap.version == 4 ?
41066         this.el : Roo.get(this.el.dom.firstChild, true);
41067
41068     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41069     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41070
41071     
41072 //    this.el.on("mousedown", this.onTabMouseDown, this);
41073     this.el.on("click", this.onTabClick, this);
41074     /** @private */
41075     if(config.closable){
41076         var c = Roo.get(els.close, true);
41077         c.dom.title = this.closeText;
41078         c.addClassOnOver("close-over");
41079         c.on("click", this.closeClick, this);
41080      }
41081
41082     this.addEvents({
41083          /**
41084          * @event activate
41085          * Fires when this tab becomes the active tab.
41086          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41087          * @param {Roo.TabPanelItem} this
41088          */
41089         "activate": true,
41090         /**
41091          * @event beforeclose
41092          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41093          * @param {Roo.TabPanelItem} this
41094          * @param {Object} e Set cancel to true on this object to cancel the close.
41095          */
41096         "beforeclose": true,
41097         /**
41098          * @event close
41099          * Fires when this tab is closed.
41100          * @param {Roo.TabPanelItem} this
41101          */
41102          "close": true,
41103         /**
41104          * @event deactivate
41105          * Fires when this tab is no longer the active tab.
41106          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41107          * @param {Roo.TabPanelItem} this
41108          */
41109          "deactivate" : true
41110     });
41111     this.hidden = false;
41112
41113     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41114 };
41115
41116 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41117            {
41118     purgeListeners : function(){
41119        Roo.util.Observable.prototype.purgeListeners.call(this);
41120        this.el.removeAllListeners();
41121     },
41122     /**
41123      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41124      */
41125     show : function(){
41126         this.status_node.addClass("active");
41127         this.showAction();
41128         if(Roo.isOpera){
41129             this.tabPanel.stripWrap.repaint();
41130         }
41131         this.fireEvent("activate", this.tabPanel, this);
41132     },
41133
41134     /**
41135      * Returns true if this tab is the active tab.
41136      * @return {Boolean}
41137      */
41138     isActive : function(){
41139         return this.tabPanel.getActiveTab() == this;
41140     },
41141
41142     /**
41143      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41144      */
41145     hide : function(){
41146         this.status_node.removeClass("active");
41147         this.hideAction();
41148         this.fireEvent("deactivate", this.tabPanel, this);
41149     },
41150
41151     hideAction : function(){
41152         this.bodyEl.hide();
41153         this.bodyEl.setStyle("position", "absolute");
41154         this.bodyEl.setLeft("-20000px");
41155         this.bodyEl.setTop("-20000px");
41156     },
41157
41158     showAction : function(){
41159         this.bodyEl.setStyle("position", "relative");
41160         this.bodyEl.setTop("");
41161         this.bodyEl.setLeft("");
41162         this.bodyEl.show();
41163     },
41164
41165     /**
41166      * Set the tooltip for the tab.
41167      * @param {String} tooltip The tab's tooltip
41168      */
41169     setTooltip : function(text){
41170         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41171             this.textEl.dom.qtip = text;
41172             this.textEl.dom.removeAttribute('title');
41173         }else{
41174             this.textEl.dom.title = text;
41175         }
41176     },
41177
41178     onTabClick : function(e){
41179         e.preventDefault();
41180         this.tabPanel.activate(this.id);
41181     },
41182
41183     onTabMouseDown : function(e){
41184         e.preventDefault();
41185         this.tabPanel.activate(this.id);
41186     },
41187 /*
41188     getWidth : function(){
41189         return this.inner.getWidth();
41190     },
41191
41192     setWidth : function(width){
41193         var iwidth = width - this.linode.getPadding("lr");
41194         this.inner.setWidth(iwidth);
41195         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41196         this.linode.setWidth(width);
41197     },
41198 */
41199     /**
41200      * Show or hide the tab
41201      * @param {Boolean} hidden True to hide or false to show.
41202      */
41203     setHidden : function(hidden){
41204         this.hidden = hidden;
41205         this.linode.setStyle("display", hidden ? "none" : "");
41206     },
41207
41208     /**
41209      * Returns true if this tab is "hidden"
41210      * @return {Boolean}
41211      */
41212     isHidden : function(){
41213         return this.hidden;
41214     },
41215
41216     /**
41217      * Returns the text for this tab
41218      * @return {String}
41219      */
41220     getText : function(){
41221         return this.text;
41222     },
41223     /*
41224     autoSize : function(){
41225         //this.el.beginMeasure();
41226         this.textEl.setWidth(1);
41227         /*
41228          *  #2804 [new] Tabs in Roojs
41229          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41230          */
41231         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41232         //this.el.endMeasure();
41233     //},
41234
41235     /**
41236      * Sets the text for the tab (Note: this also sets the tooltip text)
41237      * @param {String} text The tab's text and tooltip
41238      */
41239     setText : function(text){
41240         this.text = text;
41241         this.textEl.update(text);
41242         this.setTooltip(text);
41243         //if(!this.tabPanel.resizeTabs){
41244         //    this.autoSize();
41245         //}
41246     },
41247     /**
41248      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41249      */
41250     activate : function(){
41251         this.tabPanel.activate(this.id);
41252     },
41253
41254     /**
41255      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41256      */
41257     disable : function(){
41258         if(this.tabPanel.active != this){
41259             this.disabled = true;
41260             this.status_node.addClass("disabled");
41261         }
41262     },
41263
41264     /**
41265      * Enables this TabPanelItem if it was previously disabled.
41266      */
41267     enable : function(){
41268         this.disabled = false;
41269         this.status_node.removeClass("disabled");
41270     },
41271
41272     /**
41273      * Sets the content for this TabPanelItem.
41274      * @param {String} content The content
41275      * @param {Boolean} loadScripts true to look for and load scripts
41276      */
41277     setContent : function(content, loadScripts){
41278         this.bodyEl.update(content, loadScripts);
41279     },
41280
41281     /**
41282      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41283      * @return {Roo.UpdateManager} The UpdateManager
41284      */
41285     getUpdateManager : function(){
41286         return this.bodyEl.getUpdateManager();
41287     },
41288
41289     /**
41290      * Set a URL to be used to load the content for this TabPanelItem.
41291      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41292      * @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)
41293      * @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)
41294      * @return {Roo.UpdateManager} The UpdateManager
41295      */
41296     setUrl : function(url, params, loadOnce){
41297         if(this.refreshDelegate){
41298             this.un('activate', this.refreshDelegate);
41299         }
41300         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41301         this.on("activate", this.refreshDelegate);
41302         return this.bodyEl.getUpdateManager();
41303     },
41304
41305     /** @private */
41306     _handleRefresh : function(url, params, loadOnce){
41307         if(!loadOnce || !this.loaded){
41308             var updater = this.bodyEl.getUpdateManager();
41309             updater.update(url, params, this._setLoaded.createDelegate(this));
41310         }
41311     },
41312
41313     /**
41314      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41315      *   Will fail silently if the setUrl method has not been called.
41316      *   This does not activate the panel, just updates its content.
41317      */
41318     refresh : function(){
41319         if(this.refreshDelegate){
41320            this.loaded = false;
41321            this.refreshDelegate();
41322         }
41323     },
41324
41325     /** @private */
41326     _setLoaded : function(){
41327         this.loaded = true;
41328     },
41329
41330     /** @private */
41331     closeClick : function(e){
41332         var o = {};
41333         e.stopEvent();
41334         this.fireEvent("beforeclose", this, o);
41335         if(o.cancel !== true){
41336             this.tabPanel.removeTab(this.id);
41337         }
41338     },
41339     /**
41340      * The text displayed in the tooltip for the close icon.
41341      * @type String
41342      */
41343     closeText : "Close this tab"
41344 });
41345 /**
41346 *    This script refer to:
41347 *    Title: International Telephone Input
41348 *    Author: Jack O'Connor
41349 *    Code version:  v12.1.12
41350 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41351 **/
41352
41353 Roo.bootstrap.PhoneInputData = function() {
41354     var d = [
41355       [
41356         "Afghanistan (‫افغانستان‬‎)",
41357         "af",
41358         "93"
41359       ],
41360       [
41361         "Albania (Shqipëri)",
41362         "al",
41363         "355"
41364       ],
41365       [
41366         "Algeria (‫الجزائر‬‎)",
41367         "dz",
41368         "213"
41369       ],
41370       [
41371         "American Samoa",
41372         "as",
41373         "1684"
41374       ],
41375       [
41376         "Andorra",
41377         "ad",
41378         "376"
41379       ],
41380       [
41381         "Angola",
41382         "ao",
41383         "244"
41384       ],
41385       [
41386         "Anguilla",
41387         "ai",
41388         "1264"
41389       ],
41390       [
41391         "Antigua and Barbuda",
41392         "ag",
41393         "1268"
41394       ],
41395       [
41396         "Argentina",
41397         "ar",
41398         "54"
41399       ],
41400       [
41401         "Armenia (Հայաստան)",
41402         "am",
41403         "374"
41404       ],
41405       [
41406         "Aruba",
41407         "aw",
41408         "297"
41409       ],
41410       [
41411         "Australia",
41412         "au",
41413         "61",
41414         0
41415       ],
41416       [
41417         "Austria (Österreich)",
41418         "at",
41419         "43"
41420       ],
41421       [
41422         "Azerbaijan (Azərbaycan)",
41423         "az",
41424         "994"
41425       ],
41426       [
41427         "Bahamas",
41428         "bs",
41429         "1242"
41430       ],
41431       [
41432         "Bahrain (‫البحرين‬‎)",
41433         "bh",
41434         "973"
41435       ],
41436       [
41437         "Bangladesh (বাংলাদেশ)",
41438         "bd",
41439         "880"
41440       ],
41441       [
41442         "Barbados",
41443         "bb",
41444         "1246"
41445       ],
41446       [
41447         "Belarus (Беларусь)",
41448         "by",
41449         "375"
41450       ],
41451       [
41452         "Belgium (België)",
41453         "be",
41454         "32"
41455       ],
41456       [
41457         "Belize",
41458         "bz",
41459         "501"
41460       ],
41461       [
41462         "Benin (Bénin)",
41463         "bj",
41464         "229"
41465       ],
41466       [
41467         "Bermuda",
41468         "bm",
41469         "1441"
41470       ],
41471       [
41472         "Bhutan (འབྲུག)",
41473         "bt",
41474         "975"
41475       ],
41476       [
41477         "Bolivia",
41478         "bo",
41479         "591"
41480       ],
41481       [
41482         "Bosnia and Herzegovina (Босна и Херцеговина)",
41483         "ba",
41484         "387"
41485       ],
41486       [
41487         "Botswana",
41488         "bw",
41489         "267"
41490       ],
41491       [
41492         "Brazil (Brasil)",
41493         "br",
41494         "55"
41495       ],
41496       [
41497         "British Indian Ocean Territory",
41498         "io",
41499         "246"
41500       ],
41501       [
41502         "British Virgin Islands",
41503         "vg",
41504         "1284"
41505       ],
41506       [
41507         "Brunei",
41508         "bn",
41509         "673"
41510       ],
41511       [
41512         "Bulgaria (България)",
41513         "bg",
41514         "359"
41515       ],
41516       [
41517         "Burkina Faso",
41518         "bf",
41519         "226"
41520       ],
41521       [
41522         "Burundi (Uburundi)",
41523         "bi",
41524         "257"
41525       ],
41526       [
41527         "Cambodia (កម្ពុជា)",
41528         "kh",
41529         "855"
41530       ],
41531       [
41532         "Cameroon (Cameroun)",
41533         "cm",
41534         "237"
41535       ],
41536       [
41537         "Canada",
41538         "ca",
41539         "1",
41540         1,
41541         ["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"]
41542       ],
41543       [
41544         "Cape Verde (Kabu Verdi)",
41545         "cv",
41546         "238"
41547       ],
41548       [
41549         "Caribbean Netherlands",
41550         "bq",
41551         "599",
41552         1
41553       ],
41554       [
41555         "Cayman Islands",
41556         "ky",
41557         "1345"
41558       ],
41559       [
41560         "Central African Republic (République centrafricaine)",
41561         "cf",
41562         "236"
41563       ],
41564       [
41565         "Chad (Tchad)",
41566         "td",
41567         "235"
41568       ],
41569       [
41570         "Chile",
41571         "cl",
41572         "56"
41573       ],
41574       [
41575         "China (中国)",
41576         "cn",
41577         "86"
41578       ],
41579       [
41580         "Christmas Island",
41581         "cx",
41582         "61",
41583         2
41584       ],
41585       [
41586         "Cocos (Keeling) Islands",
41587         "cc",
41588         "61",
41589         1
41590       ],
41591       [
41592         "Colombia",
41593         "co",
41594         "57"
41595       ],
41596       [
41597         "Comoros (‫جزر القمر‬‎)",
41598         "km",
41599         "269"
41600       ],
41601       [
41602         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41603         "cd",
41604         "243"
41605       ],
41606       [
41607         "Congo (Republic) (Congo-Brazzaville)",
41608         "cg",
41609         "242"
41610       ],
41611       [
41612         "Cook Islands",
41613         "ck",
41614         "682"
41615       ],
41616       [
41617         "Costa Rica",
41618         "cr",
41619         "506"
41620       ],
41621       [
41622         "Côte d’Ivoire",
41623         "ci",
41624         "225"
41625       ],
41626       [
41627         "Croatia (Hrvatska)",
41628         "hr",
41629         "385"
41630       ],
41631       [
41632         "Cuba",
41633         "cu",
41634         "53"
41635       ],
41636       [
41637         "Curaçao",
41638         "cw",
41639         "599",
41640         0
41641       ],
41642       [
41643         "Cyprus (Κύπρος)",
41644         "cy",
41645         "357"
41646       ],
41647       [
41648         "Czech Republic (Česká republika)",
41649         "cz",
41650         "420"
41651       ],
41652       [
41653         "Denmark (Danmark)",
41654         "dk",
41655         "45"
41656       ],
41657       [
41658         "Djibouti",
41659         "dj",
41660         "253"
41661       ],
41662       [
41663         "Dominica",
41664         "dm",
41665         "1767"
41666       ],
41667       [
41668         "Dominican Republic (República Dominicana)",
41669         "do",
41670         "1",
41671         2,
41672         ["809", "829", "849"]
41673       ],
41674       [
41675         "Ecuador",
41676         "ec",
41677         "593"
41678       ],
41679       [
41680         "Egypt (‫مصر‬‎)",
41681         "eg",
41682         "20"
41683       ],
41684       [
41685         "El Salvador",
41686         "sv",
41687         "503"
41688       ],
41689       [
41690         "Equatorial Guinea (Guinea Ecuatorial)",
41691         "gq",
41692         "240"
41693       ],
41694       [
41695         "Eritrea",
41696         "er",
41697         "291"
41698       ],
41699       [
41700         "Estonia (Eesti)",
41701         "ee",
41702         "372"
41703       ],
41704       [
41705         "Ethiopia",
41706         "et",
41707         "251"
41708       ],
41709       [
41710         "Falkland Islands (Islas Malvinas)",
41711         "fk",
41712         "500"
41713       ],
41714       [
41715         "Faroe Islands (Føroyar)",
41716         "fo",
41717         "298"
41718       ],
41719       [
41720         "Fiji",
41721         "fj",
41722         "679"
41723       ],
41724       [
41725         "Finland (Suomi)",
41726         "fi",
41727         "358",
41728         0
41729       ],
41730       [
41731         "France",
41732         "fr",
41733         "33"
41734       ],
41735       [
41736         "French Guiana (Guyane française)",
41737         "gf",
41738         "594"
41739       ],
41740       [
41741         "French Polynesia (Polynésie française)",
41742         "pf",
41743         "689"
41744       ],
41745       [
41746         "Gabon",
41747         "ga",
41748         "241"
41749       ],
41750       [
41751         "Gambia",
41752         "gm",
41753         "220"
41754       ],
41755       [
41756         "Georgia (საქართველო)",
41757         "ge",
41758         "995"
41759       ],
41760       [
41761         "Germany (Deutschland)",
41762         "de",
41763         "49"
41764       ],
41765       [
41766         "Ghana (Gaana)",
41767         "gh",
41768         "233"
41769       ],
41770       [
41771         "Gibraltar",
41772         "gi",
41773         "350"
41774       ],
41775       [
41776         "Greece (Ελλάδα)",
41777         "gr",
41778         "30"
41779       ],
41780       [
41781         "Greenland (Kalaallit Nunaat)",
41782         "gl",
41783         "299"
41784       ],
41785       [
41786         "Grenada",
41787         "gd",
41788         "1473"
41789       ],
41790       [
41791         "Guadeloupe",
41792         "gp",
41793         "590",
41794         0
41795       ],
41796       [
41797         "Guam",
41798         "gu",
41799         "1671"
41800       ],
41801       [
41802         "Guatemala",
41803         "gt",
41804         "502"
41805       ],
41806       [
41807         "Guernsey",
41808         "gg",
41809         "44",
41810         1
41811       ],
41812       [
41813         "Guinea (Guinée)",
41814         "gn",
41815         "224"
41816       ],
41817       [
41818         "Guinea-Bissau (Guiné Bissau)",
41819         "gw",
41820         "245"
41821       ],
41822       [
41823         "Guyana",
41824         "gy",
41825         "592"
41826       ],
41827       [
41828         "Haiti",
41829         "ht",
41830         "509"
41831       ],
41832       [
41833         "Honduras",
41834         "hn",
41835         "504"
41836       ],
41837       [
41838         "Hong Kong (香港)",
41839         "hk",
41840         "852"
41841       ],
41842       [
41843         "Hungary (Magyarország)",
41844         "hu",
41845         "36"
41846       ],
41847       [
41848         "Iceland (Ísland)",
41849         "is",
41850         "354"
41851       ],
41852       [
41853         "India (भारत)",
41854         "in",
41855         "91"
41856       ],
41857       [
41858         "Indonesia",
41859         "id",
41860         "62"
41861       ],
41862       [
41863         "Iran (‫ایران‬‎)",
41864         "ir",
41865         "98"
41866       ],
41867       [
41868         "Iraq (‫العراق‬‎)",
41869         "iq",
41870         "964"
41871       ],
41872       [
41873         "Ireland",
41874         "ie",
41875         "353"
41876       ],
41877       [
41878         "Isle of Man",
41879         "im",
41880         "44",
41881         2
41882       ],
41883       [
41884         "Israel (‫ישראל‬‎)",
41885         "il",
41886         "972"
41887       ],
41888       [
41889         "Italy (Italia)",
41890         "it",
41891         "39",
41892         0
41893       ],
41894       [
41895         "Jamaica",
41896         "jm",
41897         "1876"
41898       ],
41899       [
41900         "Japan (日本)",
41901         "jp",
41902         "81"
41903       ],
41904       [
41905         "Jersey",
41906         "je",
41907         "44",
41908         3
41909       ],
41910       [
41911         "Jordan (‫الأردن‬‎)",
41912         "jo",
41913         "962"
41914       ],
41915       [
41916         "Kazakhstan (Казахстан)",
41917         "kz",
41918         "7",
41919         1
41920       ],
41921       [
41922         "Kenya",
41923         "ke",
41924         "254"
41925       ],
41926       [
41927         "Kiribati",
41928         "ki",
41929         "686"
41930       ],
41931       [
41932         "Kosovo",
41933         "xk",
41934         "383"
41935       ],
41936       [
41937         "Kuwait (‫الكويت‬‎)",
41938         "kw",
41939         "965"
41940       ],
41941       [
41942         "Kyrgyzstan (Кыргызстан)",
41943         "kg",
41944         "996"
41945       ],
41946       [
41947         "Laos (ລາວ)",
41948         "la",
41949         "856"
41950       ],
41951       [
41952         "Latvia (Latvija)",
41953         "lv",
41954         "371"
41955       ],
41956       [
41957         "Lebanon (‫لبنان‬‎)",
41958         "lb",
41959         "961"
41960       ],
41961       [
41962         "Lesotho",
41963         "ls",
41964         "266"
41965       ],
41966       [
41967         "Liberia",
41968         "lr",
41969         "231"
41970       ],
41971       [
41972         "Libya (‫ليبيا‬‎)",
41973         "ly",
41974         "218"
41975       ],
41976       [
41977         "Liechtenstein",
41978         "li",
41979         "423"
41980       ],
41981       [
41982         "Lithuania (Lietuva)",
41983         "lt",
41984         "370"
41985       ],
41986       [
41987         "Luxembourg",
41988         "lu",
41989         "352"
41990       ],
41991       [
41992         "Macau (澳門)",
41993         "mo",
41994         "853"
41995       ],
41996       [
41997         "Macedonia (FYROM) (Македонија)",
41998         "mk",
41999         "389"
42000       ],
42001       [
42002         "Madagascar (Madagasikara)",
42003         "mg",
42004         "261"
42005       ],
42006       [
42007         "Malawi",
42008         "mw",
42009         "265"
42010       ],
42011       [
42012         "Malaysia",
42013         "my",
42014         "60"
42015       ],
42016       [
42017         "Maldives",
42018         "mv",
42019         "960"
42020       ],
42021       [
42022         "Mali",
42023         "ml",
42024         "223"
42025       ],
42026       [
42027         "Malta",
42028         "mt",
42029         "356"
42030       ],
42031       [
42032         "Marshall Islands",
42033         "mh",
42034         "692"
42035       ],
42036       [
42037         "Martinique",
42038         "mq",
42039         "596"
42040       ],
42041       [
42042         "Mauritania (‫موريتانيا‬‎)",
42043         "mr",
42044         "222"
42045       ],
42046       [
42047         "Mauritius (Moris)",
42048         "mu",
42049         "230"
42050       ],
42051       [
42052         "Mayotte",
42053         "yt",
42054         "262",
42055         1
42056       ],
42057       [
42058         "Mexico (México)",
42059         "mx",
42060         "52"
42061       ],
42062       [
42063         "Micronesia",
42064         "fm",
42065         "691"
42066       ],
42067       [
42068         "Moldova (Republica Moldova)",
42069         "md",
42070         "373"
42071       ],
42072       [
42073         "Monaco",
42074         "mc",
42075         "377"
42076       ],
42077       [
42078         "Mongolia (Монгол)",
42079         "mn",
42080         "976"
42081       ],
42082       [
42083         "Montenegro (Crna Gora)",
42084         "me",
42085         "382"
42086       ],
42087       [
42088         "Montserrat",
42089         "ms",
42090         "1664"
42091       ],
42092       [
42093         "Morocco (‫المغرب‬‎)",
42094         "ma",
42095         "212",
42096         0
42097       ],
42098       [
42099         "Mozambique (Moçambique)",
42100         "mz",
42101         "258"
42102       ],
42103       [
42104         "Myanmar (Burma) (မြန်မာ)",
42105         "mm",
42106         "95"
42107       ],
42108       [
42109         "Namibia (Namibië)",
42110         "na",
42111         "264"
42112       ],
42113       [
42114         "Nauru",
42115         "nr",
42116         "674"
42117       ],
42118       [
42119         "Nepal (नेपाल)",
42120         "np",
42121         "977"
42122       ],
42123       [
42124         "Netherlands (Nederland)",
42125         "nl",
42126         "31"
42127       ],
42128       [
42129         "New Caledonia (Nouvelle-Calédonie)",
42130         "nc",
42131         "687"
42132       ],
42133       [
42134         "New Zealand",
42135         "nz",
42136         "64"
42137       ],
42138       [
42139         "Nicaragua",
42140         "ni",
42141         "505"
42142       ],
42143       [
42144         "Niger (Nijar)",
42145         "ne",
42146         "227"
42147       ],
42148       [
42149         "Nigeria",
42150         "ng",
42151         "234"
42152       ],
42153       [
42154         "Niue",
42155         "nu",
42156         "683"
42157       ],
42158       [
42159         "Norfolk Island",
42160         "nf",
42161         "672"
42162       ],
42163       [
42164         "North Korea (조선 민주주의 인민 공화국)",
42165         "kp",
42166         "850"
42167       ],
42168       [
42169         "Northern Mariana Islands",
42170         "mp",
42171         "1670"
42172       ],
42173       [
42174         "Norway (Norge)",
42175         "no",
42176         "47",
42177         0
42178       ],
42179       [
42180         "Oman (‫عُمان‬‎)",
42181         "om",
42182         "968"
42183       ],
42184       [
42185         "Pakistan (‫پاکستان‬‎)",
42186         "pk",
42187         "92"
42188       ],
42189       [
42190         "Palau",
42191         "pw",
42192         "680"
42193       ],
42194       [
42195         "Palestine (‫فلسطين‬‎)",
42196         "ps",
42197         "970"
42198       ],
42199       [
42200         "Panama (Panamá)",
42201         "pa",
42202         "507"
42203       ],
42204       [
42205         "Papua New Guinea",
42206         "pg",
42207         "675"
42208       ],
42209       [
42210         "Paraguay",
42211         "py",
42212         "595"
42213       ],
42214       [
42215         "Peru (Perú)",
42216         "pe",
42217         "51"
42218       ],
42219       [
42220         "Philippines",
42221         "ph",
42222         "63"
42223       ],
42224       [
42225         "Poland (Polska)",
42226         "pl",
42227         "48"
42228       ],
42229       [
42230         "Portugal",
42231         "pt",
42232         "351"
42233       ],
42234       [
42235         "Puerto Rico",
42236         "pr",
42237         "1",
42238         3,
42239         ["787", "939"]
42240       ],
42241       [
42242         "Qatar (‫قطر‬‎)",
42243         "qa",
42244         "974"
42245       ],
42246       [
42247         "Réunion (La Réunion)",
42248         "re",
42249         "262",
42250         0
42251       ],
42252       [
42253         "Romania (România)",
42254         "ro",
42255         "40"
42256       ],
42257       [
42258         "Russia (Россия)",
42259         "ru",
42260         "7",
42261         0
42262       ],
42263       [
42264         "Rwanda",
42265         "rw",
42266         "250"
42267       ],
42268       [
42269         "Saint Barthélemy",
42270         "bl",
42271         "590",
42272         1
42273       ],
42274       [
42275         "Saint Helena",
42276         "sh",
42277         "290"
42278       ],
42279       [
42280         "Saint Kitts and Nevis",
42281         "kn",
42282         "1869"
42283       ],
42284       [
42285         "Saint Lucia",
42286         "lc",
42287         "1758"
42288       ],
42289       [
42290         "Saint Martin (Saint-Martin (partie française))",
42291         "mf",
42292         "590",
42293         2
42294       ],
42295       [
42296         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42297         "pm",
42298         "508"
42299       ],
42300       [
42301         "Saint Vincent and the Grenadines",
42302         "vc",
42303         "1784"
42304       ],
42305       [
42306         "Samoa",
42307         "ws",
42308         "685"
42309       ],
42310       [
42311         "San Marino",
42312         "sm",
42313         "378"
42314       ],
42315       [
42316         "São Tomé and Príncipe (São Tomé e Príncipe)",
42317         "st",
42318         "239"
42319       ],
42320       [
42321         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42322         "sa",
42323         "966"
42324       ],
42325       [
42326         "Senegal (Sénégal)",
42327         "sn",
42328         "221"
42329       ],
42330       [
42331         "Serbia (Србија)",
42332         "rs",
42333         "381"
42334       ],
42335       [
42336         "Seychelles",
42337         "sc",
42338         "248"
42339       ],
42340       [
42341         "Sierra Leone",
42342         "sl",
42343         "232"
42344       ],
42345       [
42346         "Singapore",
42347         "sg",
42348         "65"
42349       ],
42350       [
42351         "Sint Maarten",
42352         "sx",
42353         "1721"
42354       ],
42355       [
42356         "Slovakia (Slovensko)",
42357         "sk",
42358         "421"
42359       ],
42360       [
42361         "Slovenia (Slovenija)",
42362         "si",
42363         "386"
42364       ],
42365       [
42366         "Solomon Islands",
42367         "sb",
42368         "677"
42369       ],
42370       [
42371         "Somalia (Soomaaliya)",
42372         "so",
42373         "252"
42374       ],
42375       [
42376         "South Africa",
42377         "za",
42378         "27"
42379       ],
42380       [
42381         "South Korea (대한민국)",
42382         "kr",
42383         "82"
42384       ],
42385       [
42386         "South Sudan (‫جنوب السودان‬‎)",
42387         "ss",
42388         "211"
42389       ],
42390       [
42391         "Spain (España)",
42392         "es",
42393         "34"
42394       ],
42395       [
42396         "Sri Lanka (ශ්‍රී ලංකාව)",
42397         "lk",
42398         "94"
42399       ],
42400       [
42401         "Sudan (‫السودان‬‎)",
42402         "sd",
42403         "249"
42404       ],
42405       [
42406         "Suriname",
42407         "sr",
42408         "597"
42409       ],
42410       [
42411         "Svalbard and Jan Mayen",
42412         "sj",
42413         "47",
42414         1
42415       ],
42416       [
42417         "Swaziland",
42418         "sz",
42419         "268"
42420       ],
42421       [
42422         "Sweden (Sverige)",
42423         "se",
42424         "46"
42425       ],
42426       [
42427         "Switzerland (Schweiz)",
42428         "ch",
42429         "41"
42430       ],
42431       [
42432         "Syria (‫سوريا‬‎)",
42433         "sy",
42434         "963"
42435       ],
42436       [
42437         "Taiwan (台灣)",
42438         "tw",
42439         "886"
42440       ],
42441       [
42442         "Tajikistan",
42443         "tj",
42444         "992"
42445       ],
42446       [
42447         "Tanzania",
42448         "tz",
42449         "255"
42450       ],
42451       [
42452         "Thailand (ไทย)",
42453         "th",
42454         "66"
42455       ],
42456       [
42457         "Timor-Leste",
42458         "tl",
42459         "670"
42460       ],
42461       [
42462         "Togo",
42463         "tg",
42464         "228"
42465       ],
42466       [
42467         "Tokelau",
42468         "tk",
42469         "690"
42470       ],
42471       [
42472         "Tonga",
42473         "to",
42474         "676"
42475       ],
42476       [
42477         "Trinidad and Tobago",
42478         "tt",
42479         "1868"
42480       ],
42481       [
42482         "Tunisia (‫تونس‬‎)",
42483         "tn",
42484         "216"
42485       ],
42486       [
42487         "Turkey (Türkiye)",
42488         "tr",
42489         "90"
42490       ],
42491       [
42492         "Turkmenistan",
42493         "tm",
42494         "993"
42495       ],
42496       [
42497         "Turks and Caicos Islands",
42498         "tc",
42499         "1649"
42500       ],
42501       [
42502         "Tuvalu",
42503         "tv",
42504         "688"
42505       ],
42506       [
42507         "U.S. Virgin Islands",
42508         "vi",
42509         "1340"
42510       ],
42511       [
42512         "Uganda",
42513         "ug",
42514         "256"
42515       ],
42516       [
42517         "Ukraine (Україна)",
42518         "ua",
42519         "380"
42520       ],
42521       [
42522         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42523         "ae",
42524         "971"
42525       ],
42526       [
42527         "United Kingdom",
42528         "gb",
42529         "44",
42530         0
42531       ],
42532       [
42533         "United States",
42534         "us",
42535         "1",
42536         0
42537       ],
42538       [
42539         "Uruguay",
42540         "uy",
42541         "598"
42542       ],
42543       [
42544         "Uzbekistan (Oʻzbekiston)",
42545         "uz",
42546         "998"
42547       ],
42548       [
42549         "Vanuatu",
42550         "vu",
42551         "678"
42552       ],
42553       [
42554         "Vatican City (Città del Vaticano)",
42555         "va",
42556         "39",
42557         1
42558       ],
42559       [
42560         "Venezuela",
42561         "ve",
42562         "58"
42563       ],
42564       [
42565         "Vietnam (Việt Nam)",
42566         "vn",
42567         "84"
42568       ],
42569       [
42570         "Wallis and Futuna (Wallis-et-Futuna)",
42571         "wf",
42572         "681"
42573       ],
42574       [
42575         "Western Sahara (‫الصحراء الغربية‬‎)",
42576         "eh",
42577         "212",
42578         1
42579       ],
42580       [
42581         "Yemen (‫اليمن‬‎)",
42582         "ye",
42583         "967"
42584       ],
42585       [
42586         "Zambia",
42587         "zm",
42588         "260"
42589       ],
42590       [
42591         "Zimbabwe",
42592         "zw",
42593         "263"
42594       ],
42595       [
42596         "Åland Islands",
42597         "ax",
42598         "358",
42599         1
42600       ]
42601   ];
42602   
42603   return d;
42604 }/**
42605 *    This script refer to:
42606 *    Title: International Telephone Input
42607 *    Author: Jack O'Connor
42608 *    Code version:  v12.1.12
42609 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42610 **/
42611
42612 /**
42613  * @class Roo.bootstrap.PhoneInput
42614  * @extends Roo.bootstrap.TriggerField
42615  * An input with International dial-code selection
42616  
42617  * @cfg {String} defaultDialCode default '+852'
42618  * @cfg {Array} preferedCountries default []
42619   
42620  * @constructor
42621  * Create a new PhoneInput.
42622  * @param {Object} config Configuration options
42623  */
42624
42625 Roo.bootstrap.PhoneInput = function(config) {
42626     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42627 };
42628
42629 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42630         
42631         listWidth: undefined,
42632         
42633         selectedClass: 'active',
42634         
42635         invalidClass : "has-warning",
42636         
42637         validClass: 'has-success',
42638         
42639         allowed: '0123456789',
42640         
42641         max_length: 15,
42642         
42643         /**
42644          * @cfg {String} defaultDialCode The default dial code when initializing the input
42645          */
42646         defaultDialCode: '+852',
42647         
42648         /**
42649          * @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
42650          */
42651         preferedCountries: false,
42652         
42653         getAutoCreate : function()
42654         {
42655             var data = Roo.bootstrap.PhoneInputData();
42656             var align = this.labelAlign || this.parentLabelAlign();
42657             var id = Roo.id();
42658             
42659             this.allCountries = [];
42660             this.dialCodeMapping = [];
42661             
42662             for (var i = 0; i < data.length; i++) {
42663               var c = data[i];
42664               this.allCountries[i] = {
42665                 name: c[0],
42666                 iso2: c[1],
42667                 dialCode: c[2],
42668                 priority: c[3] || 0,
42669                 areaCodes: c[4] || null
42670               };
42671               this.dialCodeMapping[c[2]] = {
42672                   name: c[0],
42673                   iso2: c[1],
42674                   priority: c[3] || 0,
42675                   areaCodes: c[4] || null
42676               };
42677             }
42678             
42679             var cfg = {
42680                 cls: 'form-group',
42681                 cn: []
42682             };
42683             
42684             var input =  {
42685                 tag: 'input',
42686                 id : id,
42687                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42688                 maxlength: this.max_length,
42689                 cls : 'form-control tel-input',
42690                 autocomplete: 'new-password'
42691             };
42692             
42693             var hiddenInput = {
42694                 tag: 'input',
42695                 type: 'hidden',
42696                 cls: 'hidden-tel-input'
42697             };
42698             
42699             if (this.name) {
42700                 hiddenInput.name = this.name;
42701             }
42702             
42703             if (this.disabled) {
42704                 input.disabled = true;
42705             }
42706             
42707             var flag_container = {
42708                 tag: 'div',
42709                 cls: 'flag-box',
42710                 cn: [
42711                     {
42712                         tag: 'div',
42713                         cls: 'flag'
42714                     },
42715                     {
42716                         tag: 'div',
42717                         cls: 'caret'
42718                     }
42719                 ]
42720             };
42721             
42722             var box = {
42723                 tag: 'div',
42724                 cls: this.hasFeedback ? 'has-feedback' : '',
42725                 cn: [
42726                     hiddenInput,
42727                     input,
42728                     {
42729                         tag: 'input',
42730                         cls: 'dial-code-holder',
42731                         disabled: true
42732                     }
42733                 ]
42734             };
42735             
42736             var container = {
42737                 cls: 'roo-select2-container input-group',
42738                 cn: [
42739                     flag_container,
42740                     box
42741                 ]
42742             };
42743             
42744             if (this.fieldLabel.length) {
42745                 var indicator = {
42746                     tag: 'i',
42747                     tooltip: 'This field is required'
42748                 };
42749                 
42750                 var label = {
42751                     tag: 'label',
42752                     'for':  id,
42753                     cls: 'control-label',
42754                     cn: []
42755                 };
42756                 
42757                 var label_text = {
42758                     tag: 'span',
42759                     html: this.fieldLabel
42760                 };
42761                 
42762                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42763                 label.cn = [
42764                     indicator,
42765                     label_text
42766                 ];
42767                 
42768                 if(this.indicatorpos == 'right') {
42769                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42770                     label.cn = [
42771                         label_text,
42772                         indicator
42773                     ];
42774                 }
42775                 
42776                 if(align == 'left') {
42777                     container = {
42778                         tag: 'div',
42779                         cn: [
42780                             container
42781                         ]
42782                     };
42783                     
42784                     if(this.labelWidth > 12){
42785                         label.style = "width: " + this.labelWidth + 'px';
42786                     }
42787                     if(this.labelWidth < 13 && this.labelmd == 0){
42788                         this.labelmd = this.labelWidth;
42789                     }
42790                     if(this.labellg > 0){
42791                         label.cls += ' col-lg-' + this.labellg;
42792                         input.cls += ' col-lg-' + (12 - this.labellg);
42793                     }
42794                     if(this.labelmd > 0){
42795                         label.cls += ' col-md-' + this.labelmd;
42796                         container.cls += ' col-md-' + (12 - this.labelmd);
42797                     }
42798                     if(this.labelsm > 0){
42799                         label.cls += ' col-sm-' + this.labelsm;
42800                         container.cls += ' col-sm-' + (12 - this.labelsm);
42801                     }
42802                     if(this.labelxs > 0){
42803                         label.cls += ' col-xs-' + this.labelxs;
42804                         container.cls += ' col-xs-' + (12 - this.labelxs);
42805                     }
42806                 }
42807             }
42808             
42809             cfg.cn = [
42810                 label,
42811                 container
42812             ];
42813             
42814             var settings = this;
42815             
42816             ['xs','sm','md','lg'].map(function(size){
42817                 if (settings[size]) {
42818                     cfg.cls += ' col-' + size + '-' + settings[size];
42819                 }
42820             });
42821             
42822             this.store = new Roo.data.Store({
42823                 proxy : new Roo.data.MemoryProxy({}),
42824                 reader : new Roo.data.JsonReader({
42825                     fields : [
42826                         {
42827                             'name' : 'name',
42828                             'type' : 'string'
42829                         },
42830                         {
42831                             'name' : 'iso2',
42832                             'type' : 'string'
42833                         },
42834                         {
42835                             'name' : 'dialCode',
42836                             'type' : 'string'
42837                         },
42838                         {
42839                             'name' : 'priority',
42840                             'type' : 'string'
42841                         },
42842                         {
42843                             'name' : 'areaCodes',
42844                             'type' : 'string'
42845                         }
42846                     ]
42847                 })
42848             });
42849             
42850             if(!this.preferedCountries) {
42851                 this.preferedCountries = [
42852                     'hk',
42853                     'gb',
42854                     'us'
42855                 ];
42856             }
42857             
42858             var p = this.preferedCountries.reverse();
42859             
42860             if(p) {
42861                 for (var i = 0; i < p.length; i++) {
42862                     for (var j = 0; j < this.allCountries.length; j++) {
42863                         if(this.allCountries[j].iso2 == p[i]) {
42864                             var t = this.allCountries[j];
42865                             this.allCountries.splice(j,1);
42866                             this.allCountries.unshift(t);
42867                         }
42868                     } 
42869                 }
42870             }
42871             
42872             this.store.proxy.data = {
42873                 success: true,
42874                 data: this.allCountries
42875             };
42876             
42877             return cfg;
42878         },
42879         
42880         initEvents : function()
42881         {
42882             this.createList();
42883             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42884             
42885             this.indicator = this.indicatorEl();
42886             this.flag = this.flagEl();
42887             this.dialCodeHolder = this.dialCodeHolderEl();
42888             
42889             this.trigger = this.el.select('div.flag-box',true).first();
42890             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42891             
42892             var _this = this;
42893             
42894             (function(){
42895                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42896                 _this.list.setWidth(lw);
42897             }).defer(100);
42898             
42899             this.list.on('mouseover', this.onViewOver, this);
42900             this.list.on('mousemove', this.onViewMove, this);
42901             this.inputEl().on("keyup", this.onKeyUp, this);
42902             this.inputEl().on("keypress", this.onKeyPress, this);
42903             
42904             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42905
42906             this.view = new Roo.View(this.list, this.tpl, {
42907                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42908             });
42909             
42910             this.view.on('click', this.onViewClick, this);
42911             this.setValue(this.defaultDialCode);
42912         },
42913         
42914         onTriggerClick : function(e)
42915         {
42916             Roo.log('trigger click');
42917             if(this.disabled){
42918                 return;
42919             }
42920             
42921             if(this.isExpanded()){
42922                 this.collapse();
42923                 this.hasFocus = false;
42924             }else {
42925                 this.store.load({});
42926                 this.hasFocus = true;
42927                 this.expand();
42928             }
42929         },
42930         
42931         isExpanded : function()
42932         {
42933             return this.list.isVisible();
42934         },
42935         
42936         collapse : function()
42937         {
42938             if(!this.isExpanded()){
42939                 return;
42940             }
42941             this.list.hide();
42942             Roo.get(document).un('mousedown', this.collapseIf, this);
42943             Roo.get(document).un('mousewheel', this.collapseIf, this);
42944             this.fireEvent('collapse', this);
42945             this.validate();
42946         },
42947         
42948         expand : function()
42949         {
42950             Roo.log('expand');
42951
42952             if(this.isExpanded() || !this.hasFocus){
42953                 return;
42954             }
42955             
42956             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42957             this.list.setWidth(lw);
42958             
42959             this.list.show();
42960             this.restrictHeight();
42961             
42962             Roo.get(document).on('mousedown', this.collapseIf, this);
42963             Roo.get(document).on('mousewheel', this.collapseIf, this);
42964             
42965             this.fireEvent('expand', this);
42966         },
42967         
42968         restrictHeight : function()
42969         {
42970             this.list.alignTo(this.inputEl(), this.listAlign);
42971             this.list.alignTo(this.inputEl(), this.listAlign);
42972         },
42973         
42974         onViewOver : function(e, t)
42975         {
42976             if(this.inKeyMode){
42977                 return;
42978             }
42979             var item = this.view.findItemFromChild(t);
42980             
42981             if(item){
42982                 var index = this.view.indexOf(item);
42983                 this.select(index, false);
42984             }
42985         },
42986
42987         // private
42988         onViewClick : function(view, doFocus, el, e)
42989         {
42990             var index = this.view.getSelectedIndexes()[0];
42991             
42992             var r = this.store.getAt(index);
42993             
42994             if(r){
42995                 this.onSelect(r, index);
42996             }
42997             if(doFocus !== false && !this.blockFocus){
42998                 this.inputEl().focus();
42999             }
43000         },
43001         
43002         onViewMove : function(e, t)
43003         {
43004             this.inKeyMode = false;
43005         },
43006         
43007         select : function(index, scrollIntoView)
43008         {
43009             this.selectedIndex = index;
43010             this.view.select(index);
43011             if(scrollIntoView !== false){
43012                 var el = this.view.getNode(index);
43013                 if(el){
43014                     this.list.scrollChildIntoView(el, false);
43015                 }
43016             }
43017         },
43018         
43019         createList : function()
43020         {
43021             this.list = Roo.get(document.body).createChild({
43022                 tag: 'ul',
43023                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43024                 style: 'display:none'
43025             });
43026             
43027             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43028         },
43029         
43030         collapseIf : function(e)
43031         {
43032             var in_combo  = e.within(this.el);
43033             var in_list =  e.within(this.list);
43034             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43035             
43036             if (in_combo || in_list || is_list) {
43037                 return;
43038             }
43039             this.collapse();
43040         },
43041         
43042         onSelect : function(record, index)
43043         {
43044             if(this.fireEvent('beforeselect', this, record, index) !== false){
43045                 
43046                 this.setFlagClass(record.data.iso2);
43047                 this.setDialCode(record.data.dialCode);
43048                 this.hasFocus = false;
43049                 this.collapse();
43050                 this.fireEvent('select', this, record, index);
43051             }
43052         },
43053         
43054         flagEl : function()
43055         {
43056             var flag = this.el.select('div.flag',true).first();
43057             if(!flag){
43058                 return false;
43059             }
43060             return flag;
43061         },
43062         
43063         dialCodeHolderEl : function()
43064         {
43065             var d = this.el.select('input.dial-code-holder',true).first();
43066             if(!d){
43067                 return false;
43068             }
43069             return d;
43070         },
43071         
43072         setDialCode : function(v)
43073         {
43074             this.dialCodeHolder.dom.value = '+'+v;
43075         },
43076         
43077         setFlagClass : function(n)
43078         {
43079             this.flag.dom.className = 'flag '+n;
43080         },
43081         
43082         getValue : function()
43083         {
43084             var v = this.inputEl().getValue();
43085             if(this.dialCodeHolder) {
43086                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43087             }
43088             return v;
43089         },
43090         
43091         setValue : function(v)
43092         {
43093             var d = this.getDialCode(v);
43094             
43095             //invalid dial code
43096             if(v.length == 0 || !d || d.length == 0) {
43097                 if(this.rendered){
43098                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43099                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43100                 }
43101                 return;
43102             }
43103             
43104             //valid dial code
43105             this.setFlagClass(this.dialCodeMapping[d].iso2);
43106             this.setDialCode(d);
43107             this.inputEl().dom.value = v.replace('+'+d,'');
43108             this.hiddenEl().dom.value = this.getValue();
43109             
43110             this.validate();
43111         },
43112         
43113         getDialCode : function(v)
43114         {
43115             v = v ||  '';
43116             
43117             if (v.length == 0) {
43118                 return this.dialCodeHolder.dom.value;
43119             }
43120             
43121             var dialCode = "";
43122             if (v.charAt(0) != "+") {
43123                 return false;
43124             }
43125             var numericChars = "";
43126             for (var i = 1; i < v.length; i++) {
43127               var c = v.charAt(i);
43128               if (!isNaN(c)) {
43129                 numericChars += c;
43130                 if (this.dialCodeMapping[numericChars]) {
43131                   dialCode = v.substr(1, i);
43132                 }
43133                 if (numericChars.length == 4) {
43134                   break;
43135                 }
43136               }
43137             }
43138             return dialCode;
43139         },
43140         
43141         reset : function()
43142         {
43143             this.setValue(this.defaultDialCode);
43144             this.validate();
43145         },
43146         
43147         hiddenEl : function()
43148         {
43149             return this.el.select('input.hidden-tel-input',true).first();
43150         },
43151         
43152         // after setting val
43153         onKeyUp : function(e){
43154             this.setValue(this.getValue());
43155         },
43156         
43157         onKeyPress : function(e){
43158             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43159                 e.stopEvent();
43160             }
43161         }
43162         
43163 });
43164 /**
43165  * @class Roo.bootstrap.MoneyField
43166  * @extends Roo.bootstrap.ComboBox
43167  * Bootstrap MoneyField class
43168  * 
43169  * @constructor
43170  * Create a new MoneyField.
43171  * @param {Object} config Configuration options
43172  */
43173
43174 Roo.bootstrap.MoneyField = function(config) {
43175     
43176     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43177     
43178 };
43179
43180 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43181     
43182     /**
43183      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43184      */
43185     allowDecimals : true,
43186     /**
43187      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43188      */
43189     decimalSeparator : ".",
43190     /**
43191      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43192      */
43193     decimalPrecision : 0,
43194     /**
43195      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43196      */
43197     allowNegative : true,
43198     /**
43199      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43200      */
43201     allowZero: true,
43202     /**
43203      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43204      */
43205     minValue : Number.NEGATIVE_INFINITY,
43206     /**
43207      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43208      */
43209     maxValue : Number.MAX_VALUE,
43210     /**
43211      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43212      */
43213     minText : "The minimum value for this field is {0}",
43214     /**
43215      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43216      */
43217     maxText : "The maximum value for this field is {0}",
43218     /**
43219      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43220      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43221      */
43222     nanText : "{0} is not a valid number",
43223     /**
43224      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43225      */
43226     castInt : true,
43227     /**
43228      * @cfg {String} defaults currency of the MoneyField
43229      * value should be in lkey
43230      */
43231     defaultCurrency : false,
43232     /**
43233      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43234      */
43235     thousandsDelimiter : false,
43236     /**
43237      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43238      */
43239     max_length: false,
43240     
43241     inputlg : 9,
43242     inputmd : 9,
43243     inputsm : 9,
43244     inputxs : 6,
43245     
43246     store : false,
43247     
43248     getAutoCreate : function()
43249     {
43250         var align = this.labelAlign || this.parentLabelAlign();
43251         
43252         var id = Roo.id();
43253
43254         var cfg = {
43255             cls: 'form-group',
43256             cn: []
43257         };
43258
43259         var input =  {
43260             tag: 'input',
43261             id : id,
43262             cls : 'form-control roo-money-amount-input',
43263             autocomplete: 'new-password'
43264         };
43265         
43266         var hiddenInput = {
43267             tag: 'input',
43268             type: 'hidden',
43269             id: Roo.id(),
43270             cls: 'hidden-number-input'
43271         };
43272         
43273         if(this.max_length) {
43274             input.maxlength = this.max_length; 
43275         }
43276         
43277         if (this.name) {
43278             hiddenInput.name = this.name;
43279         }
43280
43281         if (this.disabled) {
43282             input.disabled = true;
43283         }
43284
43285         var clg = 12 - this.inputlg;
43286         var cmd = 12 - this.inputmd;
43287         var csm = 12 - this.inputsm;
43288         var cxs = 12 - this.inputxs;
43289         
43290         var container = {
43291             tag : 'div',
43292             cls : 'row roo-money-field',
43293             cn : [
43294                 {
43295                     tag : 'div',
43296                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43297                     cn : [
43298                         {
43299                             tag : 'div',
43300                             cls: 'roo-select2-container input-group',
43301                             cn: [
43302                                 {
43303                                     tag : 'input',
43304                                     cls : 'form-control roo-money-currency-input',
43305                                     autocomplete: 'new-password',
43306                                     readOnly : 1,
43307                                     name : this.currencyName
43308                                 },
43309                                 {
43310                                     tag :'span',
43311                                     cls : 'input-group-addon',
43312                                     cn : [
43313                                         {
43314                                             tag: 'span',
43315                                             cls: 'caret'
43316                                         }
43317                                     ]
43318                                 }
43319                             ]
43320                         }
43321                     ]
43322                 },
43323                 {
43324                     tag : 'div',
43325                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43326                     cn : [
43327                         {
43328                             tag: 'div',
43329                             cls: this.hasFeedback ? 'has-feedback' : '',
43330                             cn: [
43331                                 input
43332                             ]
43333                         }
43334                     ]
43335                 }
43336             ]
43337             
43338         };
43339         
43340         if (this.fieldLabel.length) {
43341             var indicator = {
43342                 tag: 'i',
43343                 tooltip: 'This field is required'
43344             };
43345
43346             var label = {
43347                 tag: 'label',
43348                 'for':  id,
43349                 cls: 'control-label',
43350                 cn: []
43351             };
43352
43353             var label_text = {
43354                 tag: 'span',
43355                 html: this.fieldLabel
43356             };
43357
43358             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43359             label.cn = [
43360                 indicator,
43361                 label_text
43362             ];
43363
43364             if(this.indicatorpos == 'right') {
43365                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43366                 label.cn = [
43367                     label_text,
43368                     indicator
43369                 ];
43370             }
43371
43372             if(align == 'left') {
43373                 container = {
43374                     tag: 'div',
43375                     cn: [
43376                         container
43377                     ]
43378                 };
43379
43380                 if(this.labelWidth > 12){
43381                     label.style = "width: " + this.labelWidth + 'px';
43382                 }
43383                 if(this.labelWidth < 13 && this.labelmd == 0){
43384                     this.labelmd = this.labelWidth;
43385                 }
43386                 if(this.labellg > 0){
43387                     label.cls += ' col-lg-' + this.labellg;
43388                     input.cls += ' col-lg-' + (12 - this.labellg);
43389                 }
43390                 if(this.labelmd > 0){
43391                     label.cls += ' col-md-' + this.labelmd;
43392                     container.cls += ' col-md-' + (12 - this.labelmd);
43393                 }
43394                 if(this.labelsm > 0){
43395                     label.cls += ' col-sm-' + this.labelsm;
43396                     container.cls += ' col-sm-' + (12 - this.labelsm);
43397                 }
43398                 if(this.labelxs > 0){
43399                     label.cls += ' col-xs-' + this.labelxs;
43400                     container.cls += ' col-xs-' + (12 - this.labelxs);
43401                 }
43402             }
43403         }
43404
43405         cfg.cn = [
43406             label,
43407             container,
43408             hiddenInput
43409         ];
43410         
43411         var settings = this;
43412
43413         ['xs','sm','md','lg'].map(function(size){
43414             if (settings[size]) {
43415                 cfg.cls += ' col-' + size + '-' + settings[size];
43416             }
43417         });
43418         
43419         return cfg;
43420     },
43421     
43422     initEvents : function()
43423     {
43424         this.indicator = this.indicatorEl();
43425         
43426         this.initCurrencyEvent();
43427         
43428         this.initNumberEvent();
43429     },
43430     
43431     initCurrencyEvent : function()
43432     {
43433         if (!this.store) {
43434             throw "can not find store for combo";
43435         }
43436         
43437         this.store = Roo.factory(this.store, Roo.data);
43438         this.store.parent = this;
43439         
43440         this.createList();
43441         
43442         this.triggerEl = this.el.select('.input-group-addon', true).first();
43443         
43444         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43445         
43446         var _this = this;
43447         
43448         (function(){
43449             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43450             _this.list.setWidth(lw);
43451         }).defer(100);
43452         
43453         this.list.on('mouseover', this.onViewOver, this);
43454         this.list.on('mousemove', this.onViewMove, this);
43455         this.list.on('scroll', this.onViewScroll, this);
43456         
43457         if(!this.tpl){
43458             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43459         }
43460         
43461         this.view = new Roo.View(this.list, this.tpl, {
43462             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43463         });
43464         
43465         this.view.on('click', this.onViewClick, this);
43466         
43467         this.store.on('beforeload', this.onBeforeLoad, this);
43468         this.store.on('load', this.onLoad, this);
43469         this.store.on('loadexception', this.onLoadException, this);
43470         
43471         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43472             "up" : function(e){
43473                 this.inKeyMode = true;
43474                 this.selectPrev();
43475             },
43476
43477             "down" : function(e){
43478                 if(!this.isExpanded()){
43479                     this.onTriggerClick();
43480                 }else{
43481                     this.inKeyMode = true;
43482                     this.selectNext();
43483                 }
43484             },
43485
43486             "enter" : function(e){
43487                 this.collapse();
43488                 
43489                 if(this.fireEvent("specialkey", this, e)){
43490                     this.onViewClick(false);
43491                 }
43492                 
43493                 return true;
43494             },
43495
43496             "esc" : function(e){
43497                 this.collapse();
43498             },
43499
43500             "tab" : function(e){
43501                 this.collapse();
43502                 
43503                 if(this.fireEvent("specialkey", this, e)){
43504                     this.onViewClick(false);
43505                 }
43506                 
43507                 return true;
43508             },
43509
43510             scope : this,
43511
43512             doRelay : function(foo, bar, hname){
43513                 if(hname == 'down' || this.scope.isExpanded()){
43514                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43515                 }
43516                 return true;
43517             },
43518
43519             forceKeyDown: true
43520         });
43521         
43522         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43523         
43524     },
43525     
43526     initNumberEvent : function(e)
43527     {
43528         this.inputEl().on("keydown" , this.fireKey,  this);
43529         this.inputEl().on("focus", this.onFocus,  this);
43530         this.inputEl().on("blur", this.onBlur,  this);
43531         
43532         this.inputEl().relayEvent('keyup', this);
43533         
43534         if(this.indicator){
43535             this.indicator.addClass('invisible');
43536         }
43537  
43538         this.originalValue = this.getValue();
43539         
43540         if(this.validationEvent == 'keyup'){
43541             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43542             this.inputEl().on('keyup', this.filterValidation, this);
43543         }
43544         else if(this.validationEvent !== false){
43545             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43546         }
43547         
43548         if(this.selectOnFocus){
43549             this.on("focus", this.preFocus, this);
43550             
43551         }
43552         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43553             this.inputEl().on("keypress", this.filterKeys, this);
43554         } else {
43555             this.inputEl().relayEvent('keypress', this);
43556         }
43557         
43558         var allowed = "0123456789";
43559         
43560         if(this.allowDecimals){
43561             allowed += this.decimalSeparator;
43562         }
43563         
43564         if(this.allowNegative){
43565             allowed += "-";
43566         }
43567         
43568         if(this.thousandsDelimiter) {
43569             allowed += ",";
43570         }
43571         
43572         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43573         
43574         var keyPress = function(e){
43575             
43576             var k = e.getKey();
43577             
43578             var c = e.getCharCode();
43579             
43580             if(
43581                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43582                     allowed.indexOf(String.fromCharCode(c)) === -1
43583             ){
43584                 e.stopEvent();
43585                 return;
43586             }
43587             
43588             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43589                 return;
43590             }
43591             
43592             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43593                 e.stopEvent();
43594             }
43595         };
43596         
43597         this.inputEl().on("keypress", keyPress, this);
43598         
43599     },
43600     
43601     onTriggerClick : function(e)
43602     {   
43603         if(this.disabled){
43604             return;
43605         }
43606         
43607         this.page = 0;
43608         this.loadNext = false;
43609         
43610         if(this.isExpanded()){
43611             this.collapse();
43612             return;
43613         }
43614         
43615         this.hasFocus = true;
43616         
43617         if(this.triggerAction == 'all') {
43618             this.doQuery(this.allQuery, true);
43619             return;
43620         }
43621         
43622         this.doQuery(this.getRawValue());
43623     },
43624     
43625     getCurrency : function()
43626     {   
43627         var v = this.currencyEl().getValue();
43628         
43629         return v;
43630     },
43631     
43632     restrictHeight : function()
43633     {
43634         this.list.alignTo(this.currencyEl(), this.listAlign);
43635         this.list.alignTo(this.currencyEl(), this.listAlign);
43636     },
43637     
43638     onViewClick : function(view, doFocus, el, e)
43639     {
43640         var index = this.view.getSelectedIndexes()[0];
43641         
43642         var r = this.store.getAt(index);
43643         
43644         if(r){
43645             this.onSelect(r, index);
43646         }
43647     },
43648     
43649     onSelect : function(record, index){
43650         
43651         if(this.fireEvent('beforeselect', this, record, index) !== false){
43652         
43653             this.setFromCurrencyData(index > -1 ? record.data : false);
43654             
43655             this.collapse();
43656             
43657             this.fireEvent('select', this, record, index);
43658         }
43659     },
43660     
43661     setFromCurrencyData : function(o)
43662     {
43663         var currency = '';
43664         
43665         this.lastCurrency = o;
43666         
43667         if (this.currencyField) {
43668             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43669         } else {
43670             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43671         }
43672         
43673         this.lastSelectionText = currency;
43674         
43675         //setting default currency
43676         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43677             this.setCurrency(this.defaultCurrency);
43678             return;
43679         }
43680         
43681         this.setCurrency(currency);
43682     },
43683     
43684     setFromData : function(o)
43685     {
43686         var c = {};
43687         
43688         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43689         
43690         this.setFromCurrencyData(c);
43691         
43692         var value = '';
43693         
43694         if (this.name) {
43695             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43696         } else {
43697             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43698         }
43699         
43700         this.setValue(value);
43701         
43702     },
43703     
43704     setCurrency : function(v)
43705     {   
43706         this.currencyValue = v;
43707         
43708         if(this.rendered){
43709             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43710             this.validate();
43711         }
43712     },
43713     
43714     setValue : function(v)
43715     {
43716         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43717         
43718         this.value = v;
43719         
43720         if(this.rendered){
43721             
43722             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43723             
43724             this.inputEl().dom.value = (v == '') ? '' :
43725                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43726             
43727             if(!this.allowZero && v === '0') {
43728                 this.hiddenEl().dom.value = '';
43729                 this.inputEl().dom.value = '';
43730             }
43731             
43732             this.validate();
43733         }
43734     },
43735     
43736     getRawValue : function()
43737     {
43738         var v = this.inputEl().getValue();
43739         
43740         return v;
43741     },
43742     
43743     getValue : function()
43744     {
43745         return this.fixPrecision(this.parseValue(this.getRawValue()));
43746     },
43747     
43748     parseValue : function(value)
43749     {
43750         if(this.thousandsDelimiter) {
43751             value += "";
43752             r = new RegExp(",", "g");
43753             value = value.replace(r, "");
43754         }
43755         
43756         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43757         return isNaN(value) ? '' : value;
43758         
43759     },
43760     
43761     fixPrecision : function(value)
43762     {
43763         if(this.thousandsDelimiter) {
43764             value += "";
43765             r = new RegExp(",", "g");
43766             value = value.replace(r, "");
43767         }
43768         
43769         var nan = isNaN(value);
43770         
43771         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43772             return nan ? '' : value;
43773         }
43774         return parseFloat(value).toFixed(this.decimalPrecision);
43775     },
43776     
43777     decimalPrecisionFcn : function(v)
43778     {
43779         return Math.floor(v);
43780     },
43781     
43782     validateValue : function(value)
43783     {
43784         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43785             return false;
43786         }
43787         
43788         var num = this.parseValue(value);
43789         
43790         if(isNaN(num)){
43791             this.markInvalid(String.format(this.nanText, value));
43792             return false;
43793         }
43794         
43795         if(num < this.minValue){
43796             this.markInvalid(String.format(this.minText, this.minValue));
43797             return false;
43798         }
43799         
43800         if(num > this.maxValue){
43801             this.markInvalid(String.format(this.maxText, this.maxValue));
43802             return false;
43803         }
43804         
43805         return true;
43806     },
43807     
43808     validate : function()
43809     {
43810         if(this.disabled || this.allowBlank){
43811             this.markValid();
43812             return true;
43813         }
43814         
43815         var currency = this.getCurrency();
43816         
43817         if(this.validateValue(this.getRawValue()) && currency.length){
43818             this.markValid();
43819             return true;
43820         }
43821         
43822         this.markInvalid();
43823         return false;
43824     },
43825     
43826     getName: function()
43827     {
43828         return this.name;
43829     },
43830     
43831     beforeBlur : function()
43832     {
43833         if(!this.castInt){
43834             return;
43835         }
43836         
43837         var v = this.parseValue(this.getRawValue());
43838         
43839         if(v || v == 0){
43840             this.setValue(v);
43841         }
43842     },
43843     
43844     onBlur : function()
43845     {
43846         this.beforeBlur();
43847         
43848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43849             //this.el.removeClass(this.focusClass);
43850         }
43851         
43852         this.hasFocus = false;
43853         
43854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43855             this.validate();
43856         }
43857         
43858         var v = this.getValue();
43859         
43860         if(String(v) !== String(this.startValue)){
43861             this.fireEvent('change', this, v, this.startValue);
43862         }
43863         
43864         this.fireEvent("blur", this);
43865     },
43866     
43867     inputEl : function()
43868     {
43869         return this.el.select('.roo-money-amount-input', true).first();
43870     },
43871     
43872     currencyEl : function()
43873     {
43874         return this.el.select('.roo-money-currency-input', true).first();
43875     },
43876     
43877     hiddenEl : function()
43878     {
43879         return this.el.select('input.hidden-number-input',true).first();
43880     }
43881     
43882 });/**
43883  * @class Roo.bootstrap.BezierSignature
43884  * @extends Roo.bootstrap.Component
43885  * Bootstrap BezierSignature class
43886  * This script refer to:
43887  *    Title: Signature Pad
43888  *    Author: szimek
43889  *    Availability: https://github.com/szimek/signature_pad
43890  *
43891  * @constructor
43892  * Create a new BezierSignature
43893  * @param {Object} config The config object
43894  */
43895
43896 Roo.bootstrap.BezierSignature = function(config){
43897     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43898     this.addEvents({
43899         "resize" : true
43900     });
43901 };
43902
43903 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43904 {
43905      
43906     curve_data: [],
43907     
43908     is_empty: true,
43909     
43910     mouse_btn_down: true,
43911     
43912     /**
43913      * @cfg {int} canvas height
43914      */
43915     canvas_height: '200px',
43916     
43917     /**
43918      * @cfg {float|function} Radius of a single dot.
43919      */ 
43920     dot_size: false,
43921     
43922     /**
43923      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43924      */
43925     min_width: 0.5,
43926     
43927     /**
43928      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43929      */
43930     max_width: 2.5,
43931     
43932     /**
43933      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43934      */
43935     throttle: 16,
43936     
43937     /**
43938      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43939      */
43940     min_distance: 5,
43941     
43942     /**
43943      * @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.
43944      */
43945     bg_color: 'rgba(0, 0, 0, 0)',
43946     
43947     /**
43948      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43949      */
43950     dot_color: 'black',
43951     
43952     /**
43953      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43954      */ 
43955     velocity_filter_weight: 0.7,
43956     
43957     /**
43958      * @cfg {function} Callback when stroke begin. 
43959      */
43960     onBegin: false,
43961     
43962     /**
43963      * @cfg {function} Callback when stroke end.
43964      */
43965     onEnd: false,
43966     
43967     getAutoCreate : function()
43968     {
43969         var cls = 'roo-signature column';
43970         
43971         if(this.cls){
43972             cls += ' ' + this.cls;
43973         }
43974         
43975         var col_sizes = [
43976             'lg',
43977             'md',
43978             'sm',
43979             'xs'
43980         ];
43981         
43982         for(var i = 0; i < col_sizes.length; i++) {
43983             if(this[col_sizes[i]]) {
43984                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43985             }
43986         }
43987         
43988         var cfg = {
43989             tag: 'div',
43990             cls: cls,
43991             cn: [
43992                 {
43993                     tag: 'div',
43994                     cls: 'roo-signature-body',
43995                     cn: [
43996                         {
43997                             tag: 'canvas',
43998                             cls: 'roo-signature-body-canvas',
43999                             height: this.canvas_height,
44000                             width: this.canvas_width
44001                         }
44002                     ]
44003                 },
44004                 {
44005                     tag: 'input',
44006                     type: 'file',
44007                     style: 'display: none'
44008                 }
44009             ]
44010         };
44011         
44012         return cfg;
44013     },
44014     
44015     initEvents: function() 
44016     {
44017         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44018         
44019         var canvas = this.canvasEl();
44020         
44021         // mouse && touch event swapping...
44022         canvas.dom.style.touchAction = 'none';
44023         canvas.dom.style.msTouchAction = 'none';
44024         
44025         this.mouse_btn_down = false;
44026         canvas.on('mousedown', this._handleMouseDown, this);
44027         canvas.on('mousemove', this._handleMouseMove, this);
44028         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44029         
44030         if (window.PointerEvent) {
44031             canvas.on('pointerdown', this._handleMouseDown, this);
44032             canvas.on('pointermove', this._handleMouseMove, this);
44033             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44034         }
44035         
44036         if ('ontouchstart' in window) {
44037             canvas.on('touchstart', this._handleTouchStart, this);
44038             canvas.on('touchmove', this._handleTouchMove, this);
44039             canvas.on('touchend', this._handleTouchEnd, this);
44040         }
44041         
44042         Roo.EventManager.onWindowResize(this.resize, this, true);
44043         
44044         // file input event
44045         this.fileEl().on('change', this.uploadImage, this);
44046         
44047         this.clear();
44048         
44049         this.resize();
44050     },
44051     
44052     resize: function(){
44053         
44054         var canvas = this.canvasEl().dom;
44055         var ctx = this.canvasElCtx();
44056         var img_data = false;
44057         
44058         if(canvas.width > 0) {
44059             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44060         }
44061         // setting canvas width will clean img data
44062         canvas.width = 0;
44063         
44064         var style = window.getComputedStyle ? 
44065             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44066             
44067         var padding_left = parseInt(style.paddingLeft) || 0;
44068         var padding_right = parseInt(style.paddingRight) || 0;
44069         
44070         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44071         
44072         if(img_data) {
44073             ctx.putImageData(img_data, 0, 0);
44074         }
44075     },
44076     
44077     _handleMouseDown: function(e)
44078     {
44079         if (e.browserEvent.which === 1) {
44080             this.mouse_btn_down = true;
44081             this.strokeBegin(e);
44082         }
44083     },
44084     
44085     _handleMouseMove: function (e)
44086     {
44087         if (this.mouse_btn_down) {
44088             this.strokeMoveUpdate(e);
44089         }
44090     },
44091     
44092     _handleMouseUp: function (e)
44093     {
44094         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44095             this.mouse_btn_down = false;
44096             this.strokeEnd(e);
44097         }
44098     },
44099     
44100     _handleTouchStart: function (e) {
44101         
44102         e.preventDefault();
44103         if (e.browserEvent.targetTouches.length === 1) {
44104             // var touch = e.browserEvent.changedTouches[0];
44105             // this.strokeBegin(touch);
44106             
44107              this.strokeBegin(e); // assume e catching the correct xy...
44108         }
44109     },
44110     
44111     _handleTouchMove: function (e) {
44112         e.preventDefault();
44113         // var touch = event.targetTouches[0];
44114         // _this._strokeMoveUpdate(touch);
44115         this.strokeMoveUpdate(e);
44116     },
44117     
44118     _handleTouchEnd: function (e) {
44119         var wasCanvasTouched = e.target === this.canvasEl().dom;
44120         if (wasCanvasTouched) {
44121             e.preventDefault();
44122             // var touch = event.changedTouches[0];
44123             // _this._strokeEnd(touch);
44124             this.strokeEnd(e);
44125         }
44126     },
44127     
44128     reset: function () {
44129         this._lastPoints = [];
44130         this._lastVelocity = 0;
44131         this._lastWidth = (this.min_width + this.max_width) / 2;
44132         this.canvasElCtx().fillStyle = this.dot_color;
44133     },
44134     
44135     strokeMoveUpdate: function(e)
44136     {
44137         this.strokeUpdate(e);
44138         
44139         if (this.throttle) {
44140             this.throttleStroke(this.strokeUpdate, this.throttle);
44141         }
44142         else {
44143             this.strokeUpdate(e);
44144         }
44145     },
44146     
44147     strokeBegin: function(e)
44148     {
44149         var newPointGroup = {
44150             color: this.dot_color,
44151             points: []
44152         };
44153         
44154         if (typeof this.onBegin === 'function') {
44155             this.onBegin(e);
44156         }
44157         
44158         this.curve_data.push(newPointGroup);
44159         this.reset();
44160         this.strokeUpdate(e);
44161     },
44162     
44163     strokeUpdate: function(e)
44164     {
44165         var rect = this.canvasEl().dom.getBoundingClientRect();
44166         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44167         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44168         var lastPoints = lastPointGroup.points;
44169         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44170         var isLastPointTooClose = lastPoint
44171             ? point.distanceTo(lastPoint) <= this.min_distance
44172             : false;
44173         var color = lastPointGroup.color;
44174         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44175             var curve = this.addPoint(point);
44176             if (!lastPoint) {
44177                 this.drawDot({color: color, point: point});
44178             }
44179             else if (curve) {
44180                 this.drawCurve({color: color, curve: curve});
44181             }
44182             lastPoints.push({
44183                 time: point.time,
44184                 x: point.x,
44185                 y: point.y
44186             });
44187         }
44188     },
44189     
44190     strokeEnd: function(e)
44191     {
44192         this.strokeUpdate(e);
44193         if (typeof this.onEnd === 'function') {
44194             this.onEnd(e);
44195         }
44196     },
44197     
44198     addPoint:  function (point) {
44199         var _lastPoints = this._lastPoints;
44200         _lastPoints.push(point);
44201         if (_lastPoints.length > 2) {
44202             if (_lastPoints.length === 3) {
44203                 _lastPoints.unshift(_lastPoints[0]);
44204             }
44205             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44206             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44207             _lastPoints.shift();
44208             return curve;
44209         }
44210         return null;
44211     },
44212     
44213     calculateCurveWidths: function (startPoint, endPoint) {
44214         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44215             (1 - this.velocity_filter_weight) * this._lastVelocity;
44216
44217         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44218         var widths = {
44219             end: newWidth,
44220             start: this._lastWidth
44221         };
44222         
44223         this._lastVelocity = velocity;
44224         this._lastWidth = newWidth;
44225         return widths;
44226     },
44227     
44228     drawDot: function (_a) {
44229         var color = _a.color, point = _a.point;
44230         var ctx = this.canvasElCtx();
44231         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44232         ctx.beginPath();
44233         this.drawCurveSegment(point.x, point.y, width);
44234         ctx.closePath();
44235         ctx.fillStyle = color;
44236         ctx.fill();
44237     },
44238     
44239     drawCurve: function (_a) {
44240         var color = _a.color, curve = _a.curve;
44241         var ctx = this.canvasElCtx();
44242         var widthDelta = curve.endWidth - curve.startWidth;
44243         var drawSteps = Math.floor(curve.length()) * 2;
44244         ctx.beginPath();
44245         ctx.fillStyle = color;
44246         for (var i = 0; i < drawSteps; i += 1) {
44247         var t = i / drawSteps;
44248         var tt = t * t;
44249         var ttt = tt * t;
44250         var u = 1 - t;
44251         var uu = u * u;
44252         var uuu = uu * u;
44253         var x = uuu * curve.startPoint.x;
44254         x += 3 * uu * t * curve.control1.x;
44255         x += 3 * u * tt * curve.control2.x;
44256         x += ttt * curve.endPoint.x;
44257         var y = uuu * curve.startPoint.y;
44258         y += 3 * uu * t * curve.control1.y;
44259         y += 3 * u * tt * curve.control2.y;
44260         y += ttt * curve.endPoint.y;
44261         var width = curve.startWidth + ttt * widthDelta;
44262         this.drawCurveSegment(x, y, width);
44263         }
44264         ctx.closePath();
44265         ctx.fill();
44266     },
44267     
44268     drawCurveSegment: function (x, y, width) {
44269         var ctx = this.canvasElCtx();
44270         ctx.moveTo(x, y);
44271         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44272         this.is_empty = false;
44273     },
44274     
44275     clear: function()
44276     {
44277         var ctx = this.canvasElCtx();
44278         var canvas = this.canvasEl().dom;
44279         ctx.fillStyle = this.bg_color;
44280         ctx.clearRect(0, 0, canvas.width, canvas.height);
44281         ctx.fillRect(0, 0, canvas.width, canvas.height);
44282         this.curve_data = [];
44283         this.reset();
44284         this.is_empty = true;
44285     },
44286     
44287     fileEl: function()
44288     {
44289         return  this.el.select('input',true).first();
44290     },
44291     
44292     canvasEl: function()
44293     {
44294         return this.el.select('canvas',true).first();
44295     },
44296     
44297     canvasElCtx: function()
44298     {
44299         return this.el.select('canvas',true).first().dom.getContext('2d');
44300     },
44301     
44302     getImage: function(type)
44303     {
44304         if(this.is_empty) {
44305             return false;
44306         }
44307         
44308         // encryption ?
44309         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44310     },
44311     
44312     drawFromImage: function(img_src)
44313     {
44314         var img = new Image();
44315         
44316         img.onload = function(){
44317             this.canvasElCtx().drawImage(img, 0, 0);
44318         }.bind(this);
44319         
44320         img.src = img_src;
44321         
44322         this.is_empty = false;
44323     },
44324     
44325     selectImage: function()
44326     {
44327         this.fileEl().dom.click();
44328     },
44329     
44330     uploadImage: function(e)
44331     {
44332         var reader = new FileReader();
44333         
44334         reader.onload = function(e){
44335             var img = new Image();
44336             img.onload = function(){
44337                 this.reset();
44338                 this.canvasElCtx().drawImage(img, 0, 0);
44339             }.bind(this);
44340             img.src = e.target.result;
44341         }.bind(this);
44342         
44343         reader.readAsDataURL(e.target.files[0]);
44344     },
44345     
44346     // Bezier Point Constructor
44347     Point: (function () {
44348         function Point(x, y, time) {
44349             this.x = x;
44350             this.y = y;
44351             this.time = time || Date.now();
44352         }
44353         Point.prototype.distanceTo = function (start) {
44354             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44355         };
44356         Point.prototype.equals = function (other) {
44357             return this.x === other.x && this.y === other.y && this.time === other.time;
44358         };
44359         Point.prototype.velocityFrom = function (start) {
44360             return this.time !== start.time
44361             ? this.distanceTo(start) / (this.time - start.time)
44362             : 0;
44363         };
44364         return Point;
44365     }()),
44366     
44367     
44368     // Bezier Constructor
44369     Bezier: (function () {
44370         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44371             this.startPoint = startPoint;
44372             this.control2 = control2;
44373             this.control1 = control1;
44374             this.endPoint = endPoint;
44375             this.startWidth = startWidth;
44376             this.endWidth = endWidth;
44377         }
44378         Bezier.fromPoints = function (points, widths, scope) {
44379             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44380             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44381             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44382         };
44383         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44384             var dx1 = s1.x - s2.x;
44385             var dy1 = s1.y - s2.y;
44386             var dx2 = s2.x - s3.x;
44387             var dy2 = s2.y - s3.y;
44388             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44389             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44390             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44391             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44392             var dxm = m1.x - m2.x;
44393             var dym = m1.y - m2.y;
44394             var k = l2 / (l1 + l2);
44395             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44396             var tx = s2.x - cm.x;
44397             var ty = s2.y - cm.y;
44398             return {
44399                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44400                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44401             };
44402         };
44403         Bezier.prototype.length = function () {
44404             var steps = 10;
44405             var length = 0;
44406             var px;
44407             var py;
44408             for (var i = 0; i <= steps; i += 1) {
44409                 var t = i / steps;
44410                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44411                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44412                 if (i > 0) {
44413                     var xdiff = cx - px;
44414                     var ydiff = cy - py;
44415                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44416                 }
44417                 px = cx;
44418                 py = cy;
44419             }
44420             return length;
44421         };
44422         Bezier.prototype.point = function (t, start, c1, c2, end) {
44423             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44424             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44425             + (3.0 * c2 * (1.0 - t) * t * t)
44426             + (end * t * t * t);
44427         };
44428         return Bezier;
44429     }()),
44430     
44431     throttleStroke: function(fn, wait) {
44432       if (wait === void 0) { wait = 250; }
44433       var previous = 0;
44434       var timeout = null;
44435       var result;
44436       var storedContext;
44437       var storedArgs;
44438       var later = function () {
44439           previous = Date.now();
44440           timeout = null;
44441           result = fn.apply(storedContext, storedArgs);
44442           if (!timeout) {
44443               storedContext = null;
44444               storedArgs = [];
44445           }
44446       };
44447       return function wrapper() {
44448           var args = [];
44449           for (var _i = 0; _i < arguments.length; _i++) {
44450               args[_i] = arguments[_i];
44451           }
44452           var now = Date.now();
44453           var remaining = wait - (now - previous);
44454           storedContext = this;
44455           storedArgs = args;
44456           if (remaining <= 0 || remaining > wait) {
44457               if (timeout) {
44458                   clearTimeout(timeout);
44459                   timeout = null;
44460               }
44461               previous = now;
44462               result = fn.apply(storedContext, storedArgs);
44463               if (!timeout) {
44464                   storedContext = null;
44465                   storedArgs = [];
44466               }
44467           }
44468           else if (!timeout) {
44469               timeout = window.setTimeout(later, remaining);
44470           }
44471           return result;
44472       };
44473   }
44474   
44475 });
44476
44477  
44478
44479