roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
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                     nane : this.name,
12532                     value : this.value,
12533                     cls : 'd-none  form-control'
12534                 },
12535                 
12536                 {
12537                     tag: 'input',
12538                     multiple : 'multiple',
12539                     type : 'file',
12540                     cls : 'd-none  roo-card-upload-selector'
12541                 },
12542                 
12543                 {
12544                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12545                 },
12546                 {
12547                     cls : 'card-columns roo-card-uploader-container'
12548                 }
12549
12550             ]
12551         };
12552            
12553          
12554         return cfg;
12555     },
12556     
12557     getChildContainer : function() /// what children are added to.
12558     {
12559         return this.containerEl;
12560     },
12561    
12562     getButtonContainer : function() /// what children are added to.
12563     {
12564         return this.el.select(".roo-card-uploader-button-container").first();
12565     },
12566    
12567     initEvents : function()
12568     {
12569         
12570         Roo.bootstrap.Input.prototype.initEvents.call(this);
12571         
12572         var t = this;
12573         this.addxtype({
12574             xns: Roo.bootstrap,
12575
12576             xtype : 'Button',
12577             container_method : 'getButtonContainer' ,            
12578             html :  this.html, // fix changable?
12579             cls : 'w-100 ',
12580             listeners : {
12581                 'click' : function(btn, e) {
12582                     t.onClick(e);
12583                 }
12584             }
12585         });
12586         
12587         
12588         
12589         
12590         this.urlAPI = (window.createObjectURL && window) || 
12591                                 (window.URL && URL.revokeObjectURL && URL) || 
12592                                 (window.webkitURL && webkitURL);
12593                         
12594          
12595          
12596          
12597         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598         
12599         this.selectorEl.on('change', this.onFileSelected, this);
12600         if (this.images) {
12601             var t = this;
12602             this.images.forEach(function(img) {
12603                 t.addCard(img)
12604             });
12605             this.images = false;
12606         }
12607         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12608          
12609        
12610     },
12611     
12612    
12613     onClick : function(e)
12614     {
12615         e.preventDefault();
12616          
12617         this.selectorEl.dom.click();
12618          
12619     },
12620     
12621     onFileSelected : function(e)
12622     {
12623         e.preventDefault();
12624         
12625         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626             return;
12627         }
12628         
12629         Roo.each(this.selectorEl.dom.files, function(file){    
12630             this.addFile(file);
12631         }, this);
12632          
12633     },
12634     
12635       
12636     
12637       
12638     
12639     addFile : function(file)
12640     {
12641            
12642         if(typeof(file) === 'string'){
12643             throw "Add file by name?"; // should not happen
12644             return;
12645         }
12646         
12647         if(!file || !this.urlAPI){
12648             return;
12649         }
12650         
12651         // file;
12652         // file.type;
12653         
12654         var _this = this;
12655         
12656         
12657         var url = _this.urlAPI.createObjectURL( file);
12658            
12659         this.addCard({
12660             id : Roo.bootstrap.CardUploader.ID--,
12661             is_uploaded : false,
12662             src : url,
12663             title : file.name,
12664             mimetype : file.type,
12665             preview : false,
12666             is_deleted : 0
12667         })
12668         
12669     },
12670     
12671     addCard : function (data)
12672     {
12673         // hidden input element?
12674         // if the file is not an image...
12675         //then we need to use something other that and header_image
12676         var t = this;
12677         //   remove.....
12678         var footer = [
12679             {
12680                 xns : Roo.bootstrap,
12681                 xtype : 'CardFooter',
12682                 items: [
12683                     {
12684                         xns : Roo.bootstrap,
12685                         xtype : 'Element',
12686                         cls : 'd-flex',
12687                         items : [
12688                             
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 html : String.format("<small>{0}</small>", data.title),
12693                                 cls : 'col-11 text-left',
12694                                 size: 'sm',
12695                                 weight: 'link',
12696                                 fa : 'download',
12697                                 listeners : {
12698                                     click : function() {
12699                                         this.downloadCard(data.id)
12700                                     }
12701                                 }
12702                             },
12703                           
12704                             {
12705                                 xns : Roo.bootstrap,
12706                                 xtype : 'Button',
12707                                 
12708                                 size : 'sm',
12709                                 weight: 'danger',
12710                                 cls : 'col-1',
12711                                 fa : 'times',
12712                                 listeners : {
12713                                     click : function() {
12714                                         t.removeCard(data.id)
12715                                     }
12716                                 }
12717                             }
12718                         ]
12719                     }
12720                     
12721                 ] 
12722             }
12723             
12724         ];
12725
12726         var cn = this.addxtype(
12727             {
12728                  
12729                 xns : Roo.bootstrap,
12730                 xtype : 'Card',
12731                 closeable : true,
12732                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12734                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12735                 data : data,
12736                 html : false,
12737                  
12738                 items : footer,
12739                 initEvents : function() {
12740                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12741                     this.imgEl = this.el.select('.card-img-top').first();
12742                     if (this.imgEl) {
12743                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744                         this.imgEl.set({ 'pointer' : 'cursor' });
12745                                   
12746                     }
12747                     
12748                   
12749                 }
12750                 
12751             }
12752         );
12753         // dont' really need ot update items.
12754         // this.items.push(cn);
12755         this.fileCollection.add(cn);
12756         this.updateInput();
12757         
12758     },
12759     removeCard : function(id)
12760     {
12761         
12762         var card  = this.fileCollection.get(id);
12763         card.data.is_deleted = 1;
12764         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12765         this.fileCollection.remove(card);
12766         //this.items = this.items.filter(function(e) { return e != card });
12767         // dont' really need ot update items.
12768         card.el.dom.parentNode.removeChild(card.el.dom);
12769         
12770     },
12771     reset: function()
12772     {
12773         this.fileCollection.each(function(card) {
12774             card.el.dom.parentNode.removeChild(card.el.dom);    
12775         });
12776         this.fileCollection.clear();
12777         this.updateInput();
12778     },
12779     
12780     updateInput : function()
12781     {
12782         var i =0;
12783         var data = [];
12784         var dom = this.inputEl().dom;
12785         var fc = this.fileCollection;
12786         var next = function() {
12787             if (i >= fc.length) {
12788                 dom.value = JSON.stringify(data);
12789                 return;
12790             }
12791             var reader = new FileReader();
12792             reader.onloadend = function(evt) {  
12793                 // file is loaded
12794                 var ee = Roo.apply({}, fc[i]);
12795                 ee.src = evt.target.result;
12796                 data.push(ee);
12797                 i++;
12798                 next();
12799             };
12800             reader.readAsDataURL(fc[i].src);
12801             
12802         }
12803         next();
12804         
12805         
12806     }
12807     
12808     
12809 });
12810
12811
12812 Roo.bootstrap.CardUploader.ID = -1;/*
12813  * Based on:
12814  * Ext JS Library 1.1.1
12815  * Copyright(c) 2006-2007, Ext JS, LLC.
12816  *
12817  * Originally Released Under LGPL - original licence link has changed is not relivant.
12818  *
12819  * Fork - LGPL
12820  * <script type="text/javascript">
12821  */
12822
12823
12824 /**
12825  * @class Roo.data.SortTypes
12826  * @singleton
12827  * Defines the default sorting (casting?) comparison functions used when sorting data.
12828  */
12829 Roo.data.SortTypes = {
12830     /**
12831      * Default sort that does nothing
12832      * @param {Mixed} s The value being converted
12833      * @return {Mixed} The comparison value
12834      */
12835     none : function(s){
12836         return s;
12837     },
12838     
12839     /**
12840      * The regular expression used to strip tags
12841      * @type {RegExp}
12842      * @property
12843      */
12844     stripTagsRE : /<\/?[^>]+>/gi,
12845     
12846     /**
12847      * Strips all HTML tags to sort on text only
12848      * @param {Mixed} s The value being converted
12849      * @return {String} The comparison value
12850      */
12851     asText : function(s){
12852         return String(s).replace(this.stripTagsRE, "");
12853     },
12854     
12855     /**
12856      * Strips all HTML tags to sort on text only - Case insensitive
12857      * @param {Mixed} s The value being converted
12858      * @return {String} The comparison value
12859      */
12860     asUCText : function(s){
12861         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12862     },
12863     
12864     /**
12865      * Case insensitive string
12866      * @param {Mixed} s The value being converted
12867      * @return {String} The comparison value
12868      */
12869     asUCString : function(s) {
12870         return String(s).toUpperCase();
12871     },
12872     
12873     /**
12874      * Date sorting
12875      * @param {Mixed} s The value being converted
12876      * @return {Number} The comparison value
12877      */
12878     asDate : function(s) {
12879         if(!s){
12880             return 0;
12881         }
12882         if(s instanceof Date){
12883             return s.getTime();
12884         }
12885         return Date.parse(String(s));
12886     },
12887     
12888     /**
12889      * Float sorting
12890      * @param {Mixed} s The value being converted
12891      * @return {Float} The comparison value
12892      */
12893     asFloat : function(s) {
12894         var val = parseFloat(String(s).replace(/,/g, ""));
12895         if(isNaN(val)) {
12896             val = 0;
12897         }
12898         return val;
12899     },
12900     
12901     /**
12902      * Integer sorting
12903      * @param {Mixed} s The value being converted
12904      * @return {Number} The comparison value
12905      */
12906     asInt : function(s) {
12907         var val = parseInt(String(s).replace(/,/g, ""));
12908         if(isNaN(val)) {
12909             val = 0;
12910         }
12911         return val;
12912     }
12913 };/*
12914  * Based on:
12915  * Ext JS Library 1.1.1
12916  * Copyright(c) 2006-2007, Ext JS, LLC.
12917  *
12918  * Originally Released Under LGPL - original licence link has changed is not relivant.
12919  *
12920  * Fork - LGPL
12921  * <script type="text/javascript">
12922  */
12923
12924 /**
12925 * @class Roo.data.Record
12926  * Instances of this class encapsulate both record <em>definition</em> information, and record
12927  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12928  * to access Records cached in an {@link Roo.data.Store} object.<br>
12929  * <p>
12930  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12931  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12932  * objects.<br>
12933  * <p>
12934  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12935  * @constructor
12936  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12937  * {@link #create}. The parameters are the same.
12938  * @param {Array} data An associative Array of data values keyed by the field name.
12939  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12940  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12941  * not specified an integer id is generated.
12942  */
12943 Roo.data.Record = function(data, id){
12944     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12945     this.data = data;
12946 };
12947
12948 /**
12949  * Generate a constructor for a specific record layout.
12950  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12951  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12952  * Each field definition object may contain the following properties: <ul>
12953  * <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,
12954  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12955  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12956  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12957  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12958  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12959  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12960  * this may be omitted.</p></li>
12961  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12962  * <ul><li>auto (Default, implies no conversion)</li>
12963  * <li>string</li>
12964  * <li>int</li>
12965  * <li>float</li>
12966  * <li>boolean</li>
12967  * <li>date</li></ul></p></li>
12968  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12969  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12970  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12971  * by the Reader into an object that will be stored in the Record. It is passed the
12972  * following parameters:<ul>
12973  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12974  * </ul></p></li>
12975  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12976  * </ul>
12977  * <br>usage:<br><pre><code>
12978 var TopicRecord = Roo.data.Record.create(
12979     {name: 'title', mapping: 'topic_title'},
12980     {name: 'author', mapping: 'username'},
12981     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12982     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12983     {name: 'lastPoster', mapping: 'user2'},
12984     {name: 'excerpt', mapping: 'post_text'}
12985 );
12986
12987 var myNewRecord = new TopicRecord({
12988     title: 'Do my job please',
12989     author: 'noobie',
12990     totalPosts: 1,
12991     lastPost: new Date(),
12992     lastPoster: 'Animal',
12993     excerpt: 'No way dude!'
12994 });
12995 myStore.add(myNewRecord);
12996 </code></pre>
12997  * @method create
12998  * @static
12999  */
13000 Roo.data.Record.create = function(o){
13001     var f = function(){
13002         f.superclass.constructor.apply(this, arguments);
13003     };
13004     Roo.extend(f, Roo.data.Record);
13005     var p = f.prototype;
13006     p.fields = new Roo.util.MixedCollection(false, function(field){
13007         return field.name;
13008     });
13009     for(var i = 0, len = o.length; i < len; i++){
13010         p.fields.add(new Roo.data.Field(o[i]));
13011     }
13012     f.getField = function(name){
13013         return p.fields.get(name);  
13014     };
13015     return f;
13016 };
13017
13018 Roo.data.Record.AUTO_ID = 1000;
13019 Roo.data.Record.EDIT = 'edit';
13020 Roo.data.Record.REJECT = 'reject';
13021 Roo.data.Record.COMMIT = 'commit';
13022
13023 Roo.data.Record.prototype = {
13024     /**
13025      * Readonly flag - true if this record has been modified.
13026      * @type Boolean
13027      */
13028     dirty : false,
13029     editing : false,
13030     error: null,
13031     modified: null,
13032
13033     // private
13034     join : function(store){
13035         this.store = store;
13036     },
13037
13038     /**
13039      * Set the named field to the specified value.
13040      * @param {String} name The name of the field to set.
13041      * @param {Object} value The value to set the field to.
13042      */
13043     set : function(name, value){
13044         if(this.data[name] == value){
13045             return;
13046         }
13047         this.dirty = true;
13048         if(!this.modified){
13049             this.modified = {};
13050         }
13051         if(typeof this.modified[name] == 'undefined'){
13052             this.modified[name] = this.data[name];
13053         }
13054         this.data[name] = value;
13055         if(!this.editing && this.store){
13056             this.store.afterEdit(this);
13057         }       
13058     },
13059
13060     /**
13061      * Get the value of the named field.
13062      * @param {String} name The name of the field to get the value of.
13063      * @return {Object} The value of the field.
13064      */
13065     get : function(name){
13066         return this.data[name]; 
13067     },
13068
13069     // private
13070     beginEdit : function(){
13071         this.editing = true;
13072         this.modified = {}; 
13073     },
13074
13075     // private
13076     cancelEdit : function(){
13077         this.editing = false;
13078         delete this.modified;
13079     },
13080
13081     // private
13082     endEdit : function(){
13083         this.editing = false;
13084         if(this.dirty && this.store){
13085             this.store.afterEdit(this);
13086         }
13087     },
13088
13089     /**
13090      * Usually called by the {@link Roo.data.Store} which owns the Record.
13091      * Rejects all changes made to the Record since either creation, or the last commit operation.
13092      * Modified fields are reverted to their original values.
13093      * <p>
13094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095      * of reject operations.
13096      */
13097     reject : function(){
13098         var m = this.modified;
13099         for(var n in m){
13100             if(typeof m[n] != "function"){
13101                 this.data[n] = m[n];
13102             }
13103         }
13104         this.dirty = false;
13105         delete this.modified;
13106         this.editing = false;
13107         if(this.store){
13108             this.store.afterReject(this);
13109         }
13110     },
13111
13112     /**
13113      * Usually called by the {@link Roo.data.Store} which owns the Record.
13114      * Commits all changes made to the Record since either creation, or the last commit operation.
13115      * <p>
13116      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13117      * of commit operations.
13118      */
13119     commit : function(){
13120         this.dirty = false;
13121         delete this.modified;
13122         this.editing = false;
13123         if(this.store){
13124             this.store.afterCommit(this);
13125         }
13126     },
13127
13128     // private
13129     hasError : function(){
13130         return this.error != null;
13131     },
13132
13133     // private
13134     clearError : function(){
13135         this.error = null;
13136     },
13137
13138     /**
13139      * Creates a copy of this record.
13140      * @param {String} id (optional) A new record id if you don't want to use this record's id
13141      * @return {Record}
13142      */
13143     copy : function(newId) {
13144         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13145     }
13146 };/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157
13158
13159 /**
13160  * @class Roo.data.Store
13161  * @extends Roo.util.Observable
13162  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13163  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13164  * <p>
13165  * 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
13166  * has no knowledge of the format of the data returned by the Proxy.<br>
13167  * <p>
13168  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13169  * instances from the data object. These records are cached and made available through accessor functions.
13170  * @constructor
13171  * Creates a new Store.
13172  * @param {Object} config A config object containing the objects needed for the Store to access data,
13173  * and read the data into Records.
13174  */
13175 Roo.data.Store = function(config){
13176     this.data = new Roo.util.MixedCollection(false);
13177     this.data.getKey = function(o){
13178         return o.id;
13179     };
13180     this.baseParams = {};
13181     // private
13182     this.paramNames = {
13183         "start" : "start",
13184         "limit" : "limit",
13185         "sort" : "sort",
13186         "dir" : "dir",
13187         "multisort" : "_multisort"
13188     };
13189
13190     if(config && config.data){
13191         this.inlineData = config.data;
13192         delete config.data;
13193     }
13194
13195     Roo.apply(this, config);
13196     
13197     if(this.reader){ // reader passed
13198         this.reader = Roo.factory(this.reader, Roo.data);
13199         this.reader.xmodule = this.xmodule || false;
13200         if(!this.recordType){
13201             this.recordType = this.reader.recordType;
13202         }
13203         if(this.reader.onMetaChange){
13204             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13205         }
13206     }
13207
13208     if(this.recordType){
13209         this.fields = this.recordType.prototype.fields;
13210     }
13211     this.modified = [];
13212
13213     this.addEvents({
13214         /**
13215          * @event datachanged
13216          * Fires when the data cache has changed, and a widget which is using this Store
13217          * as a Record cache should refresh its view.
13218          * @param {Store} this
13219          */
13220         datachanged : true,
13221         /**
13222          * @event metachange
13223          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13224          * @param {Store} this
13225          * @param {Object} meta The JSON metadata
13226          */
13227         metachange : true,
13228         /**
13229          * @event add
13230          * Fires when Records have been added to the Store
13231          * @param {Store} this
13232          * @param {Roo.data.Record[]} records The array of Records added
13233          * @param {Number} index The index at which the record(s) were added
13234          */
13235         add : true,
13236         /**
13237          * @event remove
13238          * Fires when a Record has been removed from the Store
13239          * @param {Store} this
13240          * @param {Roo.data.Record} record The Record that was removed
13241          * @param {Number} index The index at which the record was removed
13242          */
13243         remove : true,
13244         /**
13245          * @event update
13246          * Fires when a Record has been updated
13247          * @param {Store} this
13248          * @param {Roo.data.Record} record The Record that was updated
13249          * @param {String} operation The update operation being performed.  Value may be one of:
13250          * <pre><code>
13251  Roo.data.Record.EDIT
13252  Roo.data.Record.REJECT
13253  Roo.data.Record.COMMIT
13254          * </code></pre>
13255          */
13256         update : true,
13257         /**
13258          * @event clear
13259          * Fires when the data cache has been cleared.
13260          * @param {Store} this
13261          */
13262         clear : true,
13263         /**
13264          * @event beforeload
13265          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13266          * the load action will be canceled.
13267          * @param {Store} this
13268          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13269          */
13270         beforeload : true,
13271         /**
13272          * @event beforeloadadd
13273          * Fires after a new set of Records has been loaded.
13274          * @param {Store} this
13275          * @param {Roo.data.Record[]} records The Records that were loaded
13276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13277          */
13278         beforeloadadd : true,
13279         /**
13280          * @event load
13281          * Fires after a new set of Records has been loaded, before they are added to the store.
13282          * @param {Store} this
13283          * @param {Roo.data.Record[]} records The Records that were loaded
13284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13285          * @params {Object} return from reader
13286          */
13287         load : true,
13288         /**
13289          * @event loadexception
13290          * Fires if an exception occurs in the Proxy during loading.
13291          * Called with the signature of the Proxy's "loadexception" event.
13292          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13293          * 
13294          * @param {Proxy} 
13295          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13296          * @param {Object} load options 
13297          * @param {Object} jsonData from your request (normally this contains the Exception)
13298          */
13299         loadexception : true
13300     });
13301     
13302     if(this.proxy){
13303         this.proxy = Roo.factory(this.proxy, Roo.data);
13304         this.proxy.xmodule = this.xmodule || false;
13305         this.relayEvents(this.proxy,  ["loadexception"]);
13306     }
13307     this.sortToggle = {};
13308     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13309
13310     Roo.data.Store.superclass.constructor.call(this);
13311
13312     if(this.inlineData){
13313         this.loadData(this.inlineData);
13314         delete this.inlineData;
13315     }
13316 };
13317
13318 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13319      /**
13320     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13321     * without a remote query - used by combo/forms at present.
13322     */
13323     
13324     /**
13325     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13326     */
13327     /**
13328     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13329     */
13330     /**
13331     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13332     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13333     */
13334     /**
13335     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13336     * on any HTTP request
13337     */
13338     /**
13339     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13340     */
13341     /**
13342     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13343     */
13344     multiSort: false,
13345     /**
13346     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13347     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13348     */
13349     remoteSort : false,
13350
13351     /**
13352     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13353      * loaded or when a record is removed. (defaults to false).
13354     */
13355     pruneModifiedRecords : false,
13356
13357     // private
13358     lastOptions : null,
13359
13360     /**
13361      * Add Records to the Store and fires the add event.
13362      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13363      */
13364     add : function(records){
13365         records = [].concat(records);
13366         for(var i = 0, len = records.length; i < len; i++){
13367             records[i].join(this);
13368         }
13369         var index = this.data.length;
13370         this.data.addAll(records);
13371         this.fireEvent("add", this, records, index);
13372     },
13373
13374     /**
13375      * Remove a Record from the Store and fires the remove event.
13376      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13377      */
13378     remove : function(record){
13379         var index = this.data.indexOf(record);
13380         this.data.removeAt(index);
13381  
13382         if(this.pruneModifiedRecords){
13383             this.modified.remove(record);
13384         }
13385         this.fireEvent("remove", this, record, index);
13386     },
13387
13388     /**
13389      * Remove all Records from the Store and fires the clear event.
13390      */
13391     removeAll : function(){
13392         this.data.clear();
13393         if(this.pruneModifiedRecords){
13394             this.modified = [];
13395         }
13396         this.fireEvent("clear", this);
13397     },
13398
13399     /**
13400      * Inserts Records to the Store at the given index and fires the add event.
13401      * @param {Number} index The start index at which to insert the passed Records.
13402      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13403      */
13404     insert : function(index, records){
13405         records = [].concat(records);
13406         for(var i = 0, len = records.length; i < len; i++){
13407             this.data.insert(index, records[i]);
13408             records[i].join(this);
13409         }
13410         this.fireEvent("add", this, records, index);
13411     },
13412
13413     /**
13414      * Get the index within the cache of the passed Record.
13415      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13416      * @return {Number} The index of the passed Record. Returns -1 if not found.
13417      */
13418     indexOf : function(record){
13419         return this.data.indexOf(record);
13420     },
13421
13422     /**
13423      * Get the index within the cache of the Record with the passed id.
13424      * @param {String} id The id of the Record to find.
13425      * @return {Number} The index of the Record. Returns -1 if not found.
13426      */
13427     indexOfId : function(id){
13428         return this.data.indexOfKey(id);
13429     },
13430
13431     /**
13432      * Get the Record with the specified id.
13433      * @param {String} id The id of the Record to find.
13434      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13435      */
13436     getById : function(id){
13437         return this.data.key(id);
13438     },
13439
13440     /**
13441      * Get the Record at the specified index.
13442      * @param {Number} index The index of the Record to find.
13443      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13444      */
13445     getAt : function(index){
13446         return this.data.itemAt(index);
13447     },
13448
13449     /**
13450      * Returns a range of Records between specified indices.
13451      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13452      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13453      * @return {Roo.data.Record[]} An array of Records
13454      */
13455     getRange : function(start, end){
13456         return this.data.getRange(start, end);
13457     },
13458
13459     // private
13460     storeOptions : function(o){
13461         o = Roo.apply({}, o);
13462         delete o.callback;
13463         delete o.scope;
13464         this.lastOptions = o;
13465     },
13466
13467     /**
13468      * Loads the Record cache from the configured Proxy using the configured Reader.
13469      * <p>
13470      * If using remote paging, then the first load call must specify the <em>start</em>
13471      * and <em>limit</em> properties in the options.params property to establish the initial
13472      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13473      * <p>
13474      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13475      * and this call will return before the new data has been loaded. Perform any post-processing
13476      * in a callback function, or in a "load" event handler.</strong>
13477      * <p>
13478      * @param {Object} options An object containing properties which control loading options:<ul>
13479      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13480      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13481      * passed the following arguments:<ul>
13482      * <li>r : Roo.data.Record[]</li>
13483      * <li>options: Options object from the load call</li>
13484      * <li>success: Boolean success indicator</li></ul></li>
13485      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13486      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13487      * </ul>
13488      */
13489     load : function(options){
13490         options = options || {};
13491         if(this.fireEvent("beforeload", this, options) !== false){
13492             this.storeOptions(options);
13493             var p = Roo.apply(options.params || {}, this.baseParams);
13494             // if meta was not loaded from remote source.. try requesting it.
13495             if (!this.reader.metaFromRemote) {
13496                 p._requestMeta = 1;
13497             }
13498             if(this.sortInfo && this.remoteSort){
13499                 var pn = this.paramNames;
13500                 p[pn["sort"]] = this.sortInfo.field;
13501                 p[pn["dir"]] = this.sortInfo.direction;
13502             }
13503             if (this.multiSort) {
13504                 var pn = this.paramNames;
13505                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13506             }
13507             
13508             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13509         }
13510     },
13511
13512     /**
13513      * Reloads the Record cache from the configured Proxy using the configured Reader and
13514      * the options from the last load operation performed.
13515      * @param {Object} options (optional) An object containing properties which may override the options
13516      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13517      * the most recently used options are reused).
13518      */
13519     reload : function(options){
13520         this.load(Roo.applyIf(options||{}, this.lastOptions));
13521     },
13522
13523     // private
13524     // Called as a callback by the Reader during a load operation.
13525     loadRecords : function(o, options, success){
13526         if(!o || success === false){
13527             if(success !== false){
13528                 this.fireEvent("load", this, [], options, o);
13529             }
13530             if(options.callback){
13531                 options.callback.call(options.scope || this, [], options, false);
13532             }
13533             return;
13534         }
13535         // if data returned failure - throw an exception.
13536         if (o.success === false) {
13537             // show a message if no listener is registered.
13538             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13539                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13540             }
13541             // loadmask wil be hooked into this..
13542             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13543             return;
13544         }
13545         var r = o.records, t = o.totalRecords || r.length;
13546         
13547         this.fireEvent("beforeloadadd", this, r, options, o);
13548         
13549         if(!options || options.add !== true){
13550             if(this.pruneModifiedRecords){
13551                 this.modified = [];
13552             }
13553             for(var i = 0, len = r.length; i < len; i++){
13554                 r[i].join(this);
13555             }
13556             if(this.snapshot){
13557                 this.data = this.snapshot;
13558                 delete this.snapshot;
13559             }
13560             this.data.clear();
13561             this.data.addAll(r);
13562             this.totalLength = t;
13563             this.applySort();
13564             this.fireEvent("datachanged", this);
13565         }else{
13566             this.totalLength = Math.max(t, this.data.length+r.length);
13567             this.add(r);
13568         }
13569         
13570         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13571                 
13572             var e = new Roo.data.Record({});
13573
13574             e.set(this.parent.displayField, this.parent.emptyTitle);
13575             e.set(this.parent.valueField, '');
13576
13577             this.insert(0, e);
13578         }
13579             
13580         this.fireEvent("load", this, r, options, o);
13581         if(options.callback){
13582             options.callback.call(options.scope || this, r, options, true);
13583         }
13584     },
13585
13586
13587     /**
13588      * Loads data from a passed data block. A Reader which understands the format of the data
13589      * must have been configured in the constructor.
13590      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13591      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13592      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13593      */
13594     loadData : function(o, append){
13595         var r = this.reader.readRecords(o);
13596         this.loadRecords(r, {add: append}, true);
13597     },
13598     
13599      /**
13600      * using 'cn' the nested child reader read the child array into it's child stores.
13601      * @param {Object} rec The record with a 'children array
13602      */
13603     loadDataFromChildren : function(rec)
13604     {
13605         this.loadData(this.reader.toLoadData(rec));
13606     },
13607     
13608
13609     /**
13610      * Gets the number of cached records.
13611      * <p>
13612      * <em>If using paging, this may not be the total size of the dataset. If the data object
13613      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13614      * the data set size</em>
13615      */
13616     getCount : function(){
13617         return this.data.length || 0;
13618     },
13619
13620     /**
13621      * Gets the total number of records in the dataset as returned by the server.
13622      * <p>
13623      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13624      * the dataset size</em>
13625      */
13626     getTotalCount : function(){
13627         return this.totalLength || 0;
13628     },
13629
13630     /**
13631      * Returns the sort state of the Store as an object with two properties:
13632      * <pre><code>
13633  field {String} The name of the field by which the Records are sorted
13634  direction {String} The sort order, "ASC" or "DESC"
13635      * </code></pre>
13636      */
13637     getSortState : function(){
13638         return this.sortInfo;
13639     },
13640
13641     // private
13642     applySort : function(){
13643         if(this.sortInfo && !this.remoteSort){
13644             var s = this.sortInfo, f = s.field;
13645             var st = this.fields.get(f).sortType;
13646             var fn = function(r1, r2){
13647                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13648                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13649             };
13650             this.data.sort(s.direction, fn);
13651             if(this.snapshot && this.snapshot != this.data){
13652                 this.snapshot.sort(s.direction, fn);
13653             }
13654         }
13655     },
13656
13657     /**
13658      * Sets the default sort column and order to be used by the next load operation.
13659      * @param {String} fieldName The name of the field to sort by.
13660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13661      */
13662     setDefaultSort : function(field, dir){
13663         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13664     },
13665
13666     /**
13667      * Sort the Records.
13668      * If remote sorting is used, the sort is performed on the server, and the cache is
13669      * reloaded. If local sorting is used, the cache is sorted internally.
13670      * @param {String} fieldName The name of the field to sort by.
13671      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13672      */
13673     sort : function(fieldName, dir){
13674         var f = this.fields.get(fieldName);
13675         if(!dir){
13676             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13677             
13678             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13679                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13680             }else{
13681                 dir = f.sortDir;
13682             }
13683         }
13684         this.sortToggle[f.name] = dir;
13685         this.sortInfo = {field: f.name, direction: dir};
13686         if(!this.remoteSort){
13687             this.applySort();
13688             this.fireEvent("datachanged", this);
13689         }else{
13690             this.load(this.lastOptions);
13691         }
13692     },
13693
13694     /**
13695      * Calls the specified function for each of the Records in the cache.
13696      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13697      * Returning <em>false</em> aborts and exits the iteration.
13698      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13699      */
13700     each : function(fn, scope){
13701         this.data.each(fn, scope);
13702     },
13703
13704     /**
13705      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13706      * (e.g., during paging).
13707      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13708      */
13709     getModifiedRecords : function(){
13710         return this.modified;
13711     },
13712
13713     // private
13714     createFilterFn : function(property, value, anyMatch){
13715         if(!value.exec){ // not a regex
13716             value = String(value);
13717             if(value.length == 0){
13718                 return false;
13719             }
13720             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13721         }
13722         return function(r){
13723             return value.test(r.data[property]);
13724         };
13725     },
13726
13727     /**
13728      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13729      * @param {String} property A field on your records
13730      * @param {Number} start The record index to start at (defaults to 0)
13731      * @param {Number} end The last record index to include (defaults to length - 1)
13732      * @return {Number} The sum
13733      */
13734     sum : function(property, start, end){
13735         var rs = this.data.items, v = 0;
13736         start = start || 0;
13737         end = (end || end === 0) ? end : rs.length-1;
13738
13739         for(var i = start; i <= end; i++){
13740             v += (rs[i].data[property] || 0);
13741         }
13742         return v;
13743     },
13744
13745     /**
13746      * Filter the records by a specified property.
13747      * @param {String} field A field on your records
13748      * @param {String/RegExp} value Either a string that the field
13749      * should start with or a RegExp to test against the field
13750      * @param {Boolean} anyMatch True to match any part not just the beginning
13751      */
13752     filter : function(property, value, anyMatch){
13753         var fn = this.createFilterFn(property, value, anyMatch);
13754         return fn ? this.filterBy(fn) : this.clearFilter();
13755     },
13756
13757     /**
13758      * Filter by a function. The specified function will be called with each
13759      * record in this data source. If the function returns true the record is included,
13760      * otherwise it is filtered.
13761      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13762      * @param {Object} scope (optional) The scope of the function (defaults to this)
13763      */
13764     filterBy : function(fn, scope){
13765         this.snapshot = this.snapshot || this.data;
13766         this.data = this.queryBy(fn, scope||this);
13767         this.fireEvent("datachanged", this);
13768     },
13769
13770     /**
13771      * Query the records by a specified property.
13772      * @param {String} field A field on your records
13773      * @param {String/RegExp} value Either a string that the field
13774      * should start with or a RegExp to test against the field
13775      * @param {Boolean} anyMatch True to match any part not just the beginning
13776      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13777      */
13778     query : function(property, value, anyMatch){
13779         var fn = this.createFilterFn(property, value, anyMatch);
13780         return fn ? this.queryBy(fn) : this.data.clone();
13781     },
13782
13783     /**
13784      * Query by a function. The specified function will be called with each
13785      * record in this data source. If the function returns true the record is included
13786      * in the results.
13787      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13788      * @param {Object} scope (optional) The scope of the function (defaults to this)
13789       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13790      **/
13791     queryBy : function(fn, scope){
13792         var data = this.snapshot || this.data;
13793         return data.filterBy(fn, scope||this);
13794     },
13795
13796     /**
13797      * Collects unique values for a particular dataIndex from this store.
13798      * @param {String} dataIndex The property to collect
13799      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13800      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13801      * @return {Array} An array of the unique values
13802      **/
13803     collect : function(dataIndex, allowNull, bypassFilter){
13804         var d = (bypassFilter === true && this.snapshot) ?
13805                 this.snapshot.items : this.data.items;
13806         var v, sv, r = [], l = {};
13807         for(var i = 0, len = d.length; i < len; i++){
13808             v = d[i].data[dataIndex];
13809             sv = String(v);
13810             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13811                 l[sv] = true;
13812                 r[r.length] = v;
13813             }
13814         }
13815         return r;
13816     },
13817
13818     /**
13819      * Revert to a view of the Record cache with no filtering applied.
13820      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13821      */
13822     clearFilter : function(suppressEvent){
13823         if(this.snapshot && this.snapshot != this.data){
13824             this.data = this.snapshot;
13825             delete this.snapshot;
13826             if(suppressEvent !== true){
13827                 this.fireEvent("datachanged", this);
13828             }
13829         }
13830     },
13831
13832     // private
13833     afterEdit : function(record){
13834         if(this.modified.indexOf(record) == -1){
13835             this.modified.push(record);
13836         }
13837         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13838     },
13839     
13840     // private
13841     afterReject : function(record){
13842         this.modified.remove(record);
13843         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13844     },
13845
13846     // private
13847     afterCommit : function(record){
13848         this.modified.remove(record);
13849         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13850     },
13851
13852     /**
13853      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13854      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13855      */
13856     commitChanges : function(){
13857         var m = this.modified.slice(0);
13858         this.modified = [];
13859         for(var i = 0, len = m.length; i < len; i++){
13860             m[i].commit();
13861         }
13862     },
13863
13864     /**
13865      * Cancel outstanding changes on all changed records.
13866      */
13867     rejectChanges : function(){
13868         var m = this.modified.slice(0);
13869         this.modified = [];
13870         for(var i = 0, len = m.length; i < len; i++){
13871             m[i].reject();
13872         }
13873     },
13874
13875     onMetaChange : function(meta, rtype, o){
13876         this.recordType = rtype;
13877         this.fields = rtype.prototype.fields;
13878         delete this.snapshot;
13879         this.sortInfo = meta.sortInfo || this.sortInfo;
13880         this.modified = [];
13881         this.fireEvent('metachange', this, this.reader.meta);
13882     },
13883     
13884     moveIndex : function(data, type)
13885     {
13886         var index = this.indexOf(data);
13887         
13888         var newIndex = index + type;
13889         
13890         this.remove(data);
13891         
13892         this.insert(newIndex, data);
13893         
13894     }
13895 });/*
13896  * Based on:
13897  * Ext JS Library 1.1.1
13898  * Copyright(c) 2006-2007, Ext JS, LLC.
13899  *
13900  * Originally Released Under LGPL - original licence link has changed is not relivant.
13901  *
13902  * Fork - LGPL
13903  * <script type="text/javascript">
13904  */
13905
13906 /**
13907  * @class Roo.data.SimpleStore
13908  * @extends Roo.data.Store
13909  * Small helper class to make creating Stores from Array data easier.
13910  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13911  * @cfg {Array} fields An array of field definition objects, or field name strings.
13912  * @cfg {Object} an existing reader (eg. copied from another store)
13913  * @cfg {Array} data The multi-dimensional array of data
13914  * @constructor
13915  * @param {Object} config
13916  */
13917 Roo.data.SimpleStore = function(config)
13918 {
13919     Roo.data.SimpleStore.superclass.constructor.call(this, {
13920         isLocal : true,
13921         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13922                 id: config.id
13923             },
13924             Roo.data.Record.create(config.fields)
13925         ),
13926         proxy : new Roo.data.MemoryProxy(config.data)
13927     });
13928     this.load();
13929 };
13930 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13931  * Based on:
13932  * Ext JS Library 1.1.1
13933  * Copyright(c) 2006-2007, Ext JS, LLC.
13934  *
13935  * Originally Released Under LGPL - original licence link has changed is not relivant.
13936  *
13937  * Fork - LGPL
13938  * <script type="text/javascript">
13939  */
13940
13941 /**
13942 /**
13943  * @extends Roo.data.Store
13944  * @class Roo.data.JsonStore
13945  * Small helper class to make creating Stores for JSON data easier. <br/>
13946 <pre><code>
13947 var store = new Roo.data.JsonStore({
13948     url: 'get-images.php',
13949     root: 'images',
13950     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13951 });
13952 </code></pre>
13953  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13954  * JsonReader and HttpProxy (unless inline data is provided).</b>
13955  * @cfg {Array} fields An array of field definition objects, or field name strings.
13956  * @constructor
13957  * @param {Object} config
13958  */
13959 Roo.data.JsonStore = function(c){
13960     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13961         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13962         reader: new Roo.data.JsonReader(c, c.fields)
13963     }));
13964 };
13965 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13966  * Based on:
13967  * Ext JS Library 1.1.1
13968  * Copyright(c) 2006-2007, Ext JS, LLC.
13969  *
13970  * Originally Released Under LGPL - original licence link has changed is not relivant.
13971  *
13972  * Fork - LGPL
13973  * <script type="text/javascript">
13974  */
13975
13976  
13977 Roo.data.Field = function(config){
13978     if(typeof config == "string"){
13979         config = {name: config};
13980     }
13981     Roo.apply(this, config);
13982     
13983     if(!this.type){
13984         this.type = "auto";
13985     }
13986     
13987     var st = Roo.data.SortTypes;
13988     // named sortTypes are supported, here we look them up
13989     if(typeof this.sortType == "string"){
13990         this.sortType = st[this.sortType];
13991     }
13992     
13993     // set default sortType for strings and dates
13994     if(!this.sortType){
13995         switch(this.type){
13996             case "string":
13997                 this.sortType = st.asUCString;
13998                 break;
13999             case "date":
14000                 this.sortType = st.asDate;
14001                 break;
14002             default:
14003                 this.sortType = st.none;
14004         }
14005     }
14006
14007     // define once
14008     var stripRe = /[\$,%]/g;
14009
14010     // prebuilt conversion function for this field, instead of
14011     // switching every time we're reading a value
14012     if(!this.convert){
14013         var cv, dateFormat = this.dateFormat;
14014         switch(this.type){
14015             case "":
14016             case "auto":
14017             case undefined:
14018                 cv = function(v){ return v; };
14019                 break;
14020             case "string":
14021                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14022                 break;
14023             case "int":
14024                 cv = function(v){
14025                     return v !== undefined && v !== null && v !== '' ?
14026                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14027                     };
14028                 break;
14029             case "float":
14030                 cv = function(v){
14031                     return v !== undefined && v !== null && v !== '' ?
14032                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14033                     };
14034                 break;
14035             case "bool":
14036             case "boolean":
14037                 cv = function(v){ return v === true || v === "true" || v == 1; };
14038                 break;
14039             case "date":
14040                 cv = function(v){
14041                     if(!v){
14042                         return '';
14043                     }
14044                     if(v instanceof Date){
14045                         return v;
14046                     }
14047                     if(dateFormat){
14048                         if(dateFormat == "timestamp"){
14049                             return new Date(v*1000);
14050                         }
14051                         return Date.parseDate(v, dateFormat);
14052                     }
14053                     var parsed = Date.parse(v);
14054                     return parsed ? new Date(parsed) : null;
14055                 };
14056              break;
14057             
14058         }
14059         this.convert = cv;
14060     }
14061 };
14062
14063 Roo.data.Field.prototype = {
14064     dateFormat: null,
14065     defaultValue: "",
14066     mapping: null,
14067     sortType : null,
14068     sortDir : "ASC"
14069 };/*
14070  * Based on:
14071  * Ext JS Library 1.1.1
14072  * Copyright(c) 2006-2007, Ext JS, LLC.
14073  *
14074  * Originally Released Under LGPL - original licence link has changed is not relivant.
14075  *
14076  * Fork - LGPL
14077  * <script type="text/javascript">
14078  */
14079  
14080 // Base class for reading structured data from a data source.  This class is intended to be
14081 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14082
14083 /**
14084  * @class Roo.data.DataReader
14085  * Base class for reading structured data from a data source.  This class is intended to be
14086  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14087  */
14088
14089 Roo.data.DataReader = function(meta, recordType){
14090     
14091     this.meta = meta;
14092     
14093     this.recordType = recordType instanceof Array ? 
14094         Roo.data.Record.create(recordType) : recordType;
14095 };
14096
14097 Roo.data.DataReader.prototype = {
14098     
14099     
14100     readerType : 'Data',
14101      /**
14102      * Create an empty record
14103      * @param {Object} data (optional) - overlay some values
14104      * @return {Roo.data.Record} record created.
14105      */
14106     newRow :  function(d) {
14107         var da =  {};
14108         this.recordType.prototype.fields.each(function(c) {
14109             switch( c.type) {
14110                 case 'int' : da[c.name] = 0; break;
14111                 case 'date' : da[c.name] = new Date(); break;
14112                 case 'float' : da[c.name] = 0.0; break;
14113                 case 'boolean' : da[c.name] = false; break;
14114                 default : da[c.name] = ""; break;
14115             }
14116             
14117         });
14118         return new this.recordType(Roo.apply(da, d));
14119     }
14120     
14121     
14122 };/*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132
14133 /**
14134  * @class Roo.data.DataProxy
14135  * @extends Roo.data.Observable
14136  * This class is an abstract base class for implementations which provide retrieval of
14137  * unformatted data objects.<br>
14138  * <p>
14139  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14140  * (of the appropriate type which knows how to parse the data object) to provide a block of
14141  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14142  * <p>
14143  * Custom implementations must implement the load method as described in
14144  * {@link Roo.data.HttpProxy#load}.
14145  */
14146 Roo.data.DataProxy = function(){
14147     this.addEvents({
14148         /**
14149          * @event beforeload
14150          * Fires before a network request is made to retrieve a data object.
14151          * @param {Object} This DataProxy object.
14152          * @param {Object} params The params parameter to the load function.
14153          */
14154         beforeload : true,
14155         /**
14156          * @event load
14157          * Fires before the load method's callback is called.
14158          * @param {Object} This DataProxy object.
14159          * @param {Object} o The data object.
14160          * @param {Object} arg The callback argument object passed to the load function.
14161          */
14162         load : true,
14163         /**
14164          * @event loadexception
14165          * Fires if an Exception occurs during data retrieval.
14166          * @param {Object} This DataProxy object.
14167          * @param {Object} o The data object.
14168          * @param {Object} arg The callback argument object passed to the load function.
14169          * @param {Object} e The Exception.
14170          */
14171         loadexception : true
14172     });
14173     Roo.data.DataProxy.superclass.constructor.call(this);
14174 };
14175
14176 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14177
14178     /**
14179      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14180      */
14181 /*
14182  * Based on:
14183  * Ext JS Library 1.1.1
14184  * Copyright(c) 2006-2007, Ext JS, LLC.
14185  *
14186  * Originally Released Under LGPL - original licence link has changed is not relivant.
14187  *
14188  * Fork - LGPL
14189  * <script type="text/javascript">
14190  */
14191 /**
14192  * @class Roo.data.MemoryProxy
14193  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14194  * to the Reader when its load method is called.
14195  * @constructor
14196  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14197  */
14198 Roo.data.MemoryProxy = function(data){
14199     if (data.data) {
14200         data = data.data;
14201     }
14202     Roo.data.MemoryProxy.superclass.constructor.call(this);
14203     this.data = data;
14204 };
14205
14206 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14207     
14208     /**
14209      * Load data from the requested source (in this case an in-memory
14210      * data object passed to the constructor), read the data object into
14211      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14212      * process that block using the passed callback.
14213      * @param {Object} params This parameter is not used by the MemoryProxy class.
14214      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14215      * object into a block of Roo.data.Records.
14216      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14217      * The function must be passed <ul>
14218      * <li>The Record block object</li>
14219      * <li>The "arg" argument from the load function</li>
14220      * <li>A boolean success indicator</li>
14221      * </ul>
14222      * @param {Object} scope The scope in which to call the callback
14223      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14224      */
14225     load : function(params, reader, callback, scope, arg){
14226         params = params || {};
14227         var result;
14228         try {
14229             result = reader.readRecords(params.data ? params.data :this.data);
14230         }catch(e){
14231             this.fireEvent("loadexception", this, arg, null, e);
14232             callback.call(scope, null, arg, false);
14233             return;
14234         }
14235         callback.call(scope, result, arg, true);
14236     },
14237     
14238     // private
14239     update : function(params, records){
14240         
14241     }
14242 });/*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252 /**
14253  * @class Roo.data.HttpProxy
14254  * @extends Roo.data.DataProxy
14255  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14256  * configured to reference a certain URL.<br><br>
14257  * <p>
14258  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14259  * from which the running page was served.<br><br>
14260  * <p>
14261  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14262  * <p>
14263  * Be aware that to enable the browser to parse an XML document, the server must set
14264  * the Content-Type header in the HTTP response to "text/xml".
14265  * @constructor
14266  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14267  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14268  * will be used to make the request.
14269  */
14270 Roo.data.HttpProxy = function(conn){
14271     Roo.data.HttpProxy.superclass.constructor.call(this);
14272     // is conn a conn config or a real conn?
14273     this.conn = conn;
14274     this.useAjax = !conn || !conn.events;
14275   
14276 };
14277
14278 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14279     // thse are take from connection...
14280     
14281     /**
14282      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14283      */
14284     /**
14285      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14286      * extra parameters to each request made by this object. (defaults to undefined)
14287      */
14288     /**
14289      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14290      *  to each request made by this object. (defaults to undefined)
14291      */
14292     /**
14293      * @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)
14294      */
14295     /**
14296      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14297      */
14298      /**
14299      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14300      * @type Boolean
14301      */
14302   
14303
14304     /**
14305      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14306      * @type Boolean
14307      */
14308     /**
14309      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14310      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14311      * a finer-grained basis than the DataProxy events.
14312      */
14313     getConnection : function(){
14314         return this.useAjax ? Roo.Ajax : this.conn;
14315     },
14316
14317     /**
14318      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14319      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14320      * process that block using the passed callback.
14321      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14322      * for the request to the remote server.
14323      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14324      * object into a block of Roo.data.Records.
14325      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14326      * The function must be passed <ul>
14327      * <li>The Record block object</li>
14328      * <li>The "arg" argument from the load function</li>
14329      * <li>A boolean success indicator</li>
14330      * </ul>
14331      * @param {Object} scope The scope in which to call the callback
14332      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14333      */
14334     load : function(params, reader, callback, scope, arg){
14335         if(this.fireEvent("beforeload", this, params) !== false){
14336             var  o = {
14337                 params : params || {},
14338                 request: {
14339                     callback : callback,
14340                     scope : scope,
14341                     arg : arg
14342                 },
14343                 reader: reader,
14344                 callback : this.loadResponse,
14345                 scope: this
14346             };
14347             if(this.useAjax){
14348                 Roo.applyIf(o, this.conn);
14349                 if(this.activeRequest){
14350                     Roo.Ajax.abort(this.activeRequest);
14351                 }
14352                 this.activeRequest = Roo.Ajax.request(o);
14353             }else{
14354                 this.conn.request(o);
14355             }
14356         }else{
14357             callback.call(scope||this, null, arg, false);
14358         }
14359     },
14360
14361     // private
14362     loadResponse : function(o, success, response){
14363         delete this.activeRequest;
14364         if(!success){
14365             this.fireEvent("loadexception", this, o, response);
14366             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14367             return;
14368         }
14369         var result;
14370         try {
14371             result = o.reader.read(response);
14372         }catch(e){
14373             this.fireEvent("loadexception", this, o, response, e);
14374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14375             return;
14376         }
14377         
14378         this.fireEvent("load", this, o, o.request.arg);
14379         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14380     },
14381
14382     // private
14383     update : function(dataSet){
14384
14385     },
14386
14387     // private
14388     updateResponse : function(dataSet){
14389
14390     }
14391 });/*
14392  * Based on:
14393  * Ext JS Library 1.1.1
14394  * Copyright(c) 2006-2007, Ext JS, LLC.
14395  *
14396  * Originally Released Under LGPL - original licence link has changed is not relivant.
14397  *
14398  * Fork - LGPL
14399  * <script type="text/javascript">
14400  */
14401
14402 /**
14403  * @class Roo.data.ScriptTagProxy
14404  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14405  * other than the originating domain of the running page.<br><br>
14406  * <p>
14407  * <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
14408  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14409  * <p>
14410  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14411  * source code that is used as the source inside a &lt;script> tag.<br><br>
14412  * <p>
14413  * In order for the browser to process the returned data, the server must wrap the data object
14414  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14415  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14416  * depending on whether the callback name was passed:
14417  * <p>
14418  * <pre><code>
14419 boolean scriptTag = false;
14420 String cb = request.getParameter("callback");
14421 if (cb != null) {
14422     scriptTag = true;
14423     response.setContentType("text/javascript");
14424 } else {
14425     response.setContentType("application/x-json");
14426 }
14427 Writer out = response.getWriter();
14428 if (scriptTag) {
14429     out.write(cb + "(");
14430 }
14431 out.print(dataBlock.toJsonString());
14432 if (scriptTag) {
14433     out.write(");");
14434 }
14435 </pre></code>
14436  *
14437  * @constructor
14438  * @param {Object} config A configuration object.
14439  */
14440 Roo.data.ScriptTagProxy = function(config){
14441     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14442     Roo.apply(this, config);
14443     this.head = document.getElementsByTagName("head")[0];
14444 };
14445
14446 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14447
14448 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14449     /**
14450      * @cfg {String} url The URL from which to request the data object.
14451      */
14452     /**
14453      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14454      */
14455     timeout : 30000,
14456     /**
14457      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14458      * the server the name of the callback function set up by the load call to process the returned data object.
14459      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14460      * javascript output which calls this named function passing the data object as its only parameter.
14461      */
14462     callbackParam : "callback",
14463     /**
14464      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14465      * name to the request.
14466      */
14467     nocache : true,
14468
14469     /**
14470      * Load data from the configured URL, read the data object into
14471      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14472      * process that block using the passed callback.
14473      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14474      * for the request to the remote server.
14475      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14476      * object into a block of Roo.data.Records.
14477      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14478      * The function must be passed <ul>
14479      * <li>The Record block object</li>
14480      * <li>The "arg" argument from the load function</li>
14481      * <li>A boolean success indicator</li>
14482      * </ul>
14483      * @param {Object} scope The scope in which to call the callback
14484      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14485      */
14486     load : function(params, reader, callback, scope, arg){
14487         if(this.fireEvent("beforeload", this, params) !== false){
14488
14489             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14490
14491             var url = this.url;
14492             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14493             if(this.nocache){
14494                 url += "&_dc=" + (new Date().getTime());
14495             }
14496             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14497             var trans = {
14498                 id : transId,
14499                 cb : "stcCallback"+transId,
14500                 scriptId : "stcScript"+transId,
14501                 params : params,
14502                 arg : arg,
14503                 url : url,
14504                 callback : callback,
14505                 scope : scope,
14506                 reader : reader
14507             };
14508             var conn = this;
14509
14510             window[trans.cb] = function(o){
14511                 conn.handleResponse(o, trans);
14512             };
14513
14514             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14515
14516             if(this.autoAbort !== false){
14517                 this.abort();
14518             }
14519
14520             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14521
14522             var script = document.createElement("script");
14523             script.setAttribute("src", url);
14524             script.setAttribute("type", "text/javascript");
14525             script.setAttribute("id", trans.scriptId);
14526             this.head.appendChild(script);
14527
14528             this.trans = trans;
14529         }else{
14530             callback.call(scope||this, null, arg, false);
14531         }
14532     },
14533
14534     // private
14535     isLoading : function(){
14536         return this.trans ? true : false;
14537     },
14538
14539     /**
14540      * Abort the current server request.
14541      */
14542     abort : function(){
14543         if(this.isLoading()){
14544             this.destroyTrans(this.trans);
14545         }
14546     },
14547
14548     // private
14549     destroyTrans : function(trans, isLoaded){
14550         this.head.removeChild(document.getElementById(trans.scriptId));
14551         clearTimeout(trans.timeoutId);
14552         if(isLoaded){
14553             window[trans.cb] = undefined;
14554             try{
14555                 delete window[trans.cb];
14556             }catch(e){}
14557         }else{
14558             // if hasn't been loaded, wait for load to remove it to prevent script error
14559             window[trans.cb] = function(){
14560                 window[trans.cb] = undefined;
14561                 try{
14562                     delete window[trans.cb];
14563                 }catch(e){}
14564             };
14565         }
14566     },
14567
14568     // private
14569     handleResponse : function(o, trans){
14570         this.trans = false;
14571         this.destroyTrans(trans, true);
14572         var result;
14573         try {
14574             result = trans.reader.readRecords(o);
14575         }catch(e){
14576             this.fireEvent("loadexception", this, o, trans.arg, e);
14577             trans.callback.call(trans.scope||window, null, trans.arg, false);
14578             return;
14579         }
14580         this.fireEvent("load", this, o, trans.arg);
14581         trans.callback.call(trans.scope||window, result, trans.arg, true);
14582     },
14583
14584     // private
14585     handleFailure : function(trans){
14586         this.trans = false;
14587         this.destroyTrans(trans, false);
14588         this.fireEvent("loadexception", this, null, trans.arg);
14589         trans.callback.call(trans.scope||window, null, trans.arg, false);
14590     }
14591 });/*
14592  * Based on:
14593  * Ext JS Library 1.1.1
14594  * Copyright(c) 2006-2007, Ext JS, LLC.
14595  *
14596  * Originally Released Under LGPL - original licence link has changed is not relivant.
14597  *
14598  * Fork - LGPL
14599  * <script type="text/javascript">
14600  */
14601
14602 /**
14603  * @class Roo.data.JsonReader
14604  * @extends Roo.data.DataReader
14605  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14606  * based on mappings in a provided Roo.data.Record constructor.
14607  * 
14608  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14609  * in the reply previously. 
14610  * 
14611  * <p>
14612  * Example code:
14613  * <pre><code>
14614 var RecordDef = Roo.data.Record.create([
14615     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14616     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14617 ]);
14618 var myReader = new Roo.data.JsonReader({
14619     totalProperty: "results",    // The property which contains the total dataset size (optional)
14620     root: "rows",                // The property which contains an Array of row objects
14621     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14622 }, RecordDef);
14623 </code></pre>
14624  * <p>
14625  * This would consume a JSON file like this:
14626  * <pre><code>
14627 { 'results': 2, 'rows': [
14628     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14629     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14630 }
14631 </code></pre>
14632  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14633  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14634  * paged from the remote server.
14635  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14636  * @cfg {String} root name of the property which contains the Array of row objects.
14637  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14638  * @cfg {Array} fields Array of field definition objects
14639  * @constructor
14640  * Create a new JsonReader
14641  * @param {Object} meta Metadata configuration options
14642  * @param {Object} recordType Either an Array of field definition objects,
14643  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14644  */
14645 Roo.data.JsonReader = function(meta, recordType){
14646     
14647     meta = meta || {};
14648     // set some defaults:
14649     Roo.applyIf(meta, {
14650         totalProperty: 'total',
14651         successProperty : 'success',
14652         root : 'data',
14653         id : 'id'
14654     });
14655     
14656     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14657 };
14658 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14659     
14660     readerType : 'Json',
14661     
14662     /**
14663      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14664      * Used by Store query builder to append _requestMeta to params.
14665      * 
14666      */
14667     metaFromRemote : false,
14668     /**
14669      * This method is only used by a DataProxy which has retrieved data from a remote server.
14670      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14671      * @return {Object} data A data block which is used by an Roo.data.Store object as
14672      * a cache of Roo.data.Records.
14673      */
14674     read : function(response){
14675         var json = response.responseText;
14676        
14677         var o = /* eval:var:o */ eval("("+json+")");
14678         if(!o) {
14679             throw {message: "JsonReader.read: Json object not found"};
14680         }
14681         
14682         if(o.metaData){
14683             
14684             delete this.ef;
14685             this.metaFromRemote = true;
14686             this.meta = o.metaData;
14687             this.recordType = Roo.data.Record.create(o.metaData.fields);
14688             this.onMetaChange(this.meta, this.recordType, o);
14689         }
14690         return this.readRecords(o);
14691     },
14692
14693     // private function a store will implement
14694     onMetaChange : function(meta, recordType, o){
14695
14696     },
14697
14698     /**
14699          * @ignore
14700          */
14701     simpleAccess: function(obj, subsc) {
14702         return obj[subsc];
14703     },
14704
14705         /**
14706          * @ignore
14707          */
14708     getJsonAccessor: function(){
14709         var re = /[\[\.]/;
14710         return function(expr) {
14711             try {
14712                 return(re.test(expr))
14713                     ? new Function("obj", "return obj." + expr)
14714                     : function(obj){
14715                         return obj[expr];
14716                     };
14717             } catch(e){}
14718             return Roo.emptyFn;
14719         };
14720     }(),
14721
14722     /**
14723      * Create a data block containing Roo.data.Records from an XML document.
14724      * @param {Object} o An object which contains an Array of row objects in the property specified
14725      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14726      * which contains the total size of the dataset.
14727      * @return {Object} data A data block which is used by an Roo.data.Store object as
14728      * a cache of Roo.data.Records.
14729      */
14730     readRecords : function(o){
14731         /**
14732          * After any data loads, the raw JSON data is available for further custom processing.
14733          * @type Object
14734          */
14735         this.o = o;
14736         var s = this.meta, Record = this.recordType,
14737             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14738
14739 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14740         if (!this.ef) {
14741             if(s.totalProperty) {
14742                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14743                 }
14744                 if(s.successProperty) {
14745                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14746                 }
14747                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14748                 if (s.id) {
14749                         var g = this.getJsonAccessor(s.id);
14750                         this.getId = function(rec) {
14751                                 var r = g(rec);  
14752                                 return (r === undefined || r === "") ? null : r;
14753                         };
14754                 } else {
14755                         this.getId = function(){return null;};
14756                 }
14757             this.ef = [];
14758             for(var jj = 0; jj < fl; jj++){
14759                 f = fi[jj];
14760                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14761                 this.ef[jj] = this.getJsonAccessor(map);
14762             }
14763         }
14764
14765         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14766         if(s.totalProperty){
14767             var vt = parseInt(this.getTotal(o), 10);
14768             if(!isNaN(vt)){
14769                 totalRecords = vt;
14770             }
14771         }
14772         if(s.successProperty){
14773             var vs = this.getSuccess(o);
14774             if(vs === false || vs === 'false'){
14775                 success = false;
14776             }
14777         }
14778         var records = [];
14779         for(var i = 0; i < c; i++){
14780                 var n = root[i];
14781             var values = {};
14782             var id = this.getId(n);
14783             for(var j = 0; j < fl; j++){
14784                 f = fi[j];
14785             var v = this.ef[j](n);
14786             if (!f.convert) {
14787                 Roo.log('missing convert for ' + f.name);
14788                 Roo.log(f);
14789                 continue;
14790             }
14791             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14792             }
14793             var record = new Record(values, id);
14794             record.json = n;
14795             records[i] = record;
14796         }
14797         return {
14798             raw : o,
14799             success : success,
14800             records : records,
14801             totalRecords : totalRecords
14802         };
14803     },
14804     // used when loading children.. @see loadDataFromChildren
14805     toLoadData: function(rec)
14806     {
14807         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14808         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14809         return { data : data, total : data.length };
14810         
14811     }
14812 });/*
14813  * Based on:
14814  * Ext JS Library 1.1.1
14815  * Copyright(c) 2006-2007, Ext JS, LLC.
14816  *
14817  * Originally Released Under LGPL - original licence link has changed is not relivant.
14818  *
14819  * Fork - LGPL
14820  * <script type="text/javascript">
14821  */
14822
14823 /**
14824  * @class Roo.data.ArrayReader
14825  * @extends Roo.data.DataReader
14826  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14827  * Each element of that Array represents a row of data fields. The
14828  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14829  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14830  * <p>
14831  * Example code:.
14832  * <pre><code>
14833 var RecordDef = Roo.data.Record.create([
14834     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14835     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14836 ]);
14837 var myReader = new Roo.data.ArrayReader({
14838     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14839 }, RecordDef);
14840 </code></pre>
14841  * <p>
14842  * This would consume an Array like this:
14843  * <pre><code>
14844 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14845   </code></pre>
14846  
14847  * @constructor
14848  * Create a new JsonReader
14849  * @param {Object} meta Metadata configuration options.
14850  * @param {Object|Array} recordType Either an Array of field definition objects
14851  * 
14852  * @cfg {Array} fields Array of field definition objects
14853  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14854  * as specified to {@link Roo.data.Record#create},
14855  * or an {@link Roo.data.Record} object
14856  *
14857  * 
14858  * created using {@link Roo.data.Record#create}.
14859  */
14860 Roo.data.ArrayReader = function(meta, recordType)
14861 {    
14862     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14863 };
14864
14865 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14866     
14867       /**
14868      * Create a data block containing Roo.data.Records from an XML document.
14869      * @param {Object} o An Array of row objects which represents the dataset.
14870      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14871      * a cache of Roo.data.Records.
14872      */
14873     readRecords : function(o)
14874     {
14875         var sid = this.meta ? this.meta.id : null;
14876         var recordType = this.recordType, fields = recordType.prototype.fields;
14877         var records = [];
14878         var root = o;
14879         for(var i = 0; i < root.length; i++){
14880                 var n = root[i];
14881             var values = {};
14882             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14883             for(var j = 0, jlen = fields.length; j < jlen; j++){
14884                 var f = fields.items[j];
14885                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14886                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14887                 v = f.convert(v);
14888                 values[f.name] = v;
14889             }
14890             var record = new recordType(values, id);
14891             record.json = n;
14892             records[records.length] = record;
14893         }
14894         return {
14895             records : records,
14896             totalRecords : records.length
14897         };
14898     },
14899     // used when loading children.. @see loadDataFromChildren
14900     toLoadData: function(rec)
14901     {
14902         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14903         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14904         
14905     }
14906     
14907     
14908 });/*
14909  * - LGPL
14910  * * 
14911  */
14912
14913 /**
14914  * @class Roo.bootstrap.ComboBox
14915  * @extends Roo.bootstrap.TriggerField
14916  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14917  * @cfg {Boolean} append (true|false) default false
14918  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14919  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14920  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14921  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14922  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14923  * @cfg {Boolean} animate default true
14924  * @cfg {Boolean} emptyResultText only for touch device
14925  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14926  * @cfg {String} emptyTitle default ''
14927  * @cfg {Number} width fixed with? experimental
14928  * @constructor
14929  * Create a new ComboBox.
14930  * @param {Object} config Configuration options
14931  */
14932 Roo.bootstrap.ComboBox = function(config){
14933     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14934     this.addEvents({
14935         /**
14936          * @event expand
14937          * Fires when the dropdown list is expanded
14938         * @param {Roo.bootstrap.ComboBox} combo This combo box
14939         */
14940         'expand' : true,
14941         /**
14942          * @event collapse
14943          * Fires when the dropdown list is collapsed
14944         * @param {Roo.bootstrap.ComboBox} combo This combo box
14945         */
14946         'collapse' : true,
14947         /**
14948          * @event beforeselect
14949          * Fires before a list item is selected. Return false to cancel the selection.
14950         * @param {Roo.bootstrap.ComboBox} combo This combo box
14951         * @param {Roo.data.Record} record The data record returned from the underlying store
14952         * @param {Number} index The index of the selected item in the dropdown list
14953         */
14954         'beforeselect' : true,
14955         /**
14956          * @event select
14957          * Fires when a list item is selected
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14960         * @param {Number} index The index of the selected item in the dropdown list
14961         */
14962         'select' : true,
14963         /**
14964          * @event beforequery
14965          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14966          * The event object passed has these properties:
14967         * @param {Roo.bootstrap.ComboBox} combo This combo box
14968         * @param {String} query The query
14969         * @param {Boolean} forceAll true to force "all" query
14970         * @param {Boolean} cancel true to cancel the query
14971         * @param {Object} e The query event object
14972         */
14973         'beforequery': true,
14974          /**
14975          * @event add
14976          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'add' : true,
14980         /**
14981          * @event edit
14982          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14983         * @param {Roo.bootstrap.ComboBox} combo This combo box
14984         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14985         */
14986         'edit' : true,
14987         /**
14988          * @event remove
14989          * Fires when the remove value from the combobox array
14990         * @param {Roo.bootstrap.ComboBox} combo This combo box
14991         */
14992         'remove' : true,
14993         /**
14994          * @event afterremove
14995          * Fires when the remove value from the combobox array
14996         * @param {Roo.bootstrap.ComboBox} combo This combo box
14997         */
14998         'afterremove' : true,
14999         /**
15000          * @event specialfilter
15001          * Fires when specialfilter
15002             * @param {Roo.bootstrap.ComboBox} combo This combo box
15003             */
15004         'specialfilter' : true,
15005         /**
15006          * @event tick
15007          * Fires when tick the element
15008             * @param {Roo.bootstrap.ComboBox} combo This combo box
15009             */
15010         'tick' : true,
15011         /**
15012          * @event touchviewdisplay
15013          * Fires when touch view require special display (default is using displayField)
15014             * @param {Roo.bootstrap.ComboBox} combo This combo box
15015             * @param {Object} cfg set html .
15016             */
15017         'touchviewdisplay' : true
15018         
15019     });
15020     
15021     this.item = [];
15022     this.tickItems = [];
15023     
15024     this.selectedIndex = -1;
15025     if(this.mode == 'local'){
15026         if(config.queryDelay === undefined){
15027             this.queryDelay = 10;
15028         }
15029         if(config.minChars === undefined){
15030             this.minChars = 0;
15031         }
15032     }
15033 };
15034
15035 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15036      
15037     /**
15038      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15039      * rendering into an Roo.Editor, defaults to false)
15040      */
15041     /**
15042      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15043      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15044      */
15045     /**
15046      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15047      */
15048     /**
15049      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15050      * the dropdown list (defaults to undefined, with no header element)
15051      */
15052
15053      /**
15054      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15055      */
15056      
15057      /**
15058      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15059      */
15060     listWidth: undefined,
15061     /**
15062      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15063      * mode = 'remote' or 'text' if mode = 'local')
15064      */
15065     displayField: undefined,
15066     
15067     /**
15068      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15069      * mode = 'remote' or 'value' if mode = 'local'). 
15070      * Note: use of a valueField requires the user make a selection
15071      * in order for a value to be mapped.
15072      */
15073     valueField: undefined,
15074     /**
15075      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15076      */
15077     modalTitle : '',
15078     
15079     /**
15080      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15081      * field's data value (defaults to the underlying DOM element's name)
15082      */
15083     hiddenName: undefined,
15084     /**
15085      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15086      */
15087     listClass: '',
15088     /**
15089      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15090      */
15091     selectedClass: 'active',
15092     
15093     /**
15094      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15095      */
15096     shadow:'sides',
15097     /**
15098      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15099      * anchor positions (defaults to 'tl-bl')
15100      */
15101     listAlign: 'tl-bl?',
15102     /**
15103      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15104      */
15105     maxHeight: 300,
15106     /**
15107      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15108      * query specified by the allQuery config option (defaults to 'query')
15109      */
15110     triggerAction: 'query',
15111     /**
15112      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15113      * (defaults to 4, does not apply if editable = false)
15114      */
15115     minChars : 4,
15116     /**
15117      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15118      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15119      */
15120     typeAhead: false,
15121     /**
15122      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15123      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15124      */
15125     queryDelay: 500,
15126     /**
15127      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15128      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15129      */
15130     pageSize: 0,
15131     /**
15132      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15133      * when editable = true (defaults to false)
15134      */
15135     selectOnFocus:false,
15136     /**
15137      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15138      */
15139     queryParam: 'query',
15140     /**
15141      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15142      * when mode = 'remote' (defaults to 'Loading...')
15143      */
15144     loadingText: 'Loading...',
15145     /**
15146      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15147      */
15148     resizable: false,
15149     /**
15150      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15151      */
15152     handleHeight : 8,
15153     /**
15154      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15155      * traditional select (defaults to true)
15156      */
15157     editable: true,
15158     /**
15159      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15160      */
15161     allQuery: '',
15162     /**
15163      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15164      */
15165     mode: 'remote',
15166     /**
15167      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15168      * listWidth has a higher value)
15169      */
15170     minListWidth : 70,
15171     /**
15172      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15173      * allow the user to set arbitrary text into the field (defaults to false)
15174      */
15175     forceSelection:false,
15176     /**
15177      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15178      * if typeAhead = true (defaults to 250)
15179      */
15180     typeAheadDelay : 250,
15181     /**
15182      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15183      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15184      */
15185     valueNotFoundText : undefined,
15186     /**
15187      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15188      */
15189     blockFocus : false,
15190     
15191     /**
15192      * @cfg {Boolean} disableClear Disable showing of clear button.
15193      */
15194     disableClear : false,
15195     /**
15196      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15197      */
15198     alwaysQuery : false,
15199     
15200     /**
15201      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15202      */
15203     multiple : false,
15204     
15205     /**
15206      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15207      */
15208     invalidClass : "has-warning",
15209     
15210     /**
15211      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15212      */
15213     validClass : "has-success",
15214     
15215     /**
15216      * @cfg {Boolean} specialFilter (true|false) special filter default false
15217      */
15218     specialFilter : false,
15219     
15220     /**
15221      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15222      */
15223     mobileTouchView : true,
15224     
15225     /**
15226      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15227      */
15228     useNativeIOS : false,
15229     
15230     /**
15231      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15232      */
15233     mobile_restrict_height : false,
15234     
15235     ios_options : false,
15236     
15237     //private
15238     addicon : false,
15239     editicon: false,
15240     
15241     page: 0,
15242     hasQuery: false,
15243     append: false,
15244     loadNext: false,
15245     autoFocus : true,
15246     tickable : false,
15247     btnPosition : 'right',
15248     triggerList : true,
15249     showToggleBtn : true,
15250     animate : true,
15251     emptyResultText: 'Empty',
15252     triggerText : 'Select',
15253     emptyTitle : '',
15254     width : false,
15255     
15256     // element that contains real text value.. (when hidden is used..)
15257     
15258     getAutoCreate : function()
15259     {   
15260         var cfg = false;
15261         //render
15262         /*
15263          * Render classic select for iso
15264          */
15265         
15266         if(Roo.isIOS && this.useNativeIOS){
15267             cfg = this.getAutoCreateNativeIOS();
15268             return cfg;
15269         }
15270         
15271         /*
15272          * Touch Devices
15273          */
15274         
15275         if(Roo.isTouch && this.mobileTouchView){
15276             cfg = this.getAutoCreateTouchView();
15277             return cfg;;
15278         }
15279         
15280         /*
15281          *  Normal ComboBox
15282          */
15283         if(!this.tickable){
15284             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15285             return cfg;
15286         }
15287         
15288         /*
15289          *  ComboBox with tickable selections
15290          */
15291              
15292         var align = this.labelAlign || this.parentLabelAlign();
15293         
15294         cfg = {
15295             cls : 'form-group roo-combobox-tickable' //input-group
15296         };
15297         
15298         var btn_text_select = '';
15299         var btn_text_done = '';
15300         var btn_text_cancel = '';
15301         
15302         if (this.btn_text_show) {
15303             btn_text_select = 'Select';
15304             btn_text_done = 'Done';
15305             btn_text_cancel = 'Cancel'; 
15306         }
15307         
15308         var buttons = {
15309             tag : 'div',
15310             cls : 'tickable-buttons',
15311             cn : [
15312                 {
15313                     tag : 'button',
15314                     type : 'button',
15315                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15316                     //html : this.triggerText
15317                     html: btn_text_select
15318                 },
15319                 {
15320                     tag : 'button',
15321                     type : 'button',
15322                     name : 'ok',
15323                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15324                     //html : 'Done'
15325                     html: btn_text_done
15326                 },
15327                 {
15328                     tag : 'button',
15329                     type : 'button',
15330                     name : 'cancel',
15331                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15332                     //html : 'Cancel'
15333                     html: btn_text_cancel
15334                 }
15335             ]
15336         };
15337         
15338         if(this.editable){
15339             buttons.cn.unshift({
15340                 tag: 'input',
15341                 cls: 'roo-select2-search-field-input'
15342             });
15343         }
15344         
15345         var _this = this;
15346         
15347         Roo.each(buttons.cn, function(c){
15348             if (_this.size) {
15349                 c.cls += ' btn-' + _this.size;
15350             }
15351
15352             if (_this.disabled) {
15353                 c.disabled = true;
15354             }
15355         });
15356         
15357         var box = {
15358             tag: 'div',
15359             style : 'display: contents',
15360             cn: [
15361                 {
15362                     tag: 'input',
15363                     type : 'hidden',
15364                     cls: 'form-hidden-field'
15365                 },
15366                 {
15367                     tag: 'ul',
15368                     cls: 'roo-select2-choices',
15369                     cn:[
15370                         {
15371                             tag: 'li',
15372                             cls: 'roo-select2-search-field',
15373                             cn: [
15374                                 buttons
15375                             ]
15376                         }
15377                     ]
15378                 }
15379             ]
15380         };
15381         
15382         var combobox = {
15383             cls: 'roo-select2-container input-group roo-select2-container-multi',
15384             cn: [
15385                 
15386                 box
15387 //                {
15388 //                    tag: 'ul',
15389 //                    cls: 'typeahead typeahead-long dropdown-menu',
15390 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15391 //                }
15392             ]
15393         };
15394         
15395         if(this.hasFeedback && !this.allowBlank){
15396             
15397             var feedback = {
15398                 tag: 'span',
15399                 cls: 'glyphicon form-control-feedback'
15400             };
15401
15402             combobox.cn.push(feedback);
15403         }
15404         
15405         
15406         
15407         var indicator = {
15408             tag : 'i',
15409             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15410             tooltip : 'This field is required'
15411         };
15412         if (Roo.bootstrap.version == 4) {
15413             indicator = {
15414                 tag : 'i',
15415                 style : 'display:none'
15416             };
15417         }
15418         if (align ==='left' && this.fieldLabel.length) {
15419             
15420             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15421             
15422             cfg.cn = [
15423                 indicator,
15424                 {
15425                     tag: 'label',
15426                     'for' :  id,
15427                     cls : 'control-label col-form-label',
15428                     html : this.fieldLabel
15429
15430                 },
15431                 {
15432                     cls : "", 
15433                     cn: [
15434                         combobox
15435                     ]
15436                 }
15437
15438             ];
15439             
15440             var labelCfg = cfg.cn[1];
15441             var contentCfg = cfg.cn[2];
15442             
15443
15444             if(this.indicatorpos == 'right'){
15445                 
15446                 cfg.cn = [
15447                     {
15448                         tag: 'label',
15449                         'for' :  id,
15450                         cls : 'control-label col-form-label',
15451                         cn : [
15452                             {
15453                                 tag : 'span',
15454                                 html : this.fieldLabel
15455                             },
15456                             indicator
15457                         ]
15458                     },
15459                     {
15460                         cls : "",
15461                         cn: [
15462                             combobox
15463                         ]
15464                     }
15465
15466                 ];
15467                 
15468                 
15469                 
15470                 labelCfg = cfg.cn[0];
15471                 contentCfg = cfg.cn[1];
15472             
15473             }
15474             
15475             if(this.labelWidth > 12){
15476                 labelCfg.style = "width: " + this.labelWidth + 'px';
15477             }
15478             if(this.width * 1 > 0){
15479                 contentCfg.style = "width: " + this.width + 'px';
15480             }
15481             if(this.labelWidth < 13 && this.labelmd == 0){
15482                 this.labelmd = this.labelWidth;
15483             }
15484             
15485             if(this.labellg > 0){
15486                 labelCfg.cls += ' col-lg-' + this.labellg;
15487                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15488             }
15489             
15490             if(this.labelmd > 0){
15491                 labelCfg.cls += ' col-md-' + this.labelmd;
15492                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15493             }
15494             
15495             if(this.labelsm > 0){
15496                 labelCfg.cls += ' col-sm-' + this.labelsm;
15497                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15498             }
15499             
15500             if(this.labelxs > 0){
15501                 labelCfg.cls += ' col-xs-' + this.labelxs;
15502                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15503             }
15504                 
15505                 
15506         } else if ( this.fieldLabel.length) {
15507 //                Roo.log(" label");
15508                  cfg.cn = [
15509                    indicator,
15510                     {
15511                         tag: 'label',
15512                         //cls : 'input-group-addon',
15513                         html : this.fieldLabel
15514                     },
15515                     combobox
15516                 ];
15517                 
15518                 if(this.indicatorpos == 'right'){
15519                     cfg.cn = [
15520                         {
15521                             tag: 'label',
15522                             //cls : 'input-group-addon',
15523                             html : this.fieldLabel
15524                         },
15525                         indicator,
15526                         combobox
15527                     ];
15528                     
15529                 }
15530
15531         } else {
15532             
15533 //                Roo.log(" no label && no align");
15534                 cfg = combobox
15535                      
15536                 
15537         }
15538          
15539         var settings=this;
15540         ['xs','sm','md','lg'].map(function(size){
15541             if (settings[size]) {
15542                 cfg.cls += ' col-' + size + '-' + settings[size];
15543             }
15544         });
15545         
15546         return cfg;
15547         
15548     },
15549     
15550     _initEventsCalled : false,
15551     
15552     // private
15553     initEvents: function()
15554     {   
15555         if (this._initEventsCalled) { // as we call render... prevent looping...
15556             return;
15557         }
15558         this._initEventsCalled = true;
15559         
15560         if (!this.store) {
15561             throw "can not find store for combo";
15562         }
15563         
15564         this.indicator = this.indicatorEl();
15565         
15566         this.store = Roo.factory(this.store, Roo.data);
15567         this.store.parent = this;
15568         
15569         // if we are building from html. then this element is so complex, that we can not really
15570         // use the rendered HTML.
15571         // so we have to trash and replace the previous code.
15572         if (Roo.XComponent.build_from_html) {
15573             // remove this element....
15574             var e = this.el.dom, k=0;
15575             while (e ) { e = e.previousSibling;  ++k;}
15576
15577             this.el.remove();
15578             
15579             this.el=false;
15580             this.rendered = false;
15581             
15582             this.render(this.parent().getChildContainer(true), k);
15583         }
15584         
15585         if(Roo.isIOS && this.useNativeIOS){
15586             this.initIOSView();
15587             return;
15588         }
15589         
15590         /*
15591          * Touch Devices
15592          */
15593         
15594         if(Roo.isTouch && this.mobileTouchView){
15595             this.initTouchView();
15596             return;
15597         }
15598         
15599         if(this.tickable){
15600             this.initTickableEvents();
15601             return;
15602         }
15603         
15604         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15605         
15606         if(this.hiddenName){
15607             
15608             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15609             
15610             this.hiddenField.dom.value =
15611                 this.hiddenValue !== undefined ? this.hiddenValue :
15612                 this.value !== undefined ? this.value : '';
15613
15614             // prevent input submission
15615             this.el.dom.removeAttribute('name');
15616             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15617              
15618              
15619         }
15620         //if(Roo.isGecko){
15621         //    this.el.dom.setAttribute('autocomplete', 'off');
15622         //}
15623         
15624         var cls = 'x-combo-list';
15625         
15626         //this.list = new Roo.Layer({
15627         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15628         //});
15629         
15630         var _this = this;
15631         
15632         (function(){
15633             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15634             _this.list.setWidth(lw);
15635         }).defer(100);
15636         
15637         this.list.on('mouseover', this.onViewOver, this);
15638         this.list.on('mousemove', this.onViewMove, this);
15639         this.list.on('scroll', this.onViewScroll, this);
15640         
15641         /*
15642         this.list.swallowEvent('mousewheel');
15643         this.assetHeight = 0;
15644
15645         if(this.title){
15646             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15647             this.assetHeight += this.header.getHeight();
15648         }
15649
15650         this.innerList = this.list.createChild({cls:cls+'-inner'});
15651         this.innerList.on('mouseover', this.onViewOver, this);
15652         this.innerList.on('mousemove', this.onViewMove, this);
15653         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15654         
15655         if(this.allowBlank && !this.pageSize && !this.disableClear){
15656             this.footer = this.list.createChild({cls:cls+'-ft'});
15657             this.pageTb = new Roo.Toolbar(this.footer);
15658            
15659         }
15660         if(this.pageSize){
15661             this.footer = this.list.createChild({cls:cls+'-ft'});
15662             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15663                     {pageSize: this.pageSize});
15664             
15665         }
15666         
15667         if (this.pageTb && this.allowBlank && !this.disableClear) {
15668             var _this = this;
15669             this.pageTb.add(new Roo.Toolbar.Fill(), {
15670                 cls: 'x-btn-icon x-btn-clear',
15671                 text: '&#160;',
15672                 handler: function()
15673                 {
15674                     _this.collapse();
15675                     _this.clearValue();
15676                     _this.onSelect(false, -1);
15677                 }
15678             });
15679         }
15680         if (this.footer) {
15681             this.assetHeight += this.footer.getHeight();
15682         }
15683         */
15684             
15685         if(!this.tpl){
15686             this.tpl = Roo.bootstrap.version == 4 ?
15687                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15688                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15689         }
15690
15691         this.view = new Roo.View(this.list, this.tpl, {
15692             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15693         });
15694         //this.view.wrapEl.setDisplayed(false);
15695         this.view.on('click', this.onViewClick, this);
15696         
15697         
15698         this.store.on('beforeload', this.onBeforeLoad, this);
15699         this.store.on('load', this.onLoad, this);
15700         this.store.on('loadexception', this.onLoadException, this);
15701         /*
15702         if(this.resizable){
15703             this.resizer = new Roo.Resizable(this.list,  {
15704                pinned:true, handles:'se'
15705             });
15706             this.resizer.on('resize', function(r, w, h){
15707                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15708                 this.listWidth = w;
15709                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15710                 this.restrictHeight();
15711             }, this);
15712             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15713         }
15714         */
15715         if(!this.editable){
15716             this.editable = true;
15717             this.setEditable(false);
15718         }
15719         
15720         /*
15721         
15722         if (typeof(this.events.add.listeners) != 'undefined') {
15723             
15724             this.addicon = this.wrap.createChild(
15725                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15726        
15727             this.addicon.on('click', function(e) {
15728                 this.fireEvent('add', this);
15729             }, this);
15730         }
15731         if (typeof(this.events.edit.listeners) != 'undefined') {
15732             
15733             this.editicon = this.wrap.createChild(
15734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15735             if (this.addicon) {
15736                 this.editicon.setStyle('margin-left', '40px');
15737             }
15738             this.editicon.on('click', function(e) {
15739                 
15740                 // we fire even  if inothing is selected..
15741                 this.fireEvent('edit', this, this.lastData );
15742                 
15743             }, this);
15744         }
15745         */
15746         
15747         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15748             "up" : function(e){
15749                 this.inKeyMode = true;
15750                 this.selectPrev();
15751             },
15752
15753             "down" : function(e){
15754                 if(!this.isExpanded()){
15755                     this.onTriggerClick();
15756                 }else{
15757                     this.inKeyMode = true;
15758                     this.selectNext();
15759                 }
15760             },
15761
15762             "enter" : function(e){
15763 //                this.onViewClick();
15764                 //return true;
15765                 this.collapse();
15766                 
15767                 if(this.fireEvent("specialkey", this, e)){
15768                     this.onViewClick(false);
15769                 }
15770                 
15771                 return true;
15772             },
15773
15774             "esc" : function(e){
15775                 this.collapse();
15776             },
15777
15778             "tab" : function(e){
15779                 this.collapse();
15780                 
15781                 if(this.fireEvent("specialkey", this, e)){
15782                     this.onViewClick(false);
15783                 }
15784                 
15785                 return true;
15786             },
15787
15788             scope : this,
15789
15790             doRelay : function(foo, bar, hname){
15791                 if(hname == 'down' || this.scope.isExpanded()){
15792                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15793                 }
15794                 return true;
15795             },
15796
15797             forceKeyDown: true
15798         });
15799         
15800         
15801         this.queryDelay = Math.max(this.queryDelay || 10,
15802                 this.mode == 'local' ? 10 : 250);
15803         
15804         
15805         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15806         
15807         if(this.typeAhead){
15808             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15809         }
15810         if(this.editable !== false){
15811             this.inputEl().on("keyup", this.onKeyUp, this);
15812         }
15813         if(this.forceSelection){
15814             this.inputEl().on('blur', this.doForce, this);
15815         }
15816         
15817         if(this.multiple){
15818             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15819             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15820         }
15821     },
15822     
15823     initTickableEvents: function()
15824     {   
15825         this.createList();
15826         
15827         if(this.hiddenName){
15828             
15829             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15830             
15831             this.hiddenField.dom.value =
15832                 this.hiddenValue !== undefined ? this.hiddenValue :
15833                 this.value !== undefined ? this.value : '';
15834
15835             // prevent input submission
15836             this.el.dom.removeAttribute('name');
15837             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15838              
15839              
15840         }
15841         
15842 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15843         
15844         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15845         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15846         if(this.triggerList){
15847             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15848         }
15849          
15850         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15851         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15852         
15853         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15854         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15855         
15856         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15857         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15858         
15859         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15860         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15861         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15862         
15863         this.okBtn.hide();
15864         this.cancelBtn.hide();
15865         
15866         var _this = this;
15867         
15868         (function(){
15869             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15870             _this.list.setWidth(lw);
15871         }).defer(100);
15872         
15873         this.list.on('mouseover', this.onViewOver, this);
15874         this.list.on('mousemove', this.onViewMove, this);
15875         
15876         this.list.on('scroll', this.onViewScroll, this);
15877         
15878         if(!this.tpl){
15879             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15880                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15881         }
15882
15883         this.view = new Roo.View(this.list, this.tpl, {
15884             singleSelect:true,
15885             tickable:true,
15886             parent:this,
15887             store: this.store,
15888             selectedClass: this.selectedClass
15889         });
15890         
15891         //this.view.wrapEl.setDisplayed(false);
15892         this.view.on('click', this.onViewClick, this);
15893         
15894         
15895         
15896         this.store.on('beforeload', this.onBeforeLoad, this);
15897         this.store.on('load', this.onLoad, this);
15898         this.store.on('loadexception', this.onLoadException, this);
15899         
15900         if(this.editable){
15901             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15902                 "up" : function(e){
15903                     this.inKeyMode = true;
15904                     this.selectPrev();
15905                 },
15906
15907                 "down" : function(e){
15908                     this.inKeyMode = true;
15909                     this.selectNext();
15910                 },
15911
15912                 "enter" : function(e){
15913                     if(this.fireEvent("specialkey", this, e)){
15914                         this.onViewClick(false);
15915                     }
15916                     
15917                     return true;
15918                 },
15919
15920                 "esc" : function(e){
15921                     this.onTickableFooterButtonClick(e, false, false);
15922                 },
15923
15924                 "tab" : function(e){
15925                     this.fireEvent("specialkey", this, e);
15926                     
15927                     this.onTickableFooterButtonClick(e, false, false);
15928                     
15929                     return true;
15930                 },
15931
15932                 scope : this,
15933
15934                 doRelay : function(e, fn, key){
15935                     if(this.scope.isExpanded()){
15936                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15937                     }
15938                     return true;
15939                 },
15940
15941                 forceKeyDown: true
15942             });
15943         }
15944         
15945         this.queryDelay = Math.max(this.queryDelay || 10,
15946                 this.mode == 'local' ? 10 : 250);
15947         
15948         
15949         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15950         
15951         if(this.typeAhead){
15952             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15953         }
15954         
15955         if(this.editable !== false){
15956             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15957         }
15958         
15959         this.indicator = this.indicatorEl();
15960         
15961         if(this.indicator){
15962             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15963             this.indicator.hide();
15964         }
15965         
15966     },
15967
15968     onDestroy : function(){
15969         if(this.view){
15970             this.view.setStore(null);
15971             this.view.el.removeAllListeners();
15972             this.view.el.remove();
15973             this.view.purgeListeners();
15974         }
15975         if(this.list){
15976             this.list.dom.innerHTML  = '';
15977         }
15978         
15979         if(this.store){
15980             this.store.un('beforeload', this.onBeforeLoad, this);
15981             this.store.un('load', this.onLoad, this);
15982             this.store.un('loadexception', this.onLoadException, this);
15983         }
15984         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15985     },
15986
15987     // private
15988     fireKey : function(e){
15989         if(e.isNavKeyPress() && !this.list.isVisible()){
15990             this.fireEvent("specialkey", this, e);
15991         }
15992     },
15993
15994     // private
15995     onResize: function(w, h)
15996     {
15997         
15998         
15999 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16000 //        
16001 //        if(typeof w != 'number'){
16002 //            // we do not handle it!?!?
16003 //            return;
16004 //        }
16005 //        var tw = this.trigger.getWidth();
16006 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16007 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16008 //        var x = w - tw;
16009 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16010 //            
16011 //        //this.trigger.setStyle('left', x+'px');
16012 //        
16013 //        if(this.list && this.listWidth === undefined){
16014 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16015 //            this.list.setWidth(lw);
16016 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16017 //        }
16018         
16019     
16020         
16021     },
16022
16023     /**
16024      * Allow or prevent the user from directly editing the field text.  If false is passed,
16025      * the user will only be able to select from the items defined in the dropdown list.  This method
16026      * is the runtime equivalent of setting the 'editable' config option at config time.
16027      * @param {Boolean} value True to allow the user to directly edit the field text
16028      */
16029     setEditable : function(value){
16030         if(value == this.editable){
16031             return;
16032         }
16033         this.editable = value;
16034         if(!value){
16035             this.inputEl().dom.setAttribute('readOnly', true);
16036             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16037             this.inputEl().addClass('x-combo-noedit');
16038         }else{
16039             this.inputEl().dom.setAttribute('readOnly', false);
16040             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16041             this.inputEl().removeClass('x-combo-noedit');
16042         }
16043     },
16044
16045     // private
16046     
16047     onBeforeLoad : function(combo,opts){
16048         if(!this.hasFocus){
16049             return;
16050         }
16051          if (!opts.add) {
16052             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16053          }
16054         this.restrictHeight();
16055         this.selectedIndex = -1;
16056     },
16057
16058     // private
16059     onLoad : function(){
16060         
16061         this.hasQuery = false;
16062         
16063         if(!this.hasFocus){
16064             return;
16065         }
16066         
16067         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16068             this.loading.hide();
16069         }
16070         
16071         if(this.store.getCount() > 0){
16072             
16073             this.expand();
16074             this.restrictHeight();
16075             if(this.lastQuery == this.allQuery){
16076                 if(this.editable && !this.tickable){
16077                     this.inputEl().dom.select();
16078                 }
16079                 
16080                 if(
16081                     !this.selectByValue(this.value, true) &&
16082                     this.autoFocus && 
16083                     (
16084                         !this.store.lastOptions ||
16085                         typeof(this.store.lastOptions.add) == 'undefined' || 
16086                         this.store.lastOptions.add != true
16087                     )
16088                 ){
16089                     this.select(0, true);
16090                 }
16091             }else{
16092                 if(this.autoFocus){
16093                     this.selectNext();
16094                 }
16095                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16096                     this.taTask.delay(this.typeAheadDelay);
16097                 }
16098             }
16099         }else{
16100             this.onEmptyResults();
16101         }
16102         
16103         //this.el.focus();
16104     },
16105     // private
16106     onLoadException : function()
16107     {
16108         this.hasQuery = false;
16109         
16110         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16111             this.loading.hide();
16112         }
16113         
16114         if(this.tickable && this.editable){
16115             return;
16116         }
16117         
16118         this.collapse();
16119         // only causes errors at present
16120         //Roo.log(this.store.reader.jsonData);
16121         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16122             // fixme
16123             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16124         //}
16125         
16126         
16127     },
16128     // private
16129     onTypeAhead : function(){
16130         if(this.store.getCount() > 0){
16131             var r = this.store.getAt(0);
16132             var newValue = r.data[this.displayField];
16133             var len = newValue.length;
16134             var selStart = this.getRawValue().length;
16135             
16136             if(selStart != len){
16137                 this.setRawValue(newValue);
16138                 this.selectText(selStart, newValue.length);
16139             }
16140         }
16141     },
16142
16143     // private
16144     onSelect : function(record, index){
16145         
16146         if(this.fireEvent('beforeselect', this, record, index) !== false){
16147         
16148             this.setFromData(index > -1 ? record.data : false);
16149             
16150             this.collapse();
16151             this.fireEvent('select', this, record, index);
16152         }
16153     },
16154
16155     /**
16156      * Returns the currently selected field value or empty string if no value is set.
16157      * @return {String} value The selected value
16158      */
16159     getValue : function()
16160     {
16161         if(Roo.isIOS && this.useNativeIOS){
16162             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16163         }
16164         
16165         if(this.multiple){
16166             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16167         }
16168         
16169         if(this.valueField){
16170             return typeof this.value != 'undefined' ? this.value : '';
16171         }else{
16172             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16173         }
16174     },
16175     
16176     getRawValue : function()
16177     {
16178         if(Roo.isIOS && this.useNativeIOS){
16179             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16180         }
16181         
16182         var v = this.inputEl().getValue();
16183         
16184         return v;
16185     },
16186
16187     /**
16188      * Clears any text/value currently set in the field
16189      */
16190     clearValue : function(){
16191         
16192         if(this.hiddenField){
16193             this.hiddenField.dom.value = '';
16194         }
16195         this.value = '';
16196         this.setRawValue('');
16197         this.lastSelectionText = '';
16198         this.lastData = false;
16199         
16200         var close = this.closeTriggerEl();
16201         
16202         if(close){
16203             close.hide();
16204         }
16205         
16206         this.validate();
16207         
16208     },
16209
16210     /**
16211      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16212      * will be displayed in the field.  If the value does not match the data value of an existing item,
16213      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16214      * Otherwise the field will be blank (although the value will still be set).
16215      * @param {String} value The value to match
16216      */
16217     setValue : function(v)
16218     {
16219         if(Roo.isIOS && this.useNativeIOS){
16220             this.setIOSValue(v);
16221             return;
16222         }
16223         
16224         if(this.multiple){
16225             this.syncValue();
16226             return;
16227         }
16228         
16229         var text = v;
16230         if(this.valueField){
16231             var r = this.findRecord(this.valueField, v);
16232             if(r){
16233                 text = r.data[this.displayField];
16234             }else if(this.valueNotFoundText !== undefined){
16235                 text = this.valueNotFoundText;
16236             }
16237         }
16238         this.lastSelectionText = text;
16239         if(this.hiddenField){
16240             this.hiddenField.dom.value = v;
16241         }
16242         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16243         this.value = v;
16244         
16245         var close = this.closeTriggerEl();
16246         
16247         if(close){
16248             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16249         }
16250         
16251         this.validate();
16252     },
16253     /**
16254      * @property {Object} the last set data for the element
16255      */
16256     
16257     lastData : false,
16258     /**
16259      * Sets the value of the field based on a object which is related to the record format for the store.
16260      * @param {Object} value the value to set as. or false on reset?
16261      */
16262     setFromData : function(o){
16263         
16264         if(this.multiple){
16265             this.addItem(o);
16266             return;
16267         }
16268             
16269         var dv = ''; // display value
16270         var vv = ''; // value value..
16271         this.lastData = o;
16272         if (this.displayField) {
16273             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16274         } else {
16275             // this is an error condition!!!
16276             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16277         }
16278         
16279         if(this.valueField){
16280             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16281         }
16282         
16283         var close = this.closeTriggerEl();
16284         
16285         if(close){
16286             if(dv.length || vv * 1 > 0){
16287                 close.show() ;
16288                 this.blockFocus=true;
16289             } else {
16290                 close.hide();
16291             }             
16292         }
16293         
16294         if(this.hiddenField){
16295             this.hiddenField.dom.value = vv;
16296             
16297             this.lastSelectionText = dv;
16298             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16299             this.value = vv;
16300             return;
16301         }
16302         // no hidden field.. - we store the value in 'value', but still display
16303         // display field!!!!
16304         this.lastSelectionText = dv;
16305         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16306         this.value = vv;
16307         
16308         
16309         
16310     },
16311     // private
16312     reset : function(){
16313         // overridden so that last data is reset..
16314         
16315         if(this.multiple){
16316             this.clearItem();
16317             return;
16318         }
16319         
16320         this.setValue(this.originalValue);
16321         //this.clearInvalid();
16322         this.lastData = false;
16323         if (this.view) {
16324             this.view.clearSelections();
16325         }
16326         
16327         this.validate();
16328     },
16329     // private
16330     findRecord : function(prop, value){
16331         var record;
16332         if(this.store.getCount() > 0){
16333             this.store.each(function(r){
16334                 if(r.data[prop] == value){
16335                     record = r;
16336                     return false;
16337                 }
16338                 return true;
16339             });
16340         }
16341         return record;
16342     },
16343     
16344     getName: function()
16345     {
16346         // returns hidden if it's set..
16347         if (!this.rendered) {return ''};
16348         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16349         
16350     },
16351     // private
16352     onViewMove : function(e, t){
16353         this.inKeyMode = false;
16354     },
16355
16356     // private
16357     onViewOver : function(e, t){
16358         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16359             return;
16360         }
16361         var item = this.view.findItemFromChild(t);
16362         
16363         if(item){
16364             var index = this.view.indexOf(item);
16365             this.select(index, false);
16366         }
16367     },
16368
16369     // private
16370     onViewClick : function(view, doFocus, el, e)
16371     {
16372         var index = this.view.getSelectedIndexes()[0];
16373         
16374         var r = this.store.getAt(index);
16375         
16376         if(this.tickable){
16377             
16378             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16379                 return;
16380             }
16381             
16382             var rm = false;
16383             var _this = this;
16384             
16385             Roo.each(this.tickItems, function(v,k){
16386                 
16387                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16388                     Roo.log(v);
16389                     _this.tickItems.splice(k, 1);
16390                     
16391                     if(typeof(e) == 'undefined' && view == false){
16392                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16393                     }
16394                     
16395                     rm = true;
16396                     return;
16397                 }
16398             });
16399             
16400             if(rm){
16401                 return;
16402             }
16403             
16404             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16405                 this.tickItems.push(r.data);
16406             }
16407             
16408             if(typeof(e) == 'undefined' && view == false){
16409                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16410             }
16411                     
16412             return;
16413         }
16414         
16415         if(r){
16416             this.onSelect(r, index);
16417         }
16418         if(doFocus !== false && !this.blockFocus){
16419             this.inputEl().focus();
16420         }
16421     },
16422
16423     // private
16424     restrictHeight : function(){
16425         //this.innerList.dom.style.height = '';
16426         //var inner = this.innerList.dom;
16427         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16428         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16429         //this.list.beginUpdate();
16430         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16431         this.list.alignTo(this.inputEl(), this.listAlign);
16432         this.list.alignTo(this.inputEl(), this.listAlign);
16433         //this.list.endUpdate();
16434     },
16435
16436     // private
16437     onEmptyResults : function(){
16438         
16439         if(this.tickable && this.editable){
16440             this.hasFocus = false;
16441             this.restrictHeight();
16442             return;
16443         }
16444         
16445         this.collapse();
16446     },
16447
16448     /**
16449      * Returns true if the dropdown list is expanded, else false.
16450      */
16451     isExpanded : function(){
16452         return this.list.isVisible();
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
16462      */
16463     selectByValue : function(v, scrollIntoView){
16464         if(v !== undefined && v !== null){
16465             var r = this.findRecord(this.valueField || this.displayField, v);
16466             if(r){
16467                 this.select(this.store.indexOf(r), scrollIntoView);
16468                 return true;
16469             }
16470         }
16471         return false;
16472     },
16473
16474     /**
16475      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16476      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16477      * @param {Number} index The zero-based index of the list item to select
16478      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16479      * selected item if it is not currently in view (defaults to true)
16480      */
16481     select : function(index, scrollIntoView){
16482         this.selectedIndex = index;
16483         this.view.select(index);
16484         if(scrollIntoView !== false){
16485             var el = this.view.getNode(index);
16486             /*
16487              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16488              */
16489             if(el){
16490                 this.list.scrollChildIntoView(el, false);
16491             }
16492         }
16493     },
16494
16495     // private
16496     selectNext : function(){
16497         var ct = this.store.getCount();
16498         if(ct > 0){
16499             if(this.selectedIndex == -1){
16500                 this.select(0);
16501             }else if(this.selectedIndex < ct-1){
16502                 this.select(this.selectedIndex+1);
16503             }
16504         }
16505     },
16506
16507     // private
16508     selectPrev : function(){
16509         var ct = this.store.getCount();
16510         if(ct > 0){
16511             if(this.selectedIndex == -1){
16512                 this.select(0);
16513             }else if(this.selectedIndex != 0){
16514                 this.select(this.selectedIndex-1);
16515             }
16516         }
16517     },
16518
16519     // private
16520     onKeyUp : function(e){
16521         if(this.editable !== false && !e.isSpecialKey()){
16522             this.lastKey = e.getKey();
16523             this.dqTask.delay(this.queryDelay);
16524         }
16525     },
16526
16527     // private
16528     validateBlur : function(){
16529         return !this.list || !this.list.isVisible();   
16530     },
16531
16532     // private
16533     initQuery : function(){
16534         
16535         var v = this.getRawValue();
16536         
16537         if(this.tickable && this.editable){
16538             v = this.tickableInputEl().getValue();
16539         }
16540         
16541         this.doQuery(v);
16542     },
16543
16544     // private
16545     doForce : function(){
16546         if(this.inputEl().dom.value.length > 0){
16547             this.inputEl().dom.value =
16548                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16549              
16550         }
16551     },
16552
16553     /**
16554      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16555      * query allowing the query action to be canceled if needed.
16556      * @param {String} query The SQL query to execute
16557      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16558      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16559      * saved in the current store (defaults to false)
16560      */
16561     doQuery : function(q, forceAll){
16562         
16563         if(q === undefined || q === null){
16564             q = '';
16565         }
16566         var qe = {
16567             query: q,
16568             forceAll: forceAll,
16569             combo: this,
16570             cancel:false
16571         };
16572         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16573             return false;
16574         }
16575         q = qe.query;
16576         
16577         forceAll = qe.forceAll;
16578         if(forceAll === true || (q.length >= this.minChars)){
16579             
16580             this.hasQuery = true;
16581             
16582             if(this.lastQuery != q || this.alwaysQuery){
16583                 this.lastQuery = q;
16584                 if(this.mode == 'local'){
16585                     this.selectedIndex = -1;
16586                     if(forceAll){
16587                         this.store.clearFilter();
16588                     }else{
16589                         
16590                         if(this.specialFilter){
16591                             this.fireEvent('specialfilter', this);
16592                             this.onLoad();
16593                             return;
16594                         }
16595                         
16596                         this.store.filter(this.displayField, q);
16597                     }
16598                     
16599                     this.store.fireEvent("datachanged", this.store);
16600                     
16601                     this.onLoad();
16602                     
16603                     
16604                 }else{
16605                     
16606                     this.store.baseParams[this.queryParam] = q;
16607                     
16608                     var options = {params : this.getParams(q)};
16609                     
16610                     if(this.loadNext){
16611                         options.add = true;
16612                         options.params.start = this.page * this.pageSize;
16613                     }
16614                     
16615                     this.store.load(options);
16616                     
16617                     /*
16618                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16619                      *  we should expand the list on onLoad
16620                      *  so command out it
16621                      */
16622 //                    this.expand();
16623                 }
16624             }else{
16625                 this.selectedIndex = -1;
16626                 this.onLoad();   
16627             }
16628         }
16629         
16630         this.loadNext = false;
16631     },
16632     
16633     // private
16634     getParams : function(q){
16635         var p = {};
16636         //p[this.queryParam] = q;
16637         
16638         if(this.pageSize){
16639             p.start = 0;
16640             p.limit = this.pageSize;
16641         }
16642         return p;
16643     },
16644
16645     /**
16646      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16647      */
16648     collapse : function(){
16649         if(!this.isExpanded()){
16650             return;
16651         }
16652         
16653         this.list.hide();
16654         
16655         this.hasFocus = false;
16656         
16657         if(this.tickable){
16658             this.okBtn.hide();
16659             this.cancelBtn.hide();
16660             this.trigger.show();
16661             
16662             if(this.editable){
16663                 this.tickableInputEl().dom.value = '';
16664                 this.tickableInputEl().blur();
16665             }
16666             
16667         }
16668         
16669         Roo.get(document).un('mousedown', this.collapseIf, this);
16670         Roo.get(document).un('mousewheel', this.collapseIf, this);
16671         if (!this.editable) {
16672             Roo.get(document).un('keydown', this.listKeyPress, this);
16673         }
16674         this.fireEvent('collapse', this);
16675         
16676         this.validate();
16677     },
16678
16679     // private
16680     collapseIf : function(e){
16681         var in_combo  = e.within(this.el);
16682         var in_list =  e.within(this.list);
16683         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16684         
16685         if (in_combo || in_list || is_list) {
16686             //e.stopPropagation();
16687             return;
16688         }
16689         
16690         if(this.tickable){
16691             this.onTickableFooterButtonClick(e, false, false);
16692         }
16693
16694         this.collapse();
16695         
16696     },
16697
16698     /**
16699      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16700      */
16701     expand : function(){
16702        
16703         if(this.isExpanded() || !this.hasFocus){
16704             return;
16705         }
16706         
16707         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16708         this.list.setWidth(lw);
16709         
16710         Roo.log('expand');
16711         
16712         this.list.show();
16713         
16714         this.restrictHeight();
16715         
16716         if(this.tickable){
16717             
16718             this.tickItems = Roo.apply([], this.item);
16719             
16720             this.okBtn.show();
16721             this.cancelBtn.show();
16722             this.trigger.hide();
16723             
16724             if(this.editable){
16725                 this.tickableInputEl().focus();
16726             }
16727             
16728         }
16729         
16730         Roo.get(document).on('mousedown', this.collapseIf, this);
16731         Roo.get(document).on('mousewheel', this.collapseIf, this);
16732         if (!this.editable) {
16733             Roo.get(document).on('keydown', this.listKeyPress, this);
16734         }
16735         
16736         this.fireEvent('expand', this);
16737     },
16738
16739     // private
16740     // Implements the default empty TriggerField.onTriggerClick function
16741     onTriggerClick : function(e)
16742     {
16743         Roo.log('trigger click');
16744         
16745         if(this.disabled || !this.triggerList){
16746             return;
16747         }
16748         
16749         this.page = 0;
16750         this.loadNext = false;
16751         
16752         if(this.isExpanded()){
16753             this.collapse();
16754             if (!this.blockFocus) {
16755                 this.inputEl().focus();
16756             }
16757             
16758         }else {
16759             this.hasFocus = true;
16760             if(this.triggerAction == 'all') {
16761                 this.doQuery(this.allQuery, true);
16762             } else {
16763                 this.doQuery(this.getRawValue());
16764             }
16765             if (!this.blockFocus) {
16766                 this.inputEl().focus();
16767             }
16768         }
16769     },
16770     
16771     onTickableTriggerClick : function(e)
16772     {
16773         if(this.disabled){
16774             return;
16775         }
16776         
16777         this.page = 0;
16778         this.loadNext = false;
16779         this.hasFocus = true;
16780         
16781         if(this.triggerAction == 'all') {
16782             this.doQuery(this.allQuery, true);
16783         } else {
16784             this.doQuery(this.getRawValue());
16785         }
16786     },
16787     
16788     onSearchFieldClick : function(e)
16789     {
16790         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16791             this.onTickableFooterButtonClick(e, false, false);
16792             return;
16793         }
16794         
16795         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16796             return;
16797         }
16798         
16799         this.page = 0;
16800         this.loadNext = false;
16801         this.hasFocus = true;
16802         
16803         if(this.triggerAction == 'all') {
16804             this.doQuery(this.allQuery, true);
16805         } else {
16806             this.doQuery(this.getRawValue());
16807         }
16808     },
16809     
16810     listKeyPress : function(e)
16811     {
16812         //Roo.log('listkeypress');
16813         // scroll to first matching element based on key pres..
16814         if (e.isSpecialKey()) {
16815             return false;
16816         }
16817         var k = String.fromCharCode(e.getKey()).toUpperCase();
16818         //Roo.log(k);
16819         var match  = false;
16820         var csel = this.view.getSelectedNodes();
16821         var cselitem = false;
16822         if (csel.length) {
16823             var ix = this.view.indexOf(csel[0]);
16824             cselitem  = this.store.getAt(ix);
16825             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16826                 cselitem = false;
16827             }
16828             
16829         }
16830         
16831         this.store.each(function(v) { 
16832             if (cselitem) {
16833                 // start at existing selection.
16834                 if (cselitem.id == v.id) {
16835                     cselitem = false;
16836                 }
16837                 return true;
16838             }
16839                 
16840             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16841                 match = this.store.indexOf(v);
16842                 return false;
16843             }
16844             return true;
16845         }, this);
16846         
16847         if (match === false) {
16848             return true; // no more action?
16849         }
16850         // scroll to?
16851         this.view.select(match);
16852         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16853         sn.scrollIntoView(sn.dom.parentNode, false);
16854     },
16855     
16856     onViewScroll : function(e, t){
16857         
16858         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){
16859             return;
16860         }
16861         
16862         this.hasQuery = true;
16863         
16864         this.loading = this.list.select('.loading', true).first();
16865         
16866         if(this.loading === null){
16867             this.list.createChild({
16868                 tag: 'div',
16869                 cls: 'loading roo-select2-more-results roo-select2-active',
16870                 html: 'Loading more results...'
16871             });
16872             
16873             this.loading = this.list.select('.loading', true).first();
16874             
16875             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16876             
16877             this.loading.hide();
16878         }
16879         
16880         this.loading.show();
16881         
16882         var _combo = this;
16883         
16884         this.page++;
16885         this.loadNext = true;
16886         
16887         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16888         
16889         return;
16890     },
16891     
16892     addItem : function(o)
16893     {   
16894         var dv = ''; // display value
16895         
16896         if (this.displayField) {
16897             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16898         } else {
16899             // this is an error condition!!!
16900             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16901         }
16902         
16903         if(!dv.length){
16904             return;
16905         }
16906         
16907         var choice = this.choices.createChild({
16908             tag: 'li',
16909             cls: 'roo-select2-search-choice',
16910             cn: [
16911                 {
16912                     tag: 'div',
16913                     html: dv
16914                 },
16915                 {
16916                     tag: 'a',
16917                     href: '#',
16918                     cls: 'roo-select2-search-choice-close fa fa-times',
16919                     tabindex: '-1'
16920                 }
16921             ]
16922             
16923         }, this.searchField);
16924         
16925         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16926         
16927         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16928         
16929         this.item.push(o);
16930         
16931         this.lastData = o;
16932         
16933         this.syncValue();
16934         
16935         this.inputEl().dom.value = '';
16936         
16937         this.validate();
16938     },
16939     
16940     onRemoveItem : function(e, _self, o)
16941     {
16942         e.preventDefault();
16943         
16944         this.lastItem = Roo.apply([], this.item);
16945         
16946         var index = this.item.indexOf(o.data) * 1;
16947         
16948         if( index < 0){
16949             Roo.log('not this item?!');
16950             return;
16951         }
16952         
16953         this.item.splice(index, 1);
16954         o.item.remove();
16955         
16956         this.syncValue();
16957         
16958         this.fireEvent('remove', this, e);
16959         
16960         this.validate();
16961         
16962     },
16963     
16964     syncValue : function()
16965     {
16966         if(!this.item.length){
16967             this.clearValue();
16968             return;
16969         }
16970             
16971         var value = [];
16972         var _this = this;
16973         Roo.each(this.item, function(i){
16974             if(_this.valueField){
16975                 value.push(i[_this.valueField]);
16976                 return;
16977             }
16978
16979             value.push(i);
16980         });
16981
16982         this.value = value.join(',');
16983
16984         if(this.hiddenField){
16985             this.hiddenField.dom.value = this.value;
16986         }
16987         
16988         this.store.fireEvent("datachanged", this.store);
16989         
16990         this.validate();
16991     },
16992     
16993     clearItem : function()
16994     {
16995         if(!this.multiple){
16996             return;
16997         }
16998         
16999         this.item = [];
17000         
17001         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17002            c.remove();
17003         });
17004         
17005         this.syncValue();
17006         
17007         this.validate();
17008         
17009         if(this.tickable && !Roo.isTouch){
17010             this.view.refresh();
17011         }
17012     },
17013     
17014     inputEl: function ()
17015     {
17016         if(Roo.isIOS && this.useNativeIOS){
17017             return this.el.select('select.roo-ios-select', true).first();
17018         }
17019         
17020         if(Roo.isTouch && this.mobileTouchView){
17021             return this.el.select('input.form-control',true).first();
17022         }
17023         
17024         if(this.tickable){
17025             return this.searchField;
17026         }
17027         
17028         return this.el.select('input.form-control',true).first();
17029     },
17030     
17031     onTickableFooterButtonClick : function(e, btn, el)
17032     {
17033         e.preventDefault();
17034         
17035         this.lastItem = Roo.apply([], this.item);
17036         
17037         if(btn && btn.name == 'cancel'){
17038             this.tickItems = Roo.apply([], this.item);
17039             this.collapse();
17040             return;
17041         }
17042         
17043         this.clearItem();
17044         
17045         var _this = this;
17046         
17047         Roo.each(this.tickItems, function(o){
17048             _this.addItem(o);
17049         });
17050         
17051         this.collapse();
17052         
17053     },
17054     
17055     validate : function()
17056     {
17057         if(this.getVisibilityEl().hasClass('hidden')){
17058             return true;
17059         }
17060         
17061         var v = this.getRawValue();
17062         
17063         if(this.multiple){
17064             v = this.getValue();
17065         }
17066         
17067         if(this.disabled || this.allowBlank || v.length){
17068             this.markValid();
17069             return true;
17070         }
17071         
17072         this.markInvalid();
17073         return false;
17074     },
17075     
17076     tickableInputEl : function()
17077     {
17078         if(!this.tickable || !this.editable){
17079             return this.inputEl();
17080         }
17081         
17082         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17083     },
17084     
17085     
17086     getAutoCreateTouchView : function()
17087     {
17088         var id = Roo.id();
17089         
17090         var cfg = {
17091             cls: 'form-group' //input-group
17092         };
17093         
17094         var input =  {
17095             tag: 'input',
17096             id : id,
17097             type : this.inputType,
17098             cls : 'form-control x-combo-noedit',
17099             autocomplete: 'new-password',
17100             placeholder : this.placeholder || '',
17101             readonly : true
17102         };
17103         
17104         if (this.name) {
17105             input.name = this.name;
17106         }
17107         
17108         if (this.size) {
17109             input.cls += ' input-' + this.size;
17110         }
17111         
17112         if (this.disabled) {
17113             input.disabled = true;
17114         }
17115         
17116         var inputblock = {
17117             cls : 'roo-combobox-wrap',
17118             cn : [
17119                 input
17120             ]
17121         };
17122         
17123         if(this.before){
17124             inputblock.cls += ' input-group';
17125             
17126             inputblock.cn.unshift({
17127                 tag :'span',
17128                 cls : 'input-group-addon input-group-prepend input-group-text',
17129                 html : this.before
17130             });
17131         }
17132         
17133         if(this.removable && !this.multiple){
17134             inputblock.cls += ' roo-removable';
17135             
17136             inputblock.cn.push({
17137                 tag: 'button',
17138                 html : 'x',
17139                 cls : 'roo-combo-removable-btn close'
17140             });
17141         }
17142
17143         if(this.hasFeedback && !this.allowBlank){
17144             
17145             inputblock.cls += ' has-feedback';
17146             
17147             inputblock.cn.push({
17148                 tag: 'span',
17149                 cls: 'glyphicon form-control-feedback'
17150             });
17151             
17152         }
17153         
17154         if (this.after) {
17155             
17156             inputblock.cls += (this.before) ? '' : ' input-group';
17157             
17158             inputblock.cn.push({
17159                 tag :'span',
17160                 cls : 'input-group-addon input-group-append input-group-text',
17161                 html : this.after
17162             });
17163         }
17164
17165         
17166         var ibwrap = inputblock;
17167         
17168         if(this.multiple){
17169             ibwrap = {
17170                 tag: 'ul',
17171                 cls: 'roo-select2-choices',
17172                 cn:[
17173                     {
17174                         tag: 'li',
17175                         cls: 'roo-select2-search-field',
17176                         cn: [
17177
17178                             inputblock
17179                         ]
17180                     }
17181                 ]
17182             };
17183         
17184             
17185         }
17186         
17187         var combobox = {
17188             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17189             cn: [
17190                 {
17191                     tag: 'input',
17192                     type : 'hidden',
17193                     cls: 'form-hidden-field'
17194                 },
17195                 ibwrap
17196             ]
17197         };
17198         
17199         if(!this.multiple && this.showToggleBtn){
17200             
17201             var caret = {
17202                 cls: 'caret'
17203             };
17204             
17205             if (this.caret != false) {
17206                 caret = {
17207                      tag: 'i',
17208                      cls: 'fa fa-' + this.caret
17209                 };
17210                 
17211             }
17212             
17213             combobox.cn.push({
17214                 tag :'span',
17215                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17216                 cn : [
17217                     Roo.bootstrap.version == 3 ? caret : '',
17218                     {
17219                         tag: 'span',
17220                         cls: 'combobox-clear',
17221                         cn  : [
17222                             {
17223                                 tag : 'i',
17224                                 cls: 'icon-remove'
17225                             }
17226                         ]
17227                     }
17228                 ]
17229
17230             })
17231         }
17232         
17233         if(this.multiple){
17234             combobox.cls += ' roo-select2-container-multi';
17235         }
17236         
17237         var align = this.labelAlign || this.parentLabelAlign();
17238         
17239         if (align ==='left' && this.fieldLabel.length) {
17240
17241             cfg.cn = [
17242                 {
17243                    tag : 'i',
17244                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17245                    tooltip : 'This field is required'
17246                 },
17247                 {
17248                     tag: 'label',
17249                     cls : 'control-label col-form-label',
17250                     html : this.fieldLabel
17251
17252                 },
17253                 {
17254                     cls : 'roo-combobox-wrap ', 
17255                     cn: [
17256                         combobox
17257                     ]
17258                 }
17259             ];
17260             
17261             var labelCfg = cfg.cn[1];
17262             var contentCfg = cfg.cn[2];
17263             
17264
17265             if(this.indicatorpos == 'right'){
17266                 cfg.cn = [
17267                     {
17268                         tag: 'label',
17269                         'for' :  id,
17270                         cls : 'control-label col-form-label',
17271                         cn : [
17272                             {
17273                                 tag : 'span',
17274                                 html : this.fieldLabel
17275                             },
17276                             {
17277                                 tag : 'i',
17278                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17279                                 tooltip : 'This field is required'
17280                             }
17281                         ]
17282                     },
17283                     {
17284                         cls : "roo-combobox-wrap ",
17285                         cn: [
17286                             combobox
17287                         ]
17288                     }
17289
17290                 ];
17291                 
17292                 labelCfg = cfg.cn[0];
17293                 contentCfg = cfg.cn[1];
17294             }
17295             
17296            
17297             
17298             if(this.labelWidth > 12){
17299                 labelCfg.style = "width: " + this.labelWidth + 'px';
17300             }
17301            
17302             if(this.labelWidth < 13 && this.labelmd == 0){
17303                 this.labelmd = this.labelWidth;
17304             }
17305             
17306             if(this.labellg > 0){
17307                 labelCfg.cls += ' col-lg-' + this.labellg;
17308                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17309             }
17310             
17311             if(this.labelmd > 0){
17312                 labelCfg.cls += ' col-md-' + this.labelmd;
17313                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17314             }
17315             
17316             if(this.labelsm > 0){
17317                 labelCfg.cls += ' col-sm-' + this.labelsm;
17318                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17319             }
17320             
17321             if(this.labelxs > 0){
17322                 labelCfg.cls += ' col-xs-' + this.labelxs;
17323                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17324             }
17325                 
17326                 
17327         } else if ( this.fieldLabel.length) {
17328             cfg.cn = [
17329                 {
17330                    tag : 'i',
17331                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17332                    tooltip : 'This field is required'
17333                 },
17334                 {
17335                     tag: 'label',
17336                     cls : 'control-label',
17337                     html : this.fieldLabel
17338
17339                 },
17340                 {
17341                     cls : '', 
17342                     cn: [
17343                         combobox
17344                     ]
17345                 }
17346             ];
17347             
17348             if(this.indicatorpos == 'right'){
17349                 cfg.cn = [
17350                     {
17351                         tag: 'label',
17352                         cls : 'control-label',
17353                         html : this.fieldLabel,
17354                         cn : [
17355                             {
17356                                tag : 'i',
17357                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17358                                tooltip : 'This field is required'
17359                             }
17360                         ]
17361                     },
17362                     {
17363                         cls : '', 
17364                         cn: [
17365                             combobox
17366                         ]
17367                     }
17368                 ];
17369             }
17370         } else {
17371             cfg.cn = combobox;    
17372         }
17373         
17374         
17375         var settings = this;
17376         
17377         ['xs','sm','md','lg'].map(function(size){
17378             if (settings[size]) {
17379                 cfg.cls += ' col-' + size + '-' + settings[size];
17380             }
17381         });
17382         
17383         return cfg;
17384     },
17385     
17386     initTouchView : function()
17387     {
17388         this.renderTouchView();
17389         
17390         this.touchViewEl.on('scroll', function(){
17391             this.el.dom.scrollTop = 0;
17392         }, this);
17393         
17394         this.originalValue = this.getValue();
17395         
17396         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17397         
17398         this.inputEl().on("click", this.showTouchView, this);
17399         if (this.triggerEl) {
17400             this.triggerEl.on("click", this.showTouchView, this);
17401         }
17402         
17403         
17404         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17405         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17406         
17407         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17408         
17409         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17410         this.store.on('load', this.onTouchViewLoad, this);
17411         this.store.on('loadexception', this.onTouchViewLoadException, this);
17412         
17413         if(this.hiddenName){
17414             
17415             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17416             
17417             this.hiddenField.dom.value =
17418                 this.hiddenValue !== undefined ? this.hiddenValue :
17419                 this.value !== undefined ? this.value : '';
17420         
17421             this.el.dom.removeAttribute('name');
17422             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17423         }
17424         
17425         if(this.multiple){
17426             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17427             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17428         }
17429         
17430         if(this.removable && !this.multiple){
17431             var close = this.closeTriggerEl();
17432             if(close){
17433                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17434                 close.on('click', this.removeBtnClick, this, close);
17435             }
17436         }
17437         /*
17438          * fix the bug in Safari iOS8
17439          */
17440         this.inputEl().on("focus", function(e){
17441             document.activeElement.blur();
17442         }, this);
17443         
17444         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17445         
17446         return;
17447         
17448         
17449     },
17450     
17451     renderTouchView : function()
17452     {
17453         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17454         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17455         
17456         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17457         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17458         
17459         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17460         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17461         this.touchViewBodyEl.setStyle('overflow', 'auto');
17462         
17463         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17464         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465         
17466         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17467         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17468         
17469     },
17470     
17471     showTouchView : function()
17472     {
17473         if(this.disabled){
17474             return;
17475         }
17476         
17477         this.touchViewHeaderEl.hide();
17478
17479         if(this.modalTitle.length){
17480             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17481             this.touchViewHeaderEl.show();
17482         }
17483
17484         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17485         this.touchViewEl.show();
17486
17487         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17488         
17489         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17490         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17491
17492         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17493
17494         if(this.modalTitle.length){
17495             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17496         }
17497         
17498         this.touchViewBodyEl.setHeight(bodyHeight);
17499
17500         if(this.animate){
17501             var _this = this;
17502             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17503         }else{
17504             this.touchViewEl.addClass(['in','show']);
17505         }
17506         
17507         if(this._touchViewMask){
17508             Roo.get(document.body).addClass("x-body-masked");
17509             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17510             this._touchViewMask.setStyle('z-index', 10000);
17511             this._touchViewMask.addClass('show');
17512         }
17513         
17514         this.doTouchViewQuery();
17515         
17516     },
17517     
17518     hideTouchView : function()
17519     {
17520         this.touchViewEl.removeClass(['in','show']);
17521
17522         if(this.animate){
17523             var _this = this;
17524             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17525         }else{
17526             this.touchViewEl.setStyle('display', 'none');
17527         }
17528         
17529         if(this._touchViewMask){
17530             this._touchViewMask.removeClass('show');
17531             Roo.get(document.body).removeClass("x-body-masked");
17532         }
17533     },
17534     
17535     setTouchViewValue : function()
17536     {
17537         if(this.multiple){
17538             this.clearItem();
17539         
17540             var _this = this;
17541
17542             Roo.each(this.tickItems, function(o){
17543                 this.addItem(o);
17544             }, this);
17545         }
17546         
17547         this.hideTouchView();
17548     },
17549     
17550     doTouchViewQuery : function()
17551     {
17552         var qe = {
17553             query: '',
17554             forceAll: true,
17555             combo: this,
17556             cancel:false
17557         };
17558         
17559         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17560             return false;
17561         }
17562         
17563         if(!this.alwaysQuery || this.mode == 'local'){
17564             this.onTouchViewLoad();
17565             return;
17566         }
17567         
17568         this.store.load();
17569     },
17570     
17571     onTouchViewBeforeLoad : function(combo,opts)
17572     {
17573         return;
17574     },
17575
17576     // private
17577     onTouchViewLoad : function()
17578     {
17579         if(this.store.getCount() < 1){
17580             this.onTouchViewEmptyResults();
17581             return;
17582         }
17583         
17584         this.clearTouchView();
17585         
17586         var rawValue = this.getRawValue();
17587         
17588         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17589         
17590         this.tickItems = [];
17591         
17592         this.store.data.each(function(d, rowIndex){
17593             var row = this.touchViewListGroup.createChild(template);
17594             
17595             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17596                 row.addClass(d.data.cls);
17597             }
17598             
17599             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17600                 var cfg = {
17601                     data : d.data,
17602                     html : d.data[this.displayField]
17603                 };
17604                 
17605                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17606                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17607                 }
17608             }
17609             row.removeClass('selected');
17610             if(!this.multiple && this.valueField &&
17611                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17612             {
17613                 // radio buttons..
17614                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17615                 row.addClass('selected');
17616             }
17617             
17618             if(this.multiple && this.valueField &&
17619                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17620             {
17621                 
17622                 // checkboxes...
17623                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17624                 this.tickItems.push(d.data);
17625             }
17626             
17627             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17628             
17629         }, this);
17630         
17631         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17632         
17633         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17634
17635         if(this.modalTitle.length){
17636             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17637         }
17638
17639         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17640         
17641         if(this.mobile_restrict_height && listHeight < bodyHeight){
17642             this.touchViewBodyEl.setHeight(listHeight);
17643         }
17644         
17645         var _this = this;
17646         
17647         if(firstChecked && listHeight > bodyHeight){
17648             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17649         }
17650         
17651     },
17652     
17653     onTouchViewLoadException : function()
17654     {
17655         this.hideTouchView();
17656     },
17657     
17658     onTouchViewEmptyResults : function()
17659     {
17660         this.clearTouchView();
17661         
17662         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17663         
17664         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17665         
17666     },
17667     
17668     clearTouchView : function()
17669     {
17670         this.touchViewListGroup.dom.innerHTML = '';
17671     },
17672     
17673     onTouchViewClick : function(e, el, o)
17674     {
17675         e.preventDefault();
17676         
17677         var row = o.row;
17678         var rowIndex = o.rowIndex;
17679         
17680         var r = this.store.getAt(rowIndex);
17681         
17682         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17683             
17684             if(!this.multiple){
17685                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17686                     c.dom.removeAttribute('checked');
17687                 }, this);
17688
17689                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17690
17691                 this.setFromData(r.data);
17692
17693                 var close = this.closeTriggerEl();
17694
17695                 if(close){
17696                     close.show();
17697                 }
17698
17699                 this.hideTouchView();
17700
17701                 this.fireEvent('select', this, r, rowIndex);
17702
17703                 return;
17704             }
17705
17706             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17707                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17708                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17709                 return;
17710             }
17711
17712             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17713             this.addItem(r.data);
17714             this.tickItems.push(r.data);
17715         }
17716     },
17717     
17718     getAutoCreateNativeIOS : function()
17719     {
17720         var cfg = {
17721             cls: 'form-group' //input-group,
17722         };
17723         
17724         var combobox =  {
17725             tag: 'select',
17726             cls : 'roo-ios-select'
17727         };
17728         
17729         if (this.name) {
17730             combobox.name = this.name;
17731         }
17732         
17733         if (this.disabled) {
17734             combobox.disabled = true;
17735         }
17736         
17737         var settings = this;
17738         
17739         ['xs','sm','md','lg'].map(function(size){
17740             if (settings[size]) {
17741                 cfg.cls += ' col-' + size + '-' + settings[size];
17742             }
17743         });
17744         
17745         cfg.cn = combobox;
17746         
17747         return cfg;
17748         
17749     },
17750     
17751     initIOSView : function()
17752     {
17753         this.store.on('load', this.onIOSViewLoad, this);
17754         
17755         return;
17756     },
17757     
17758     onIOSViewLoad : function()
17759     {
17760         if(this.store.getCount() < 1){
17761             return;
17762         }
17763         
17764         this.clearIOSView();
17765         
17766         if(this.allowBlank) {
17767             
17768             var default_text = '-- SELECT --';
17769             
17770             if(this.placeholder.length){
17771                 default_text = this.placeholder;
17772             }
17773             
17774             if(this.emptyTitle.length){
17775                 default_text += ' - ' + this.emptyTitle + ' -';
17776             }
17777             
17778             var opt = this.inputEl().createChild({
17779                 tag: 'option',
17780                 value : 0,
17781                 html : default_text
17782             });
17783             
17784             var o = {};
17785             o[this.valueField] = 0;
17786             o[this.displayField] = default_text;
17787             
17788             this.ios_options.push({
17789                 data : o,
17790                 el : opt
17791             });
17792             
17793         }
17794         
17795         this.store.data.each(function(d, rowIndex){
17796             
17797             var html = '';
17798             
17799             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17800                 html = d.data[this.displayField];
17801             }
17802             
17803             var value = '';
17804             
17805             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17806                 value = d.data[this.valueField];
17807             }
17808             
17809             var option = {
17810                 tag: 'option',
17811                 value : value,
17812                 html : html
17813             };
17814             
17815             if(this.value == d.data[this.valueField]){
17816                 option['selected'] = true;
17817             }
17818             
17819             var opt = this.inputEl().createChild(option);
17820             
17821             this.ios_options.push({
17822                 data : d.data,
17823                 el : opt
17824             });
17825             
17826         }, this);
17827         
17828         this.inputEl().on('change', function(){
17829            this.fireEvent('select', this);
17830         }, this);
17831         
17832     },
17833     
17834     clearIOSView: function()
17835     {
17836         this.inputEl().dom.innerHTML = '';
17837         
17838         this.ios_options = [];
17839     },
17840     
17841     setIOSValue: function(v)
17842     {
17843         this.value = v;
17844         
17845         if(!this.ios_options){
17846             return;
17847         }
17848         
17849         Roo.each(this.ios_options, function(opts){
17850            
17851            opts.el.dom.removeAttribute('selected');
17852            
17853            if(opts.data[this.valueField] != v){
17854                return;
17855            }
17856            
17857            opts.el.dom.setAttribute('selected', true);
17858            
17859         }, this);
17860     }
17861
17862     /** 
17863     * @cfg {Boolean} grow 
17864     * @hide 
17865     */
17866     /** 
17867     * @cfg {Number} growMin 
17868     * @hide 
17869     */
17870     /** 
17871     * @cfg {Number} growMax 
17872     * @hide 
17873     */
17874     /**
17875      * @hide
17876      * @method autoSize
17877      */
17878 });
17879
17880 Roo.apply(Roo.bootstrap.ComboBox,  {
17881     
17882     header : {
17883         tag: 'div',
17884         cls: 'modal-header',
17885         cn: [
17886             {
17887                 tag: 'h4',
17888                 cls: 'modal-title'
17889             }
17890         ]
17891     },
17892     
17893     body : {
17894         tag: 'div',
17895         cls: 'modal-body',
17896         cn: [
17897             {
17898                 tag: 'ul',
17899                 cls: 'list-group'
17900             }
17901         ]
17902     },
17903     
17904     listItemRadio : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'radio'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     listItemCheckbox : {
17929         tag: 'li',
17930         cls: 'list-group-item',
17931         cn: [
17932             {
17933                 tag: 'span',
17934                 cls: 'roo-combobox-list-group-item-value'
17935             },
17936             {
17937                 tag: 'div',
17938                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17939                 cn: [
17940                     {
17941                         tag: 'input',
17942                         type: 'checkbox'
17943                     },
17944                     {
17945                         tag: 'label'
17946                     }
17947                 ]
17948             }
17949         ]
17950     },
17951     
17952     emptyResult : {
17953         tag: 'div',
17954         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17955     },
17956     
17957     footer : {
17958         tag: 'div',
17959         cls: 'modal-footer',
17960         cn: [
17961             {
17962                 tag: 'div',
17963                 cls: 'row',
17964                 cn: [
17965                     {
17966                         tag: 'div',
17967                         cls: 'col-xs-6 text-left',
17968                         cn: {
17969                             tag: 'button',
17970                             cls: 'btn btn-danger roo-touch-view-cancel',
17971                             html: 'Cancel'
17972                         }
17973                     },
17974                     {
17975                         tag: 'div',
17976                         cls: 'col-xs-6 text-right',
17977                         cn: {
17978                             tag: 'button',
17979                             cls: 'btn btn-success roo-touch-view-ok',
17980                             html: 'OK'
17981                         }
17982                     }
17983                 ]
17984             }
17985         ]
17986         
17987     }
17988 });
17989
17990 Roo.apply(Roo.bootstrap.ComboBox,  {
17991     
17992     touchViewTemplate : {
17993         tag: 'div',
17994         cls: 'modal fade roo-combobox-touch-view',
17995         cn: [
17996             {
17997                 tag: 'div',
17998                 cls: 'modal-dialog',
17999                 style : 'position:fixed', // we have to fix position....
18000                 cn: [
18001                     {
18002                         tag: 'div',
18003                         cls: 'modal-content',
18004                         cn: [
18005                             Roo.bootstrap.ComboBox.header,
18006                             Roo.bootstrap.ComboBox.body,
18007                             Roo.bootstrap.ComboBox.footer
18008                         ]
18009                     }
18010                 ]
18011             }
18012         ]
18013     }
18014 });/*
18015  * Based on:
18016  * Ext JS Library 1.1.1
18017  * Copyright(c) 2006-2007, Ext JS, LLC.
18018  *
18019  * Originally Released Under LGPL - original licence link has changed is not relivant.
18020  *
18021  * Fork - LGPL
18022  * <script type="text/javascript">
18023  */
18024
18025 /**
18026  * @class Roo.View
18027  * @extends Roo.util.Observable
18028  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18029  * This class also supports single and multi selection modes. <br>
18030  * Create a data model bound view:
18031  <pre><code>
18032  var store = new Roo.data.Store(...);
18033
18034  var view = new Roo.View({
18035     el : "my-element",
18036     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18037  
18038     singleSelect: true,
18039     selectedClass: "ydataview-selected",
18040     store: store
18041  });
18042
18043  // listen for node click?
18044  view.on("click", function(vw, index, node, e){
18045  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18046  });
18047
18048  // load XML data
18049  dataModel.load("foobar.xml");
18050  </code></pre>
18051  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18052  * <br><br>
18053  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18054  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18055  * 
18056  * Note: old style constructor is still suported (container, template, config)
18057  * 
18058  * @constructor
18059  * Create a new View
18060  * @param {Object} config The config object
18061  * 
18062  */
18063 Roo.View = function(config, depreciated_tpl, depreciated_config){
18064     
18065     this.parent = false;
18066     
18067     if (typeof(depreciated_tpl) == 'undefined') {
18068         // new way.. - universal constructor.
18069         Roo.apply(this, config);
18070         this.el  = Roo.get(this.el);
18071     } else {
18072         // old format..
18073         this.el  = Roo.get(config);
18074         this.tpl = depreciated_tpl;
18075         Roo.apply(this, depreciated_config);
18076     }
18077     this.wrapEl  = this.el.wrap().wrap();
18078     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18079     
18080     
18081     if(typeof(this.tpl) == "string"){
18082         this.tpl = new Roo.Template(this.tpl);
18083     } else {
18084         // support xtype ctors..
18085         this.tpl = new Roo.factory(this.tpl, Roo);
18086     }
18087     
18088     
18089     this.tpl.compile();
18090     
18091     /** @private */
18092     this.addEvents({
18093         /**
18094          * @event beforeclick
18095          * Fires before a click is processed. Returns false to cancel the default action.
18096          * @param {Roo.View} this
18097          * @param {Number} index The index of the target node
18098          * @param {HTMLElement} node The target node
18099          * @param {Roo.EventObject} e The raw event object
18100          */
18101             "beforeclick" : true,
18102         /**
18103          * @event click
18104          * Fires when a template node is clicked.
18105          * @param {Roo.View} this
18106          * @param {Number} index The index of the target node
18107          * @param {HTMLElement} node The target node
18108          * @param {Roo.EventObject} e The raw event object
18109          */
18110             "click" : true,
18111         /**
18112          * @event dblclick
18113          * Fires when a template node is double clicked.
18114          * @param {Roo.View} this
18115          * @param {Number} index The index of the target node
18116          * @param {HTMLElement} node The target node
18117          * @param {Roo.EventObject} e The raw event object
18118          */
18119             "dblclick" : true,
18120         /**
18121          * @event contextmenu
18122          * Fires when a template node is right clicked.
18123          * @param {Roo.View} this
18124          * @param {Number} index The index of the target node
18125          * @param {HTMLElement} node The target node
18126          * @param {Roo.EventObject} e The raw event object
18127          */
18128             "contextmenu" : true,
18129         /**
18130          * @event selectionchange
18131          * Fires when the selected nodes change.
18132          * @param {Roo.View} this
18133          * @param {Array} selections Array of the selected nodes
18134          */
18135             "selectionchange" : true,
18136     
18137         /**
18138          * @event beforeselect
18139          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18140          * @param {Roo.View} this
18141          * @param {HTMLElement} node The node to be selected
18142          * @param {Array} selections Array of currently selected nodes
18143          */
18144             "beforeselect" : true,
18145         /**
18146          * @event preparedata
18147          * Fires on every row to render, to allow you to change the data.
18148          * @param {Roo.View} this
18149          * @param {Object} data to be rendered (change this)
18150          */
18151           "preparedata" : true
18152           
18153           
18154         });
18155
18156
18157
18158     this.el.on({
18159         "click": this.onClick,
18160         "dblclick": this.onDblClick,
18161         "contextmenu": this.onContextMenu,
18162         scope:this
18163     });
18164
18165     this.selections = [];
18166     this.nodes = [];
18167     this.cmp = new Roo.CompositeElementLite([]);
18168     if(this.store){
18169         this.store = Roo.factory(this.store, Roo.data);
18170         this.setStore(this.store, true);
18171     }
18172     
18173     if ( this.footer && this.footer.xtype) {
18174            
18175          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18176         
18177         this.footer.dataSource = this.store;
18178         this.footer.container = fctr;
18179         this.footer = Roo.factory(this.footer, Roo);
18180         fctr.insertFirst(this.el);
18181         
18182         // this is a bit insane - as the paging toolbar seems to detach the el..
18183 //        dom.parentNode.parentNode.parentNode
18184          // they get detached?
18185     }
18186     
18187     
18188     Roo.View.superclass.constructor.call(this);
18189     
18190     
18191 };
18192
18193 Roo.extend(Roo.View, Roo.util.Observable, {
18194     
18195      /**
18196      * @cfg {Roo.data.Store} store Data store to load data from.
18197      */
18198     store : false,
18199     
18200     /**
18201      * @cfg {String|Roo.Element} el The container element.
18202      */
18203     el : '',
18204     
18205     /**
18206      * @cfg {String|Roo.Template} tpl The template used by this View 
18207      */
18208     tpl : false,
18209     /**
18210      * @cfg {String} dataName the named area of the template to use as the data area
18211      *                          Works with domtemplates roo-name="name"
18212      */
18213     dataName: false,
18214     /**
18215      * @cfg {String} selectedClass The css class to add to selected nodes
18216      */
18217     selectedClass : "x-view-selected",
18218      /**
18219      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18220      */
18221     emptyText : "",
18222     
18223     /**
18224      * @cfg {String} text to display on mask (default Loading)
18225      */
18226     mask : false,
18227     /**
18228      * @cfg {Boolean} multiSelect Allow multiple selection
18229      */
18230     multiSelect : false,
18231     /**
18232      * @cfg {Boolean} singleSelect Allow single selection
18233      */
18234     singleSelect:  false,
18235     
18236     /**
18237      * @cfg {Boolean} toggleSelect - selecting 
18238      */
18239     toggleSelect : false,
18240     
18241     /**
18242      * @cfg {Boolean} tickable - selecting 
18243      */
18244     tickable : false,
18245     
18246     /**
18247      * Returns the element this view is bound to.
18248      * @return {Roo.Element}
18249      */
18250     getEl : function(){
18251         return this.wrapEl;
18252     },
18253     
18254     
18255
18256     /**
18257      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18258      */
18259     refresh : function(){
18260         //Roo.log('refresh');
18261         var t = this.tpl;
18262         
18263         // if we are using something like 'domtemplate', then
18264         // the what gets used is:
18265         // t.applySubtemplate(NAME, data, wrapping data..)
18266         // the outer template then get' applied with
18267         //     the store 'extra data'
18268         // and the body get's added to the
18269         //      roo-name="data" node?
18270         //      <span class='roo-tpl-{name}'></span> ?????
18271         
18272         
18273         
18274         this.clearSelections();
18275         this.el.update("");
18276         var html = [];
18277         var records = this.store.getRange();
18278         if(records.length < 1) {
18279             
18280             // is this valid??  = should it render a template??
18281             
18282             this.el.update(this.emptyText);
18283             return;
18284         }
18285         var el = this.el;
18286         if (this.dataName) {
18287             this.el.update(t.apply(this.store.meta)); //????
18288             el = this.el.child('.roo-tpl-' + this.dataName);
18289         }
18290         
18291         for(var i = 0, len = records.length; i < len; i++){
18292             var data = this.prepareData(records[i].data, i, records[i]);
18293             this.fireEvent("preparedata", this, data, i, records[i]);
18294             
18295             var d = Roo.apply({}, data);
18296             
18297             if(this.tickable){
18298                 Roo.apply(d, {'roo-id' : Roo.id()});
18299                 
18300                 var _this = this;
18301             
18302                 Roo.each(this.parent.item, function(item){
18303                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18304                         return;
18305                     }
18306                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18307                 });
18308             }
18309             
18310             html[html.length] = Roo.util.Format.trim(
18311                 this.dataName ?
18312                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18313                     t.apply(d)
18314             );
18315         }
18316         
18317         
18318         
18319         el.update(html.join(""));
18320         this.nodes = el.dom.childNodes;
18321         this.updateIndexes(0);
18322     },
18323     
18324
18325     /**
18326      * Function to override to reformat the data that is sent to
18327      * the template for each node.
18328      * DEPRICATED - use the preparedata event handler.
18329      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18330      * a JSON object for an UpdateManager bound view).
18331      */
18332     prepareData : function(data, index, record)
18333     {
18334         this.fireEvent("preparedata", this, data, index, record);
18335         return data;
18336     },
18337
18338     onUpdate : function(ds, record){
18339         // Roo.log('on update');   
18340         this.clearSelections();
18341         var index = this.store.indexOf(record);
18342         var n = this.nodes[index];
18343         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18344         n.parentNode.removeChild(n);
18345         this.updateIndexes(index, index);
18346     },
18347
18348     
18349     
18350 // --------- FIXME     
18351     onAdd : function(ds, records, index)
18352     {
18353         //Roo.log(['on Add', ds, records, index] );        
18354         this.clearSelections();
18355         if(this.nodes.length == 0){
18356             this.refresh();
18357             return;
18358         }
18359         var n = this.nodes[index];
18360         for(var i = 0, len = records.length; i < len; i++){
18361             var d = this.prepareData(records[i].data, i, records[i]);
18362             if(n){
18363                 this.tpl.insertBefore(n, d);
18364             }else{
18365                 
18366                 this.tpl.append(this.el, d);
18367             }
18368         }
18369         this.updateIndexes(index);
18370     },
18371
18372     onRemove : function(ds, record, index){
18373        // Roo.log('onRemove');
18374         this.clearSelections();
18375         var el = this.dataName  ?
18376             this.el.child('.roo-tpl-' + this.dataName) :
18377             this.el; 
18378         
18379         el.dom.removeChild(this.nodes[index]);
18380         this.updateIndexes(index);
18381     },
18382
18383     /**
18384      * Refresh an individual node.
18385      * @param {Number} index
18386      */
18387     refreshNode : function(index){
18388         this.onUpdate(this.store, this.store.getAt(index));
18389     },
18390
18391     updateIndexes : function(startIndex, endIndex){
18392         var ns = this.nodes;
18393         startIndex = startIndex || 0;
18394         endIndex = endIndex || ns.length - 1;
18395         for(var i = startIndex; i <= endIndex; i++){
18396             ns[i].nodeIndex = i;
18397         }
18398     },
18399
18400     /**
18401      * Changes the data store this view uses and refresh the view.
18402      * @param {Store} store
18403      */
18404     setStore : function(store, initial){
18405         if(!initial && this.store){
18406             this.store.un("datachanged", this.refresh);
18407             this.store.un("add", this.onAdd);
18408             this.store.un("remove", this.onRemove);
18409             this.store.un("update", this.onUpdate);
18410             this.store.un("clear", this.refresh);
18411             this.store.un("beforeload", this.onBeforeLoad);
18412             this.store.un("load", this.onLoad);
18413             this.store.un("loadexception", this.onLoad);
18414         }
18415         if(store){
18416           
18417             store.on("datachanged", this.refresh, this);
18418             store.on("add", this.onAdd, this);
18419             store.on("remove", this.onRemove, this);
18420             store.on("update", this.onUpdate, this);
18421             store.on("clear", this.refresh, this);
18422             store.on("beforeload", this.onBeforeLoad, this);
18423             store.on("load", this.onLoad, this);
18424             store.on("loadexception", this.onLoad, this);
18425         }
18426         
18427         if(store){
18428             this.refresh();
18429         }
18430     },
18431     /**
18432      * onbeforeLoad - masks the loading area.
18433      *
18434      */
18435     onBeforeLoad : function(store,opts)
18436     {
18437          //Roo.log('onBeforeLoad');   
18438         if (!opts.add) {
18439             this.el.update("");
18440         }
18441         this.el.mask(this.mask ? this.mask : "Loading" ); 
18442     },
18443     onLoad : function ()
18444     {
18445         this.el.unmask();
18446     },
18447     
18448
18449     /**
18450      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18451      * @param {HTMLElement} node
18452      * @return {HTMLElement} The template node
18453      */
18454     findItemFromChild : function(node){
18455         var el = this.dataName  ?
18456             this.el.child('.roo-tpl-' + this.dataName,true) :
18457             this.el.dom; 
18458         
18459         if(!node || node.parentNode == el){
18460                     return node;
18461             }
18462             var p = node.parentNode;
18463             while(p && p != el){
18464             if(p.parentNode == el){
18465                 return p;
18466             }
18467             p = p.parentNode;
18468         }
18469             return null;
18470     },
18471
18472     /** @ignore */
18473     onClick : function(e){
18474         var item = this.findItemFromChild(e.getTarget());
18475         if(item){
18476             var index = this.indexOf(item);
18477             if(this.onItemClick(item, index, e) !== false){
18478                 this.fireEvent("click", this, index, item, e);
18479             }
18480         }else{
18481             this.clearSelections();
18482         }
18483     },
18484
18485     /** @ignore */
18486     onContextMenu : function(e){
18487         var item = this.findItemFromChild(e.getTarget());
18488         if(item){
18489             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18490         }
18491     },
18492
18493     /** @ignore */
18494     onDblClick : function(e){
18495         var item = this.findItemFromChild(e.getTarget());
18496         if(item){
18497             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18498         }
18499     },
18500
18501     onItemClick : function(item, index, e)
18502     {
18503         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18504             return false;
18505         }
18506         if (this.toggleSelect) {
18507             var m = this.isSelected(item) ? 'unselect' : 'select';
18508             //Roo.log(m);
18509             var _t = this;
18510             _t[m](item, true, false);
18511             return true;
18512         }
18513         if(this.multiSelect || this.singleSelect){
18514             if(this.multiSelect && e.shiftKey && this.lastSelection){
18515                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18516             }else{
18517                 this.select(item, this.multiSelect && e.ctrlKey);
18518                 this.lastSelection = item;
18519             }
18520             
18521             if(!this.tickable){
18522                 e.preventDefault();
18523             }
18524             
18525         }
18526         return true;
18527     },
18528
18529     /**
18530      * Get the number of selected nodes.
18531      * @return {Number}
18532      */
18533     getSelectionCount : function(){
18534         return this.selections.length;
18535     },
18536
18537     /**
18538      * Get the currently selected nodes.
18539      * @return {Array} An array of HTMLElements
18540      */
18541     getSelectedNodes : function(){
18542         return this.selections;
18543     },
18544
18545     /**
18546      * Get the indexes of the selected nodes.
18547      * @return {Array}
18548      */
18549     getSelectedIndexes : function(){
18550         var indexes = [], s = this.selections;
18551         for(var i = 0, len = s.length; i < len; i++){
18552             indexes.push(s[i].nodeIndex);
18553         }
18554         return indexes;
18555     },
18556
18557     /**
18558      * Clear all selections
18559      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18560      */
18561     clearSelections : function(suppressEvent){
18562         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18563             this.cmp.elements = this.selections;
18564             this.cmp.removeClass(this.selectedClass);
18565             this.selections = [];
18566             if(!suppressEvent){
18567                 this.fireEvent("selectionchange", this, this.selections);
18568             }
18569         }
18570     },
18571
18572     /**
18573      * Returns true if the passed node is selected
18574      * @param {HTMLElement/Number} node The node or node index
18575      * @return {Boolean}
18576      */
18577     isSelected : function(node){
18578         var s = this.selections;
18579         if(s.length < 1){
18580             return false;
18581         }
18582         node = this.getNode(node);
18583         return s.indexOf(node) !== -1;
18584     },
18585
18586     /**
18587      * Selects nodes.
18588      * @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
18589      * @param {Boolean} keepExisting (optional) true to keep existing selections
18590      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18591      */
18592     select : function(nodeInfo, keepExisting, suppressEvent){
18593         if(nodeInfo instanceof Array){
18594             if(!keepExisting){
18595                 this.clearSelections(true);
18596             }
18597             for(var i = 0, len = nodeInfo.length; i < len; i++){
18598                 this.select(nodeInfo[i], true, true);
18599             }
18600             return;
18601         } 
18602         var node = this.getNode(nodeInfo);
18603         if(!node || this.isSelected(node)){
18604             return; // already selected.
18605         }
18606         if(!keepExisting){
18607             this.clearSelections(true);
18608         }
18609         
18610         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18611             Roo.fly(node).addClass(this.selectedClass);
18612             this.selections.push(node);
18613             if(!suppressEvent){
18614                 this.fireEvent("selectionchange", this, this.selections);
18615             }
18616         }
18617         
18618         
18619     },
18620       /**
18621      * Unselects nodes.
18622      * @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
18623      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18625      */
18626     unselect : function(nodeInfo, keepExisting, suppressEvent)
18627     {
18628         if(nodeInfo instanceof Array){
18629             Roo.each(this.selections, function(s) {
18630                 this.unselect(s, nodeInfo);
18631             }, this);
18632             return;
18633         }
18634         var node = this.getNode(nodeInfo);
18635         if(!node || !this.isSelected(node)){
18636             //Roo.log("not selected");
18637             return; // not selected.
18638         }
18639         // fireevent???
18640         var ns = [];
18641         Roo.each(this.selections, function(s) {
18642             if (s == node ) {
18643                 Roo.fly(node).removeClass(this.selectedClass);
18644
18645                 return;
18646             }
18647             ns.push(s);
18648         },this);
18649         
18650         this.selections= ns;
18651         this.fireEvent("selectionchange", this, this.selections);
18652     },
18653
18654     /**
18655      * Gets a template node.
18656      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18657      * @return {HTMLElement} The node or null if it wasn't found
18658      */
18659     getNode : function(nodeInfo){
18660         if(typeof nodeInfo == "string"){
18661             return document.getElementById(nodeInfo);
18662         }else if(typeof nodeInfo == "number"){
18663             return this.nodes[nodeInfo];
18664         }
18665         return nodeInfo;
18666     },
18667
18668     /**
18669      * Gets a range template nodes.
18670      * @param {Number} startIndex
18671      * @param {Number} endIndex
18672      * @return {Array} An array of nodes
18673      */
18674     getNodes : function(start, end){
18675         var ns = this.nodes;
18676         start = start || 0;
18677         end = typeof end == "undefined" ? ns.length - 1 : end;
18678         var nodes = [];
18679         if(start <= end){
18680             for(var i = start; i <= end; i++){
18681                 nodes.push(ns[i]);
18682             }
18683         } else{
18684             for(var i = start; i >= end; i--){
18685                 nodes.push(ns[i]);
18686             }
18687         }
18688         return nodes;
18689     },
18690
18691     /**
18692      * Finds the index of the passed node
18693      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18694      * @return {Number} The index of the node or -1
18695      */
18696     indexOf : function(node){
18697         node = this.getNode(node);
18698         if(typeof node.nodeIndex == "number"){
18699             return node.nodeIndex;
18700         }
18701         var ns = this.nodes;
18702         for(var i = 0, len = ns.length; i < len; i++){
18703             if(ns[i] == node){
18704                 return i;
18705             }
18706         }
18707         return -1;
18708     }
18709 });
18710 /*
18711  * - LGPL
18712  *
18713  * based on jquery fullcalendar
18714  * 
18715  */
18716
18717 Roo.bootstrap = Roo.bootstrap || {};
18718 /**
18719  * @class Roo.bootstrap.Calendar
18720  * @extends Roo.bootstrap.Component
18721  * Bootstrap Calendar class
18722  * @cfg {Boolean} loadMask (true|false) default false
18723  * @cfg {Object} header generate the user specific header of the calendar, default false
18724
18725  * @constructor
18726  * Create a new Container
18727  * @param {Object} config The config object
18728  */
18729
18730
18731
18732 Roo.bootstrap.Calendar = function(config){
18733     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18734      this.addEvents({
18735         /**
18736              * @event select
18737              * Fires when a date is selected
18738              * @param {DatePicker} this
18739              * @param {Date} date The selected date
18740              */
18741         'select': true,
18742         /**
18743              * @event monthchange
18744              * Fires when the displayed month changes 
18745              * @param {DatePicker} this
18746              * @param {Date} date The selected month
18747              */
18748         'monthchange': true,
18749         /**
18750              * @event evententer
18751              * Fires when mouse over an event
18752              * @param {Calendar} this
18753              * @param {event} Event
18754              */
18755         'evententer': true,
18756         /**
18757              * @event eventleave
18758              * Fires when the mouse leaves an
18759              * @param {Calendar} this
18760              * @param {event}
18761              */
18762         'eventleave': true,
18763         /**
18764              * @event eventclick
18765              * Fires when the mouse click an
18766              * @param {Calendar} this
18767              * @param {event}
18768              */
18769         'eventclick': true
18770         
18771     });
18772
18773 };
18774
18775 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18776     
18777      /**
18778      * @cfg {Number} startDay
18779      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18780      */
18781     startDay : 0,
18782     
18783     loadMask : false,
18784     
18785     header : false,
18786       
18787     getAutoCreate : function(){
18788         
18789         
18790         var fc_button = function(name, corner, style, content ) {
18791             return Roo.apply({},{
18792                 tag : 'span',
18793                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18794                          (corner.length ?
18795                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18796                             ''
18797                         ),
18798                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18799                 unselectable: 'on'
18800             });
18801         };
18802         
18803         var header = {};
18804         
18805         if(!this.header){
18806             header = {
18807                 tag : 'table',
18808                 cls : 'fc-header',
18809                 style : 'width:100%',
18810                 cn : [
18811                     {
18812                         tag: 'tr',
18813                         cn : [
18814                             {
18815                                 tag : 'td',
18816                                 cls : 'fc-header-left',
18817                                 cn : [
18818                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18819                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18820                                     { tag: 'span', cls: 'fc-header-space' },
18821                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18822
18823
18824                                 ]
18825                             },
18826
18827                             {
18828                                 tag : 'td',
18829                                 cls : 'fc-header-center',
18830                                 cn : [
18831                                     {
18832                                         tag: 'span',
18833                                         cls: 'fc-header-title',
18834                                         cn : {
18835                                             tag: 'H2',
18836                                             html : 'month / year'
18837                                         }
18838                                     }
18839
18840                                 ]
18841                             },
18842                             {
18843                                 tag : 'td',
18844                                 cls : 'fc-header-right',
18845                                 cn : [
18846                               /*      fc_button('month', 'left', '', 'month' ),
18847                                     fc_button('week', '', '', 'week' ),
18848                                     fc_button('day', 'right', '', 'day' )
18849                                 */    
18850
18851                                 ]
18852                             }
18853
18854                         ]
18855                     }
18856                 ]
18857             };
18858         }
18859         
18860         header = this.header;
18861         
18862        
18863         var cal_heads = function() {
18864             var ret = [];
18865             // fixme - handle this.
18866             
18867             for (var i =0; i < Date.dayNames.length; i++) {
18868                 var d = Date.dayNames[i];
18869                 ret.push({
18870                     tag: 'th',
18871                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18872                     html : d.substring(0,3)
18873                 });
18874                 
18875             }
18876             ret[0].cls += ' fc-first';
18877             ret[6].cls += ' fc-last';
18878             return ret;
18879         };
18880         var cal_cell = function(n) {
18881             return  {
18882                 tag: 'td',
18883                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18884                 cn : [
18885                     {
18886                         cn : [
18887                             {
18888                                 cls: 'fc-day-number',
18889                                 html: 'D'
18890                             },
18891                             {
18892                                 cls: 'fc-day-content',
18893                              
18894                                 cn : [
18895                                      {
18896                                         style: 'position: relative;' // height: 17px;
18897                                     }
18898                                 ]
18899                             }
18900                             
18901                             
18902                         ]
18903                     }
18904                 ]
18905                 
18906             }
18907         };
18908         var cal_rows = function() {
18909             
18910             var ret = [];
18911             for (var r = 0; r < 6; r++) {
18912                 var row= {
18913                     tag : 'tr',
18914                     cls : 'fc-week',
18915                     cn : []
18916                 };
18917                 
18918                 for (var i =0; i < Date.dayNames.length; i++) {
18919                     var d = Date.dayNames[i];
18920                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18921
18922                 }
18923                 row.cn[0].cls+=' fc-first';
18924                 row.cn[0].cn[0].style = 'min-height:90px';
18925                 row.cn[6].cls+=' fc-last';
18926                 ret.push(row);
18927                 
18928             }
18929             ret[0].cls += ' fc-first';
18930             ret[4].cls += ' fc-prev-last';
18931             ret[5].cls += ' fc-last';
18932             return ret;
18933             
18934         };
18935         
18936         var cal_table = {
18937             tag: 'table',
18938             cls: 'fc-border-separate',
18939             style : 'width:100%',
18940             cellspacing  : 0,
18941             cn : [
18942                 { 
18943                     tag: 'thead',
18944                     cn : [
18945                         { 
18946                             tag: 'tr',
18947                             cls : 'fc-first fc-last',
18948                             cn : cal_heads()
18949                         }
18950                     ]
18951                 },
18952                 { 
18953                     tag: 'tbody',
18954                     cn : cal_rows()
18955                 }
18956                   
18957             ]
18958         };
18959          
18960          var cfg = {
18961             cls : 'fc fc-ltr',
18962             cn : [
18963                 header,
18964                 {
18965                     cls : 'fc-content',
18966                     style : "position: relative;",
18967                     cn : [
18968                         {
18969                             cls : 'fc-view fc-view-month fc-grid',
18970                             style : 'position: relative',
18971                             unselectable : 'on',
18972                             cn : [
18973                                 {
18974                                     cls : 'fc-event-container',
18975                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18976                                 },
18977                                 cal_table
18978                             ]
18979                         }
18980                     ]
18981     
18982                 }
18983            ] 
18984             
18985         };
18986         
18987          
18988         
18989         return cfg;
18990     },
18991     
18992     
18993     initEvents : function()
18994     {
18995         if(!this.store){
18996             throw "can not find store for calendar";
18997         }
18998         
18999         var mark = {
19000             tag: "div",
19001             cls:"x-dlg-mask",
19002             style: "text-align:center",
19003             cn: [
19004                 {
19005                     tag: "div",
19006                     style: "background-color:white;width:50%;margin:250 auto",
19007                     cn: [
19008                         {
19009                             tag: "img",
19010                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19011                         },
19012                         {
19013                             tag: "span",
19014                             html: "Loading"
19015                         }
19016                         
19017                     ]
19018                 }
19019             ]
19020         };
19021         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19022         
19023         var size = this.el.select('.fc-content', true).first().getSize();
19024         this.maskEl.setSize(size.width, size.height);
19025         this.maskEl.enableDisplayMode("block");
19026         if(!this.loadMask){
19027             this.maskEl.hide();
19028         }
19029         
19030         this.store = Roo.factory(this.store, Roo.data);
19031         this.store.on('load', this.onLoad, this);
19032         this.store.on('beforeload', this.onBeforeLoad, this);
19033         
19034         this.resize();
19035         
19036         this.cells = this.el.select('.fc-day',true);
19037         //Roo.log(this.cells);
19038         this.textNodes = this.el.query('.fc-day-number');
19039         this.cells.addClassOnOver('fc-state-hover');
19040         
19041         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19042         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19043         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19044         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19045         
19046         this.on('monthchange', this.onMonthChange, this);
19047         
19048         this.update(new Date().clearTime());
19049     },
19050     
19051     resize : function() {
19052         var sz  = this.el.getSize();
19053         
19054         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19055         this.el.select('.fc-day-content div',true).setHeight(34);
19056     },
19057     
19058     
19059     // private
19060     showPrevMonth : function(e){
19061         this.update(this.activeDate.add("mo", -1));
19062     },
19063     showToday : function(e){
19064         this.update(new Date().clearTime());
19065     },
19066     // private
19067     showNextMonth : function(e){
19068         this.update(this.activeDate.add("mo", 1));
19069     },
19070
19071     // private
19072     showPrevYear : function(){
19073         this.update(this.activeDate.add("y", -1));
19074     },
19075
19076     // private
19077     showNextYear : function(){
19078         this.update(this.activeDate.add("y", 1));
19079     },
19080
19081     
19082    // private
19083     update : function(date)
19084     {
19085         var vd = this.activeDate;
19086         this.activeDate = date;
19087 //        if(vd && this.el){
19088 //            var t = date.getTime();
19089 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19090 //                Roo.log('using add remove');
19091 //                
19092 //                this.fireEvent('monthchange', this, date);
19093 //                
19094 //                this.cells.removeClass("fc-state-highlight");
19095 //                this.cells.each(function(c){
19096 //                   if(c.dateValue == t){
19097 //                       c.addClass("fc-state-highlight");
19098 //                       setTimeout(function(){
19099 //                            try{c.dom.firstChild.focus();}catch(e){}
19100 //                       }, 50);
19101 //                       return false;
19102 //                   }
19103 //                   return true;
19104 //                });
19105 //                return;
19106 //            }
19107 //        }
19108         
19109         var days = date.getDaysInMonth();
19110         
19111         var firstOfMonth = date.getFirstDateOfMonth();
19112         var startingPos = firstOfMonth.getDay()-this.startDay;
19113         
19114         if(startingPos < this.startDay){
19115             startingPos += 7;
19116         }
19117         
19118         var pm = date.add(Date.MONTH, -1);
19119         var prevStart = pm.getDaysInMonth()-startingPos;
19120 //        
19121         this.cells = this.el.select('.fc-day',true);
19122         this.textNodes = this.el.query('.fc-day-number');
19123         this.cells.addClassOnOver('fc-state-hover');
19124         
19125         var cells = this.cells.elements;
19126         var textEls = this.textNodes;
19127         
19128         Roo.each(cells, function(cell){
19129             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19130         });
19131         
19132         days += startingPos;
19133
19134         // convert everything to numbers so it's fast
19135         var day = 86400000;
19136         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19137         //Roo.log(d);
19138         //Roo.log(pm);
19139         //Roo.log(prevStart);
19140         
19141         var today = new Date().clearTime().getTime();
19142         var sel = date.clearTime().getTime();
19143         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19144         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19145         var ddMatch = this.disabledDatesRE;
19146         var ddText = this.disabledDatesText;
19147         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19148         var ddaysText = this.disabledDaysText;
19149         var format = this.format;
19150         
19151         var setCellClass = function(cal, cell){
19152             cell.row = 0;
19153             cell.events = [];
19154             cell.more = [];
19155             //Roo.log('set Cell Class');
19156             cell.title = "";
19157             var t = d.getTime();
19158             
19159             //Roo.log(d);
19160             
19161             cell.dateValue = t;
19162             if(t == today){
19163                 cell.className += " fc-today";
19164                 cell.className += " fc-state-highlight";
19165                 cell.title = cal.todayText;
19166             }
19167             if(t == sel){
19168                 // disable highlight in other month..
19169                 //cell.className += " fc-state-highlight";
19170                 
19171             }
19172             // disabling
19173             if(t < min) {
19174                 cell.className = " fc-state-disabled";
19175                 cell.title = cal.minText;
19176                 return;
19177             }
19178             if(t > max) {
19179                 cell.className = " fc-state-disabled";
19180                 cell.title = cal.maxText;
19181                 return;
19182             }
19183             if(ddays){
19184                 if(ddays.indexOf(d.getDay()) != -1){
19185                     cell.title = ddaysText;
19186                     cell.className = " fc-state-disabled";
19187                 }
19188             }
19189             if(ddMatch && format){
19190                 var fvalue = d.dateFormat(format);
19191                 if(ddMatch.test(fvalue)){
19192                     cell.title = ddText.replace("%0", fvalue);
19193                     cell.className = " fc-state-disabled";
19194                 }
19195             }
19196             
19197             if (!cell.initialClassName) {
19198                 cell.initialClassName = cell.dom.className;
19199             }
19200             
19201             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19202         };
19203
19204         var i = 0;
19205         
19206         for(; i < startingPos; i++) {
19207             textEls[i].innerHTML = (++prevStart);
19208             d.setDate(d.getDate()+1);
19209             
19210             cells[i].className = "fc-past fc-other-month";
19211             setCellClass(this, cells[i]);
19212         }
19213         
19214         var intDay = 0;
19215         
19216         for(; i < days; i++){
19217             intDay = i - startingPos + 1;
19218             textEls[i].innerHTML = (intDay);
19219             d.setDate(d.getDate()+1);
19220             
19221             cells[i].className = ''; // "x-date-active";
19222             setCellClass(this, cells[i]);
19223         }
19224         var extraDays = 0;
19225         
19226         for(; i < 42; i++) {
19227             textEls[i].innerHTML = (++extraDays);
19228             d.setDate(d.getDate()+1);
19229             
19230             cells[i].className = "fc-future fc-other-month";
19231             setCellClass(this, cells[i]);
19232         }
19233         
19234         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19235         
19236         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19237         
19238         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19239         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19240         
19241         if(totalRows != 6){
19242             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19243             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19244         }
19245         
19246         this.fireEvent('monthchange', this, date);
19247         
19248         
19249         /*
19250         if(!this.internalRender){
19251             var main = this.el.dom.firstChild;
19252             var w = main.offsetWidth;
19253             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19254             Roo.fly(main).setWidth(w);
19255             this.internalRender = true;
19256             // opera does not respect the auto grow header center column
19257             // then, after it gets a width opera refuses to recalculate
19258             // without a second pass
19259             if(Roo.isOpera && !this.secondPass){
19260                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19261                 this.secondPass = true;
19262                 this.update.defer(10, this, [date]);
19263             }
19264         }
19265         */
19266         
19267     },
19268     
19269     findCell : function(dt) {
19270         dt = dt.clearTime().getTime();
19271         var ret = false;
19272         this.cells.each(function(c){
19273             //Roo.log("check " +c.dateValue + '?=' + dt);
19274             if(c.dateValue == dt){
19275                 ret = c;
19276                 return false;
19277             }
19278             return true;
19279         });
19280         
19281         return ret;
19282     },
19283     
19284     findCells : function(ev) {
19285         var s = ev.start.clone().clearTime().getTime();
19286        // Roo.log(s);
19287         var e= ev.end.clone().clearTime().getTime();
19288        // Roo.log(e);
19289         var ret = [];
19290         this.cells.each(function(c){
19291              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19292             
19293             if(c.dateValue > e){
19294                 return ;
19295             }
19296             if(c.dateValue < s){
19297                 return ;
19298             }
19299             ret.push(c);
19300         });
19301         
19302         return ret;    
19303     },
19304     
19305 //    findBestRow: function(cells)
19306 //    {
19307 //        var ret = 0;
19308 //        
19309 //        for (var i =0 ; i < cells.length;i++) {
19310 //            ret  = Math.max(cells[i].rows || 0,ret);
19311 //        }
19312 //        return ret;
19313 //        
19314 //    },
19315     
19316     
19317     addItem : function(ev)
19318     {
19319         // look for vertical location slot in
19320         var cells = this.findCells(ev);
19321         
19322 //        ev.row = this.findBestRow(cells);
19323         
19324         // work out the location.
19325         
19326         var crow = false;
19327         var rows = [];
19328         for(var i =0; i < cells.length; i++) {
19329             
19330             cells[i].row = cells[0].row;
19331             
19332             if(i == 0){
19333                 cells[i].row = cells[i].row + 1;
19334             }
19335             
19336             if (!crow) {
19337                 crow = {
19338                     start : cells[i],
19339                     end :  cells[i]
19340                 };
19341                 continue;
19342             }
19343             if (crow.start.getY() == cells[i].getY()) {
19344                 // on same row.
19345                 crow.end = cells[i];
19346                 continue;
19347             }
19348             // different row.
19349             rows.push(crow);
19350             crow = {
19351                 start: cells[i],
19352                 end : cells[i]
19353             };
19354             
19355         }
19356         
19357         rows.push(crow);
19358         ev.els = [];
19359         ev.rows = rows;
19360         ev.cells = cells;
19361         
19362         cells[0].events.push(ev);
19363         
19364         this.calevents.push(ev);
19365     },
19366     
19367     clearEvents: function() {
19368         
19369         if(!this.calevents){
19370             return;
19371         }
19372         
19373         Roo.each(this.cells.elements, function(c){
19374             c.row = 0;
19375             c.events = [];
19376             c.more = [];
19377         });
19378         
19379         Roo.each(this.calevents, function(e) {
19380             Roo.each(e.els, function(el) {
19381                 el.un('mouseenter' ,this.onEventEnter, this);
19382                 el.un('mouseleave' ,this.onEventLeave, this);
19383                 el.remove();
19384             },this);
19385         },this);
19386         
19387         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19388             e.remove();
19389         });
19390         
19391     },
19392     
19393     renderEvents: function()
19394     {   
19395         var _this = this;
19396         
19397         this.cells.each(function(c) {
19398             
19399             if(c.row < 5){
19400                 return;
19401             }
19402             
19403             var ev = c.events;
19404             
19405             var r = 4;
19406             if(c.row != c.events.length){
19407                 r = 4 - (4 - (c.row - c.events.length));
19408             }
19409             
19410             c.events = ev.slice(0, r);
19411             c.more = ev.slice(r);
19412             
19413             if(c.more.length && c.more.length == 1){
19414                 c.events.push(c.more.pop());
19415             }
19416             
19417             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19418             
19419         });
19420             
19421         this.cells.each(function(c) {
19422             
19423             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19424             
19425             
19426             for (var e = 0; e < c.events.length; e++){
19427                 var ev = c.events[e];
19428                 var rows = ev.rows;
19429                 
19430                 for(var i = 0; i < rows.length; i++) {
19431                 
19432                     // how many rows should it span..
19433
19434                     var  cfg = {
19435                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19436                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19437
19438                         unselectable : "on",
19439                         cn : [
19440                             {
19441                                 cls: 'fc-event-inner',
19442                                 cn : [
19443     //                                {
19444     //                                  tag:'span',
19445     //                                  cls: 'fc-event-time',
19446     //                                  html : cells.length > 1 ? '' : ev.time
19447     //                                },
19448                                     {
19449                                       tag:'span',
19450                                       cls: 'fc-event-title',
19451                                       html : String.format('{0}', ev.title)
19452                                     }
19453
19454
19455                                 ]
19456                             },
19457                             {
19458                                 cls: 'ui-resizable-handle ui-resizable-e',
19459                                 html : '&nbsp;&nbsp;&nbsp'
19460                             }
19461
19462                         ]
19463                     };
19464
19465                     if (i == 0) {
19466                         cfg.cls += ' fc-event-start';
19467                     }
19468                     if ((i+1) == rows.length) {
19469                         cfg.cls += ' fc-event-end';
19470                     }
19471
19472                     var ctr = _this.el.select('.fc-event-container',true).first();
19473                     var cg = ctr.createChild(cfg);
19474
19475                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19476                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19477
19478                     var r = (c.more.length) ? 1 : 0;
19479                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19480                     cg.setWidth(ebox.right - sbox.x -2);
19481
19482                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19483                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19484                     cg.on('click', _this.onEventClick, _this, ev);
19485
19486                     ev.els.push(cg);
19487                     
19488                 }
19489                 
19490             }
19491             
19492             
19493             if(c.more.length){
19494                 var  cfg = {
19495                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19496                     style : 'position: absolute',
19497                     unselectable : "on",
19498                     cn : [
19499                         {
19500                             cls: 'fc-event-inner',
19501                             cn : [
19502                                 {
19503                                   tag:'span',
19504                                   cls: 'fc-event-title',
19505                                   html : 'More'
19506                                 }
19507
19508
19509                             ]
19510                         },
19511                         {
19512                             cls: 'ui-resizable-handle ui-resizable-e',
19513                             html : '&nbsp;&nbsp;&nbsp'
19514                         }
19515
19516                     ]
19517                 };
19518
19519                 var ctr = _this.el.select('.fc-event-container',true).first();
19520                 var cg = ctr.createChild(cfg);
19521
19522                 var sbox = c.select('.fc-day-content',true).first().getBox();
19523                 var ebox = c.select('.fc-day-content',true).first().getBox();
19524                 //Roo.log(cg);
19525                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19526                 cg.setWidth(ebox.right - sbox.x -2);
19527
19528                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19529                 
19530             }
19531             
19532         });
19533         
19534         
19535         
19536     },
19537     
19538     onEventEnter: function (e, el,event,d) {
19539         this.fireEvent('evententer', this, el, event);
19540     },
19541     
19542     onEventLeave: function (e, el,event,d) {
19543         this.fireEvent('eventleave', this, el, event);
19544     },
19545     
19546     onEventClick: function (e, el,event,d) {
19547         this.fireEvent('eventclick', this, el, event);
19548     },
19549     
19550     onMonthChange: function () {
19551         this.store.load();
19552     },
19553     
19554     onMoreEventClick: function(e, el, more)
19555     {
19556         var _this = this;
19557         
19558         this.calpopover.placement = 'right';
19559         this.calpopover.setTitle('More');
19560         
19561         this.calpopover.setContent('');
19562         
19563         var ctr = this.calpopover.el.select('.popover-content', true).first();
19564         
19565         Roo.each(more, function(m){
19566             var cfg = {
19567                 cls : 'fc-event-hori fc-event-draggable',
19568                 html : m.title
19569             };
19570             var cg = ctr.createChild(cfg);
19571             
19572             cg.on('click', _this.onEventClick, _this, m);
19573         });
19574         
19575         this.calpopover.show(el);
19576         
19577         
19578     },
19579     
19580     onLoad: function () 
19581     {   
19582         this.calevents = [];
19583         var cal = this;
19584         
19585         if(this.store.getCount() > 0){
19586             this.store.data.each(function(d){
19587                cal.addItem({
19588                     id : d.data.id,
19589                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19590                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19591                     time : d.data.start_time,
19592                     title : d.data.title,
19593                     description : d.data.description,
19594                     venue : d.data.venue
19595                 });
19596             });
19597         }
19598         
19599         this.renderEvents();
19600         
19601         if(this.calevents.length && this.loadMask){
19602             this.maskEl.hide();
19603         }
19604     },
19605     
19606     onBeforeLoad: function()
19607     {
19608         this.clearEvents();
19609         if(this.loadMask){
19610             this.maskEl.show();
19611         }
19612     }
19613 });
19614
19615  
19616  /*
19617  * - LGPL
19618  *
19619  * element
19620  * 
19621  */
19622
19623 /**
19624  * @class Roo.bootstrap.Popover
19625  * @extends Roo.bootstrap.Component
19626  * Bootstrap Popover class
19627  * @cfg {String} html contents of the popover   (or false to use children..)
19628  * @cfg {String} title of popover (or false to hide)
19629  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19630  * @cfg {String} trigger click || hover (or false to trigger manually)
19631  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19632  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19633  *      - if false and it has a 'parent' then it will be automatically added to that element
19634  *      - if string - Roo.get  will be called 
19635  * @cfg {Number} delay - delay before showing
19636  
19637  * @constructor
19638  * Create a new Popover
19639  * @param {Object} config The config object
19640  */
19641
19642 Roo.bootstrap.Popover = function(config){
19643     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19644     
19645     this.addEvents({
19646         // raw events
19647          /**
19648          * @event show
19649          * After the popover show
19650          * 
19651          * @param {Roo.bootstrap.Popover} this
19652          */
19653         "show" : true,
19654         /**
19655          * @event hide
19656          * After the popover hide
19657          * 
19658          * @param {Roo.bootstrap.Popover} this
19659          */
19660         "hide" : true
19661     });
19662 };
19663
19664 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19665     
19666     title: false,
19667     html: false,
19668     
19669     placement : 'right',
19670     trigger : 'hover', // hover
19671     modal : false,
19672     delay : 0,
19673     
19674     over: false,
19675     
19676     can_build_overlaid : false,
19677     
19678     maskEl : false, // the mask element
19679     headerEl : false,
19680     contentEl : false,
19681     alignEl : false, // when show is called with an element - this get's stored.
19682     
19683     getChildContainer : function()
19684     {
19685         return this.contentEl;
19686         
19687     },
19688     getPopoverHeader : function()
19689     {
19690         this.title = true; // flag not to hide it..
19691         this.headerEl.addClass('p-0');
19692         return this.headerEl
19693     },
19694     
19695     
19696     getAutoCreate : function(){
19697          
19698         var cfg = {
19699            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19700            style: 'display:block',
19701            cn : [
19702                 {
19703                     cls : 'arrow'
19704                 },
19705                 {
19706                     cls : 'popover-inner ',
19707                     cn : [
19708                         {
19709                             tag: 'h3',
19710                             cls: 'popover-title popover-header',
19711                             html : this.title === false ? '' : this.title
19712                         },
19713                         {
19714                             cls : 'popover-content popover-body '  + (this.cls || ''),
19715                             html : this.html || ''
19716                         }
19717                     ]
19718                     
19719                 }
19720            ]
19721         };
19722         
19723         return cfg;
19724     },
19725     /**
19726      * @param {string} the title
19727      */
19728     setTitle: function(str)
19729     {
19730         this.title = str;
19731         if (this.el) {
19732             this.headerEl.dom.innerHTML = str;
19733         }
19734         
19735     },
19736     /**
19737      * @param {string} the body content
19738      */
19739     setContent: function(str)
19740     {
19741         this.html = str;
19742         if (this.contentEl) {
19743             this.contentEl.dom.innerHTML = str;
19744         }
19745         
19746     },
19747     // as it get's added to the bottom of the page.
19748     onRender : function(ct, position)
19749     {
19750         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19751         
19752         
19753         
19754         if(!this.el){
19755             var cfg = Roo.apply({},  this.getAutoCreate());
19756             cfg.id = Roo.id();
19757             
19758             if (this.cls) {
19759                 cfg.cls += ' ' + this.cls;
19760             }
19761             if (this.style) {
19762                 cfg.style = this.style;
19763             }
19764             //Roo.log("adding to ");
19765             this.el = Roo.get(document.body).createChild(cfg, position);
19766 //            Roo.log(this.el);
19767         }
19768         
19769         this.contentEl = this.el.select('.popover-content',true).first();
19770         this.headerEl =  this.el.select('.popover-title',true).first();
19771         
19772         var nitems = [];
19773         if(typeof(this.items) != 'undefined'){
19774             var items = this.items;
19775             delete this.items;
19776
19777             for(var i =0;i < items.length;i++) {
19778                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19779             }
19780         }
19781
19782         this.items = nitems;
19783         
19784         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19785         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19786         
19787         
19788         
19789         this.initEvents();
19790     },
19791     
19792     resizeMask : function()
19793     {
19794         this.maskEl.setSize(
19795             Roo.lib.Dom.getViewWidth(true),
19796             Roo.lib.Dom.getViewHeight(true)
19797         );
19798     },
19799     
19800     initEvents : function()
19801     {
19802         
19803         if (!this.modal) { 
19804             Roo.bootstrap.Popover.register(this);
19805         }
19806          
19807         this.arrowEl = this.el.select('.arrow',true).first();
19808         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19809         this.el.enableDisplayMode('block');
19810         this.el.hide();
19811  
19812         
19813         if (this.over === false && !this.parent()) {
19814             return; 
19815         }
19816         if (this.triggers === false) {
19817             return;
19818         }
19819          
19820         // support parent
19821         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19822         var triggers = this.trigger ? this.trigger.split(' ') : [];
19823         Roo.each(triggers, function(trigger) {
19824         
19825             if (trigger == 'click') {
19826                 on_el.on('click', this.toggle, this);
19827             } else if (trigger != 'manual') {
19828                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19829                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19830       
19831                 on_el.on(eventIn  ,this.enter, this);
19832                 on_el.on(eventOut, this.leave, this);
19833             }
19834         }, this);
19835     },
19836     
19837     
19838     // private
19839     timeout : null,
19840     hoverState : null,
19841     
19842     toggle : function () {
19843         this.hoverState == 'in' ? this.leave() : this.enter();
19844     },
19845     
19846     enter : function () {
19847         
19848         clearTimeout(this.timeout);
19849     
19850         this.hoverState = 'in';
19851     
19852         if (!this.delay || !this.delay.show) {
19853             this.show();
19854             return;
19855         }
19856         var _t = this;
19857         this.timeout = setTimeout(function () {
19858             if (_t.hoverState == 'in') {
19859                 _t.show();
19860             }
19861         }, this.delay.show)
19862     },
19863     
19864     leave : function() {
19865         clearTimeout(this.timeout);
19866     
19867         this.hoverState = 'out';
19868     
19869         if (!this.delay || !this.delay.hide) {
19870             this.hide();
19871             return;
19872         }
19873         var _t = this;
19874         this.timeout = setTimeout(function () {
19875             if (_t.hoverState == 'out') {
19876                 _t.hide();
19877             }
19878         }, this.delay.hide)
19879     },
19880     /**
19881      * Show the popover
19882      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19883      * @param {string} (left|right|top|bottom) position
19884      */
19885     show : function (on_el, placement)
19886     {
19887         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19888         on_el = on_el || false; // default to false
19889          
19890         if (!on_el) {
19891             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19892                 on_el = this.parent().el;
19893             } else if (this.over) {
19894                 Roo.get(this.over);
19895             }
19896             
19897         }
19898         
19899         this.alignEl = Roo.get( on_el );
19900
19901         if (!this.el) {
19902             this.render(document.body);
19903         }
19904         
19905         
19906          
19907         
19908         if (this.title === false) {
19909             this.headerEl.hide();
19910         }
19911         
19912        
19913         this.el.show();
19914         this.el.dom.style.display = 'block';
19915          
19916  
19917         if (this.alignEl) {
19918             this.updatePosition(this.placement, true);
19919              
19920         } else {
19921             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19922             var es = this.el.getSize();
19923             var x = Roo.lib.Dom.getViewWidth()/2;
19924             var y = Roo.lib.Dom.getViewHeight()/2;
19925             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19926             
19927         }
19928
19929         
19930         //var arrow = this.el.select('.arrow',true).first();
19931         //arrow.set(align[2], 
19932         
19933         this.el.addClass('in');
19934         
19935          
19936         
19937         this.hoverState = 'in';
19938         
19939         if (this.modal) {
19940             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19941             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19942             this.maskEl.dom.style.display = 'block';
19943             this.maskEl.addClass('show');
19944         }
19945         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19946  
19947         this.fireEvent('show', this);
19948         
19949     },
19950     /**
19951      * fire this manually after loading a grid in the table for example
19952      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19953      * @param {Boolean} try and move it if we cant get right position.
19954      */
19955     updatePosition : function(placement, try_move)
19956     {
19957         // allow for calling with no parameters
19958         placement = placement   ? placement :  this.placement;
19959         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19960         
19961         this.el.removeClass([
19962             'fade','top','bottom', 'left', 'right','in',
19963             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19964         ]);
19965         this.el.addClass(placement + ' bs-popover-' + placement);
19966         
19967         if (!this.alignEl ) {
19968             return false;
19969         }
19970         
19971         switch (placement) {
19972             case 'right':
19973                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19974                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19975                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19976                     //normal display... or moved up/down.
19977                     this.el.setXY(offset);
19978                     var xy = this.alignEl.getAnchorXY('tr', false);
19979                     xy[0]+=2;xy[1]+=5;
19980                     this.arrowEl.setXY(xy);
19981                     return true;
19982                 }
19983                 // continue through...
19984                 return this.updatePosition('left', false);
19985                 
19986             
19987             case 'left':
19988                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19989                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19990                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19991                     //normal display... or moved up/down.
19992                     this.el.setXY(offset);
19993                     var xy = this.alignEl.getAnchorXY('tl', false);
19994                     xy[0]-=10;xy[1]+=5; // << fix me
19995                     this.arrowEl.setXY(xy);
19996                     return true;
19997                 }
19998                 // call self...
19999                 return this.updatePosition('right', false);
20000             
20001             case 'top':
20002                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20003                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20004                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20005                     //normal display... or moved up/down.
20006                     this.el.setXY(offset);
20007                     var xy = this.alignEl.getAnchorXY('t', false);
20008                     xy[1]-=10; // << fix me
20009                     this.arrowEl.setXY(xy);
20010                     return true;
20011                 }
20012                 // fall through
20013                return this.updatePosition('bottom', false);
20014             
20015             case 'bottom':
20016                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20017                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20018                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20019                     //normal display... or moved up/down.
20020                     this.el.setXY(offset);
20021                     var xy = this.alignEl.getAnchorXY('b', false);
20022                      xy[1]+=2; // << fix me
20023                     this.arrowEl.setXY(xy);
20024                     return true;
20025                 }
20026                 // fall through
20027                 return this.updatePosition('top', false);
20028                 
20029             
20030         }
20031         
20032         
20033         return false;
20034     },
20035     
20036     hide : function()
20037     {
20038         this.el.setXY([0,0]);
20039         this.el.removeClass('in');
20040         this.el.hide();
20041         this.hoverState = null;
20042         this.maskEl.hide(); // always..
20043         this.fireEvent('hide', this);
20044     }
20045     
20046 });
20047
20048
20049 Roo.apply(Roo.bootstrap.Popover, {
20050
20051     alignment : {
20052         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20053         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20054         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20055         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20056     },
20057     
20058     zIndex : 20001,
20059
20060     clickHander : false,
20061     
20062
20063     onMouseDown : function(e)
20064     {
20065         if (!e.getTarget(".roo-popover")) {
20066             this.hideAll();
20067         }
20068          
20069     },
20070     
20071     popups : [],
20072     
20073     register : function(popup)
20074     {
20075         if (!Roo.bootstrap.Popover.clickHandler) {
20076             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20077         }
20078         // hide other popups.
20079         this.hideAll();
20080         this.popups.push(popup);
20081     },
20082     hideAll : function()
20083     {
20084         this.popups.forEach(function(p) {
20085             p.hide();
20086         });
20087     }
20088
20089 });/*
20090  * - LGPL
20091  *
20092  * Card header - holder for the card header elements.
20093  * 
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.PopoverNav
20098  * @extends Roo.bootstrap.NavGroup
20099  * Bootstrap Popover header navigation class
20100  * @constructor
20101  * Create a new Popover Header Navigation 
20102  * @param {Object} config The config object
20103  */
20104
20105 Roo.bootstrap.PopoverNav = function(config){
20106     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20107 };
20108
20109 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20110     
20111     
20112     container_method : 'getPopoverHeader' 
20113     
20114      
20115     
20116     
20117    
20118 });
20119
20120  
20121
20122  /*
20123  * - LGPL
20124  *
20125  * Progress
20126  * 
20127  */
20128
20129 /**
20130  * @class Roo.bootstrap.Progress
20131  * @extends Roo.bootstrap.Component
20132  * Bootstrap Progress class
20133  * @cfg {Boolean} striped striped of the progress bar
20134  * @cfg {Boolean} active animated of the progress bar
20135  * 
20136  * 
20137  * @constructor
20138  * Create a new Progress
20139  * @param {Object} config The config object
20140  */
20141
20142 Roo.bootstrap.Progress = function(config){
20143     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20144 };
20145
20146 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20147     
20148     striped : false,
20149     active: false,
20150     
20151     getAutoCreate : function(){
20152         var cfg = {
20153             tag: 'div',
20154             cls: 'progress'
20155         };
20156         
20157         
20158         if(this.striped){
20159             cfg.cls += ' progress-striped';
20160         }
20161       
20162         if(this.active){
20163             cfg.cls += ' active';
20164         }
20165         
20166         
20167         return cfg;
20168     }
20169    
20170 });
20171
20172  
20173
20174  /*
20175  * - LGPL
20176  *
20177  * ProgressBar
20178  * 
20179  */
20180
20181 /**
20182  * @class Roo.bootstrap.ProgressBar
20183  * @extends Roo.bootstrap.Component
20184  * Bootstrap ProgressBar class
20185  * @cfg {Number} aria_valuenow aria-value now
20186  * @cfg {Number} aria_valuemin aria-value min
20187  * @cfg {Number} aria_valuemax aria-value max
20188  * @cfg {String} label label for the progress bar
20189  * @cfg {String} panel (success | info | warning | danger )
20190  * @cfg {String} role role of the progress bar
20191  * @cfg {String} sr_only text
20192  * 
20193  * 
20194  * @constructor
20195  * Create a new ProgressBar
20196  * @param {Object} config The config object
20197  */
20198
20199 Roo.bootstrap.ProgressBar = function(config){
20200     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20201 };
20202
20203 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20204     
20205     aria_valuenow : 0,
20206     aria_valuemin : 0,
20207     aria_valuemax : 100,
20208     label : false,
20209     panel : false,
20210     role : false,
20211     sr_only: false,
20212     
20213     getAutoCreate : function()
20214     {
20215         
20216         var cfg = {
20217             tag: 'div',
20218             cls: 'progress-bar',
20219             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20220         };
20221         
20222         if(this.sr_only){
20223             cfg.cn = {
20224                 tag: 'span',
20225                 cls: 'sr-only',
20226                 html: this.sr_only
20227             }
20228         }
20229         
20230         if(this.role){
20231             cfg.role = this.role;
20232         }
20233         
20234         if(this.aria_valuenow){
20235             cfg['aria-valuenow'] = this.aria_valuenow;
20236         }
20237         
20238         if(this.aria_valuemin){
20239             cfg['aria-valuemin'] = this.aria_valuemin;
20240         }
20241         
20242         if(this.aria_valuemax){
20243             cfg['aria-valuemax'] = this.aria_valuemax;
20244         }
20245         
20246         if(this.label && !this.sr_only){
20247             cfg.html = this.label;
20248         }
20249         
20250         if(this.panel){
20251             cfg.cls += ' progress-bar-' + this.panel;
20252         }
20253         
20254         return cfg;
20255     },
20256     
20257     update : function(aria_valuenow)
20258     {
20259         this.aria_valuenow = aria_valuenow;
20260         
20261         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20262     }
20263    
20264 });
20265
20266  
20267
20268  /*
20269  * - LGPL
20270  *
20271  * column
20272  * 
20273  */
20274
20275 /**
20276  * @class Roo.bootstrap.TabGroup
20277  * @extends Roo.bootstrap.Column
20278  * Bootstrap Column class
20279  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20280  * @cfg {Boolean} carousel true to make the group behave like a carousel
20281  * @cfg {Boolean} bullets show bullets for the panels
20282  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20283  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20284  * @cfg {Boolean} showarrow (true|false) show arrow default true
20285  * 
20286  * @constructor
20287  * Create a new TabGroup
20288  * @param {Object} config The config object
20289  */
20290
20291 Roo.bootstrap.TabGroup = function(config){
20292     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20293     if (!this.navId) {
20294         this.navId = Roo.id();
20295     }
20296     this.tabs = [];
20297     Roo.bootstrap.TabGroup.register(this);
20298     
20299 };
20300
20301 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20302     
20303     carousel : false,
20304     transition : false,
20305     bullets : 0,
20306     timer : 0,
20307     autoslide : false,
20308     slideFn : false,
20309     slideOnTouch : false,
20310     showarrow : true,
20311     
20312     getAutoCreate : function()
20313     {
20314         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20315         
20316         cfg.cls += ' tab-content';
20317         
20318         if (this.carousel) {
20319             cfg.cls += ' carousel slide';
20320             
20321             cfg.cn = [{
20322                cls : 'carousel-inner',
20323                cn : []
20324             }];
20325         
20326             if(this.bullets  && !Roo.isTouch){
20327                 
20328                 var bullets = {
20329                     cls : 'carousel-bullets',
20330                     cn : []
20331                 };
20332                
20333                 if(this.bullets_cls){
20334                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20335                 }
20336                 
20337                 bullets.cn.push({
20338                     cls : 'clear'
20339                 });
20340                 
20341                 cfg.cn[0].cn.push(bullets);
20342             }
20343             
20344             if(this.showarrow){
20345                 cfg.cn[0].cn.push({
20346                     tag : 'div',
20347                     class : 'carousel-arrow',
20348                     cn : [
20349                         {
20350                             tag : 'div',
20351                             class : 'carousel-prev',
20352                             cn : [
20353                                 {
20354                                     tag : 'i',
20355                                     class : 'fa fa-chevron-left'
20356                                 }
20357                             ]
20358                         },
20359                         {
20360                             tag : 'div',
20361                             class : 'carousel-next',
20362                             cn : [
20363                                 {
20364                                     tag : 'i',
20365                                     class : 'fa fa-chevron-right'
20366                                 }
20367                             ]
20368                         }
20369                     ]
20370                 });
20371             }
20372             
20373         }
20374         
20375         return cfg;
20376     },
20377     
20378     initEvents:  function()
20379     {
20380 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20381 //            this.el.on("touchstart", this.onTouchStart, this);
20382 //        }
20383         
20384         if(this.autoslide){
20385             var _this = this;
20386             
20387             this.slideFn = window.setInterval(function() {
20388                 _this.showPanelNext();
20389             }, this.timer);
20390         }
20391         
20392         if(this.showarrow){
20393             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20394             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20395         }
20396         
20397         
20398     },
20399     
20400 //    onTouchStart : function(e, el, o)
20401 //    {
20402 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20403 //            return;
20404 //        }
20405 //        
20406 //        this.showPanelNext();
20407 //    },
20408     
20409     
20410     getChildContainer : function()
20411     {
20412         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20413     },
20414     
20415     /**
20416     * register a Navigation item
20417     * @param {Roo.bootstrap.NavItem} the navitem to add
20418     */
20419     register : function(item)
20420     {
20421         this.tabs.push( item);
20422         item.navId = this.navId; // not really needed..
20423         this.addBullet();
20424     
20425     },
20426     
20427     getActivePanel : function()
20428     {
20429         var r = false;
20430         Roo.each(this.tabs, function(t) {
20431             if (t.active) {
20432                 r = t;
20433                 return false;
20434             }
20435             return null;
20436         });
20437         return r;
20438         
20439     },
20440     getPanelByName : function(n)
20441     {
20442         var r = false;
20443         Roo.each(this.tabs, function(t) {
20444             if (t.tabId == n) {
20445                 r = t;
20446                 return false;
20447             }
20448             return null;
20449         });
20450         return r;
20451     },
20452     indexOfPanel : function(p)
20453     {
20454         var r = false;
20455         Roo.each(this.tabs, function(t,i) {
20456             if (t.tabId == p.tabId) {
20457                 r = i;
20458                 return false;
20459             }
20460             return null;
20461         });
20462         return r;
20463     },
20464     /**
20465      * show a specific panel
20466      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20467      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20468      */
20469     showPanel : function (pan)
20470     {
20471         if(this.transition || typeof(pan) == 'undefined'){
20472             Roo.log("waiting for the transitionend");
20473             return false;
20474         }
20475         
20476         if (typeof(pan) == 'number') {
20477             pan = this.tabs[pan];
20478         }
20479         
20480         if (typeof(pan) == 'string') {
20481             pan = this.getPanelByName(pan);
20482         }
20483         
20484         var cur = this.getActivePanel();
20485         
20486         if(!pan || !cur){
20487             Roo.log('pan or acitve pan is undefined');
20488             return false;
20489         }
20490         
20491         if (pan.tabId == this.getActivePanel().tabId) {
20492             return true;
20493         }
20494         
20495         if (false === cur.fireEvent('beforedeactivate')) {
20496             return false;
20497         }
20498         
20499         if(this.bullets > 0 && !Roo.isTouch){
20500             this.setActiveBullet(this.indexOfPanel(pan));
20501         }
20502         
20503         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20504             
20505             //class="carousel-item carousel-item-next carousel-item-left"
20506             
20507             this.transition = true;
20508             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20509             var lr = dir == 'next' ? 'left' : 'right';
20510             pan.el.addClass(dir); // or prev
20511             pan.el.addClass('carousel-item-' + dir); // or prev
20512             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20513             cur.el.addClass(lr); // or right
20514             pan.el.addClass(lr);
20515             cur.el.addClass('carousel-item-' +lr); // or right
20516             pan.el.addClass('carousel-item-' +lr);
20517             
20518             
20519             var _this = this;
20520             cur.el.on('transitionend', function() {
20521                 Roo.log("trans end?");
20522                 
20523                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20524                 pan.setActive(true);
20525                 
20526                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20527                 cur.setActive(false);
20528                 
20529                 _this.transition = false;
20530                 
20531             }, this, { single:  true } );
20532             
20533             return true;
20534         }
20535         
20536         cur.setActive(false);
20537         pan.setActive(true);
20538         
20539         return true;
20540         
20541     },
20542     showPanelNext : function()
20543     {
20544         var i = this.indexOfPanel(this.getActivePanel());
20545         
20546         if (i >= this.tabs.length - 1 && !this.autoslide) {
20547             return;
20548         }
20549         
20550         if (i >= this.tabs.length - 1 && this.autoslide) {
20551             i = -1;
20552         }
20553         
20554         this.showPanel(this.tabs[i+1]);
20555     },
20556     
20557     showPanelPrev : function()
20558     {
20559         var i = this.indexOfPanel(this.getActivePanel());
20560         
20561         if (i  < 1 && !this.autoslide) {
20562             return;
20563         }
20564         
20565         if (i < 1 && this.autoslide) {
20566             i = this.tabs.length;
20567         }
20568         
20569         this.showPanel(this.tabs[i-1]);
20570     },
20571     
20572     
20573     addBullet: function()
20574     {
20575         if(!this.bullets || Roo.isTouch){
20576             return;
20577         }
20578         var ctr = this.el.select('.carousel-bullets',true).first();
20579         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20580         var bullet = ctr.createChild({
20581             cls : 'bullet bullet-' + i
20582         },ctr.dom.lastChild);
20583         
20584         
20585         var _this = this;
20586         
20587         bullet.on('click', (function(e, el, o, ii, t){
20588
20589             e.preventDefault();
20590
20591             this.showPanel(ii);
20592
20593             if(this.autoslide && this.slideFn){
20594                 clearInterval(this.slideFn);
20595                 this.slideFn = window.setInterval(function() {
20596                     _this.showPanelNext();
20597                 }, this.timer);
20598             }
20599
20600         }).createDelegate(this, [i, bullet], true));
20601                 
20602         
20603     },
20604      
20605     setActiveBullet : function(i)
20606     {
20607         if(Roo.isTouch){
20608             return;
20609         }
20610         
20611         Roo.each(this.el.select('.bullet', true).elements, function(el){
20612             el.removeClass('selected');
20613         });
20614
20615         var bullet = this.el.select('.bullet-' + i, true).first();
20616         
20617         if(!bullet){
20618             return;
20619         }
20620         
20621         bullet.addClass('selected');
20622     }
20623     
20624     
20625   
20626 });
20627
20628  
20629
20630  
20631  
20632 Roo.apply(Roo.bootstrap.TabGroup, {
20633     
20634     groups: {},
20635      /**
20636     * register a Navigation Group
20637     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20638     */
20639     register : function(navgrp)
20640     {
20641         this.groups[navgrp.navId] = navgrp;
20642         
20643     },
20644     /**
20645     * fetch a Navigation Group based on the navigation ID
20646     * if one does not exist , it will get created.
20647     * @param {string} the navgroup to add
20648     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20649     */
20650     get: function(navId) {
20651         if (typeof(this.groups[navId]) == 'undefined') {
20652             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20653         }
20654         return this.groups[navId] ;
20655     }
20656     
20657     
20658     
20659 });
20660
20661  /*
20662  * - LGPL
20663  *
20664  * TabPanel
20665  * 
20666  */
20667
20668 /**
20669  * @class Roo.bootstrap.TabPanel
20670  * @extends Roo.bootstrap.Component
20671  * Bootstrap TabPanel class
20672  * @cfg {Boolean} active panel active
20673  * @cfg {String} html panel content
20674  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20675  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20676  * @cfg {String} href click to link..
20677  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20678  * 
20679  * 
20680  * @constructor
20681  * Create a new TabPanel
20682  * @param {Object} config The config object
20683  */
20684
20685 Roo.bootstrap.TabPanel = function(config){
20686     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20687     this.addEvents({
20688         /**
20689              * @event changed
20690              * Fires when the active status changes
20691              * @param {Roo.bootstrap.TabPanel} this
20692              * @param {Boolean} state the new state
20693             
20694          */
20695         'changed': true,
20696         /**
20697              * @event beforedeactivate
20698              * Fires before a tab is de-activated - can be used to do validation on a form.
20699              * @param {Roo.bootstrap.TabPanel} this
20700              * @return {Boolean} false if there is an error
20701             
20702          */
20703         'beforedeactivate': true
20704      });
20705     
20706     this.tabId = this.tabId || Roo.id();
20707   
20708 };
20709
20710 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20711     
20712     active: false,
20713     html: false,
20714     tabId: false,
20715     navId : false,
20716     href : '',
20717     touchSlide : false,
20718     getAutoCreate : function(){
20719         
20720         
20721         var cfg = {
20722             tag: 'div',
20723             // item is needed for carousel - not sure if it has any effect otherwise
20724             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20725             html: this.html || ''
20726         };
20727         
20728         if(this.active){
20729             cfg.cls += ' active';
20730         }
20731         
20732         if(this.tabId){
20733             cfg.tabId = this.tabId;
20734         }
20735         
20736         
20737         
20738         return cfg;
20739     },
20740     
20741     initEvents:  function()
20742     {
20743         var p = this.parent();
20744         
20745         this.navId = this.navId || p.navId;
20746         
20747         if (typeof(this.navId) != 'undefined') {
20748             // not really needed.. but just in case.. parent should be a NavGroup.
20749             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20750             
20751             tg.register(this);
20752             
20753             var i = tg.tabs.length - 1;
20754             
20755             if(this.active && tg.bullets > 0 && i < tg.bullets){
20756                 tg.setActiveBullet(i);
20757             }
20758         }
20759         
20760         this.el.on('click', this.onClick, this);
20761         
20762         if(Roo.isTouch && this.touchSlide){
20763             this.el.on("touchstart", this.onTouchStart, this);
20764             this.el.on("touchmove", this.onTouchMove, this);
20765             this.el.on("touchend", this.onTouchEnd, this);
20766         }
20767         
20768     },
20769     
20770     onRender : function(ct, position)
20771     {
20772         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20773     },
20774     
20775     setActive : function(state)
20776     {
20777         Roo.log("panel - set active " + this.tabId + "=" + state);
20778         
20779         this.active = state;
20780         if (!state) {
20781             this.el.removeClass('active');
20782             
20783         } else  if (!this.el.hasClass('active')) {
20784             this.el.addClass('active');
20785         }
20786         
20787         this.fireEvent('changed', this, state);
20788     },
20789     
20790     onClick : function(e)
20791     {
20792         e.preventDefault();
20793         
20794         if(!this.href.length){
20795             return;
20796         }
20797         
20798         window.location.href = this.href;
20799     },
20800     
20801     startX : 0,
20802     startY : 0,
20803     endX : 0,
20804     endY : 0,
20805     swiping : false,
20806     
20807     onTouchStart : function(e)
20808     {
20809         this.swiping = false;
20810         
20811         this.startX = e.browserEvent.touches[0].clientX;
20812         this.startY = e.browserEvent.touches[0].clientY;
20813     },
20814     
20815     onTouchMove : function(e)
20816     {
20817         this.swiping = true;
20818         
20819         this.endX = e.browserEvent.touches[0].clientX;
20820         this.endY = e.browserEvent.touches[0].clientY;
20821     },
20822     
20823     onTouchEnd : function(e)
20824     {
20825         if(!this.swiping){
20826             this.onClick(e);
20827             return;
20828         }
20829         
20830         var tabGroup = this.parent();
20831         
20832         if(this.endX > this.startX){ // swiping right
20833             tabGroup.showPanelPrev();
20834             return;
20835         }
20836         
20837         if(this.startX > this.endX){ // swiping left
20838             tabGroup.showPanelNext();
20839             return;
20840         }
20841     }
20842     
20843     
20844 });
20845  
20846
20847  
20848
20849  /*
20850  * - LGPL
20851  *
20852  * DateField
20853  * 
20854  */
20855
20856 /**
20857  * @class Roo.bootstrap.DateField
20858  * @extends Roo.bootstrap.Input
20859  * Bootstrap DateField class
20860  * @cfg {Number} weekStart default 0
20861  * @cfg {String} viewMode default empty, (months|years)
20862  * @cfg {String} minViewMode default empty, (months|years)
20863  * @cfg {Number} startDate default -Infinity
20864  * @cfg {Number} endDate default Infinity
20865  * @cfg {Boolean} todayHighlight default false
20866  * @cfg {Boolean} todayBtn default false
20867  * @cfg {Boolean} calendarWeeks default false
20868  * @cfg {Object} daysOfWeekDisabled default empty
20869  * @cfg {Boolean} singleMode default false (true | false)
20870  * 
20871  * @cfg {Boolean} keyboardNavigation default true
20872  * @cfg {String} language default en
20873  * 
20874  * @constructor
20875  * Create a new DateField
20876  * @param {Object} config The config object
20877  */
20878
20879 Roo.bootstrap.DateField = function(config){
20880     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20881      this.addEvents({
20882             /**
20883              * @event show
20884              * Fires when this field show.
20885              * @param {Roo.bootstrap.DateField} this
20886              * @param {Mixed} date The date value
20887              */
20888             show : true,
20889             /**
20890              * @event show
20891              * Fires when this field hide.
20892              * @param {Roo.bootstrap.DateField} this
20893              * @param {Mixed} date The date value
20894              */
20895             hide : true,
20896             /**
20897              * @event select
20898              * Fires when select a date.
20899              * @param {Roo.bootstrap.DateField} this
20900              * @param {Mixed} date The date value
20901              */
20902             select : true,
20903             /**
20904              * @event beforeselect
20905              * Fires when before select a date.
20906              * @param {Roo.bootstrap.DateField} this
20907              * @param {Mixed} date The date value
20908              */
20909             beforeselect : true
20910         });
20911 };
20912
20913 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20914     
20915     /**
20916      * @cfg {String} format
20917      * The default date format string which can be overriden for localization support.  The format must be
20918      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20919      */
20920     format : "m/d/y",
20921     /**
20922      * @cfg {String} altFormats
20923      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20924      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20925      */
20926     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20927     
20928     weekStart : 0,
20929     
20930     viewMode : '',
20931     
20932     minViewMode : '',
20933     
20934     todayHighlight : false,
20935     
20936     todayBtn: false,
20937     
20938     language: 'en',
20939     
20940     keyboardNavigation: true,
20941     
20942     calendarWeeks: false,
20943     
20944     startDate: -Infinity,
20945     
20946     endDate: Infinity,
20947     
20948     daysOfWeekDisabled: [],
20949     
20950     _events: [],
20951     
20952     singleMode : false,
20953     
20954     UTCDate: function()
20955     {
20956         return new Date(Date.UTC.apply(Date, arguments));
20957     },
20958     
20959     UTCToday: function()
20960     {
20961         var today = new Date();
20962         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20963     },
20964     
20965     getDate: function() {
20966             var d = this.getUTCDate();
20967             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20968     },
20969     
20970     getUTCDate: function() {
20971             return this.date;
20972     },
20973     
20974     setDate: function(d) {
20975             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20976     },
20977     
20978     setUTCDate: function(d) {
20979             this.date = d;
20980             this.setValue(this.formatDate(this.date));
20981     },
20982         
20983     onRender: function(ct, position)
20984     {
20985         
20986         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20987         
20988         this.language = this.language || 'en';
20989         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20990         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20991         
20992         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20993         this.format = this.format || 'm/d/y';
20994         this.isInline = false;
20995         this.isInput = true;
20996         this.component = this.el.select('.add-on', true).first() || false;
20997         this.component = (this.component && this.component.length === 0) ? false : this.component;
20998         this.hasInput = this.component && this.inputEl().length;
20999         
21000         if (typeof(this.minViewMode === 'string')) {
21001             switch (this.minViewMode) {
21002                 case 'months':
21003                     this.minViewMode = 1;
21004                     break;
21005                 case 'years':
21006                     this.minViewMode = 2;
21007                     break;
21008                 default:
21009                     this.minViewMode = 0;
21010                     break;
21011             }
21012         }
21013         
21014         if (typeof(this.viewMode === 'string')) {
21015             switch (this.viewMode) {
21016                 case 'months':
21017                     this.viewMode = 1;
21018                     break;
21019                 case 'years':
21020                     this.viewMode = 2;
21021                     break;
21022                 default:
21023                     this.viewMode = 0;
21024                     break;
21025             }
21026         }
21027                 
21028         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21029         
21030 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21031         
21032         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21033         
21034         this.picker().on('mousedown', this.onMousedown, this);
21035         this.picker().on('click', this.onClick, this);
21036         
21037         this.picker().addClass('datepicker-dropdown');
21038         
21039         this.startViewMode = this.viewMode;
21040         
21041         if(this.singleMode){
21042             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21043                 v.setVisibilityMode(Roo.Element.DISPLAY);
21044                 v.hide();
21045             });
21046             
21047             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21048                 v.setStyle('width', '189px');
21049             });
21050         }
21051         
21052         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21053             if(!this.calendarWeeks){
21054                 v.remove();
21055                 return;
21056             }
21057             
21058             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21059             v.attr('colspan', function(i, val){
21060                 return parseInt(val) + 1;
21061             });
21062         });
21063                         
21064         
21065         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21066         
21067         this.setStartDate(this.startDate);
21068         this.setEndDate(this.endDate);
21069         
21070         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21071         
21072         this.fillDow();
21073         this.fillMonths();
21074         this.update();
21075         this.showMode();
21076         
21077         if(this.isInline) {
21078             this.showPopup();
21079         }
21080     },
21081     
21082     picker : function()
21083     {
21084         return this.pickerEl;
21085 //        return this.el.select('.datepicker', true).first();
21086     },
21087     
21088     fillDow: function()
21089     {
21090         var dowCnt = this.weekStart;
21091         
21092         var dow = {
21093             tag: 'tr',
21094             cn: [
21095                 
21096             ]
21097         };
21098         
21099         if(this.calendarWeeks){
21100             dow.cn.push({
21101                 tag: 'th',
21102                 cls: 'cw',
21103                 html: '&nbsp;'
21104             })
21105         }
21106         
21107         while (dowCnt < this.weekStart + 7) {
21108             dow.cn.push({
21109                 tag: 'th',
21110                 cls: 'dow',
21111                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21112             });
21113         }
21114         
21115         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21116     },
21117     
21118     fillMonths: function()
21119     {    
21120         var i = 0;
21121         var months = this.picker().select('>.datepicker-months td', true).first();
21122         
21123         months.dom.innerHTML = '';
21124         
21125         while (i < 12) {
21126             var month = {
21127                 tag: 'span',
21128                 cls: 'month',
21129                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21130             };
21131             
21132             months.createChild(month);
21133         }
21134         
21135     },
21136     
21137     update: function()
21138     {
21139         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;
21140         
21141         if (this.date < this.startDate) {
21142             this.viewDate = new Date(this.startDate);
21143         } else if (this.date > this.endDate) {
21144             this.viewDate = new Date(this.endDate);
21145         } else {
21146             this.viewDate = new Date(this.date);
21147         }
21148         
21149         this.fill();
21150     },
21151     
21152     fill: function() 
21153     {
21154         var d = new Date(this.viewDate),
21155                 year = d.getUTCFullYear(),
21156                 month = d.getUTCMonth(),
21157                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21158                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21159                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21160                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21161                 currentDate = this.date && this.date.valueOf(),
21162                 today = this.UTCToday();
21163         
21164         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21165         
21166 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21167         
21168 //        this.picker.select('>tfoot th.today').
21169 //                                              .text(dates[this.language].today)
21170 //                                              .toggle(this.todayBtn !== false);
21171     
21172         this.updateNavArrows();
21173         this.fillMonths();
21174                                                 
21175         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21176         
21177         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21178          
21179         prevMonth.setUTCDate(day);
21180         
21181         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21182         
21183         var nextMonth = new Date(prevMonth);
21184         
21185         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21186         
21187         nextMonth = nextMonth.valueOf();
21188         
21189         var fillMonths = false;
21190         
21191         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21192         
21193         while(prevMonth.valueOf() <= nextMonth) {
21194             var clsName = '';
21195             
21196             if (prevMonth.getUTCDay() === this.weekStart) {
21197                 if(fillMonths){
21198                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21199                 }
21200                     
21201                 fillMonths = {
21202                     tag: 'tr',
21203                     cn: []
21204                 };
21205                 
21206                 if(this.calendarWeeks){
21207                     // ISO 8601: First week contains first thursday.
21208                     // ISO also states week starts on Monday, but we can be more abstract here.
21209                     var
21210                     // Start of current week: based on weekstart/current date
21211                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21212                     // Thursday of this week
21213                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21214                     // First Thursday of year, year from thursday
21215                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21216                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21217                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21218                     
21219                     fillMonths.cn.push({
21220                         tag: 'td',
21221                         cls: 'cw',
21222                         html: calWeek
21223                     });
21224                 }
21225             }
21226             
21227             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21228                 clsName += ' old';
21229             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21230                 clsName += ' new';
21231             }
21232             if (this.todayHighlight &&
21233                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21234                 prevMonth.getUTCMonth() == today.getMonth() &&
21235                 prevMonth.getUTCDate() == today.getDate()) {
21236                 clsName += ' today';
21237             }
21238             
21239             if (currentDate && prevMonth.valueOf() === currentDate) {
21240                 clsName += ' active';
21241             }
21242             
21243             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21244                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21245                     clsName += ' disabled';
21246             }
21247             
21248             fillMonths.cn.push({
21249                 tag: 'td',
21250                 cls: 'day ' + clsName,
21251                 html: prevMonth.getDate()
21252             });
21253             
21254             prevMonth.setDate(prevMonth.getDate()+1);
21255         }
21256           
21257         var currentYear = this.date && this.date.getUTCFullYear();
21258         var currentMonth = this.date && this.date.getUTCMonth();
21259         
21260         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21261         
21262         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21263             v.removeClass('active');
21264             
21265             if(currentYear === year && k === currentMonth){
21266                 v.addClass('active');
21267             }
21268             
21269             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21270                 v.addClass('disabled');
21271             }
21272             
21273         });
21274         
21275         
21276         year = parseInt(year/10, 10) * 10;
21277         
21278         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21279         
21280         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21281         
21282         year -= 1;
21283         for (var i = -1; i < 11; i++) {
21284             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21285                 tag: 'span',
21286                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21287                 html: year
21288             });
21289             
21290             year += 1;
21291         }
21292     },
21293     
21294     showMode: function(dir) 
21295     {
21296         if (dir) {
21297             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21298         }
21299         
21300         Roo.each(this.picker().select('>div',true).elements, function(v){
21301             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21302             v.hide();
21303         });
21304         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21305     },
21306     
21307     place: function()
21308     {
21309         if(this.isInline) {
21310             return;
21311         }
21312         
21313         this.picker().removeClass(['bottom', 'top']);
21314         
21315         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21316             /*
21317              * place to the top of element!
21318              *
21319              */
21320             
21321             this.picker().addClass('top');
21322             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21323             
21324             return;
21325         }
21326         
21327         this.picker().addClass('bottom');
21328         
21329         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21330     },
21331     
21332     parseDate : function(value)
21333     {
21334         if(!value || value instanceof Date){
21335             return value;
21336         }
21337         var v = Date.parseDate(value, this.format);
21338         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21339             v = Date.parseDate(value, 'Y-m-d');
21340         }
21341         if(!v && this.altFormats){
21342             if(!this.altFormatsArray){
21343                 this.altFormatsArray = this.altFormats.split("|");
21344             }
21345             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21346                 v = Date.parseDate(value, this.altFormatsArray[i]);
21347             }
21348         }
21349         return v;
21350     },
21351     
21352     formatDate : function(date, fmt)
21353     {   
21354         return (!date || !(date instanceof Date)) ?
21355         date : date.dateFormat(fmt || this.format);
21356     },
21357     
21358     onFocus : function()
21359     {
21360         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21361         this.showPopup();
21362     },
21363     
21364     onBlur : function()
21365     {
21366         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21367         
21368         var d = this.inputEl().getValue();
21369         
21370         this.setValue(d);
21371                 
21372         this.hidePopup();
21373     },
21374     
21375     showPopup : function()
21376     {
21377         this.picker().show();
21378         this.update();
21379         this.place();
21380         
21381         this.fireEvent('showpopup', this, this.date);
21382     },
21383     
21384     hidePopup : function()
21385     {
21386         if(this.isInline) {
21387             return;
21388         }
21389         this.picker().hide();
21390         this.viewMode = this.startViewMode;
21391         this.showMode();
21392         
21393         this.fireEvent('hidepopup', this, this.date);
21394         
21395     },
21396     
21397     onMousedown: function(e)
21398     {
21399         e.stopPropagation();
21400         e.preventDefault();
21401     },
21402     
21403     keyup: function(e)
21404     {
21405         Roo.bootstrap.DateField.superclass.keyup.call(this);
21406         this.update();
21407     },
21408
21409     setValue: function(v)
21410     {
21411         if(this.fireEvent('beforeselect', this, v) !== false){
21412             var d = new Date(this.parseDate(v) ).clearTime();
21413         
21414             if(isNaN(d.getTime())){
21415                 this.date = this.viewDate = '';
21416                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21417                 return;
21418             }
21419
21420             v = this.formatDate(d);
21421
21422             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21423
21424             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21425
21426             this.update();
21427
21428             this.fireEvent('select', this, this.date);
21429         }
21430     },
21431     
21432     getValue: function()
21433     {
21434         return this.formatDate(this.date);
21435     },
21436     
21437     fireKey: function(e)
21438     {
21439         if (!this.picker().isVisible()){
21440             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21441                 this.showPopup();
21442             }
21443             return;
21444         }
21445         
21446         var dateChanged = false,
21447         dir, day, month,
21448         newDate, newViewDate;
21449         
21450         switch(e.keyCode){
21451             case 27: // escape
21452                 this.hidePopup();
21453                 e.preventDefault();
21454                 break;
21455             case 37: // left
21456             case 39: // right
21457                 if (!this.keyboardNavigation) {
21458                     break;
21459                 }
21460                 dir = e.keyCode == 37 ? -1 : 1;
21461                 
21462                 if (e.ctrlKey){
21463                     newDate = this.moveYear(this.date, dir);
21464                     newViewDate = this.moveYear(this.viewDate, dir);
21465                 } else if (e.shiftKey){
21466                     newDate = this.moveMonth(this.date, dir);
21467                     newViewDate = this.moveMonth(this.viewDate, dir);
21468                 } else {
21469                     newDate = new Date(this.date);
21470                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21471                     newViewDate = new Date(this.viewDate);
21472                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21473                 }
21474                 if (this.dateWithinRange(newDate)){
21475                     this.date = newDate;
21476                     this.viewDate = newViewDate;
21477                     this.setValue(this.formatDate(this.date));
21478 //                    this.update();
21479                     e.preventDefault();
21480                     dateChanged = true;
21481                 }
21482                 break;
21483             case 38: // up
21484             case 40: // down
21485                 if (!this.keyboardNavigation) {
21486                     break;
21487                 }
21488                 dir = e.keyCode == 38 ? -1 : 1;
21489                 if (e.ctrlKey){
21490                     newDate = this.moveYear(this.date, dir);
21491                     newViewDate = this.moveYear(this.viewDate, dir);
21492                 } else if (e.shiftKey){
21493                     newDate = this.moveMonth(this.date, dir);
21494                     newViewDate = this.moveMonth(this.viewDate, dir);
21495                 } else {
21496                     newDate = new Date(this.date);
21497                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21498                     newViewDate = new Date(this.viewDate);
21499                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21500                 }
21501                 if (this.dateWithinRange(newDate)){
21502                     this.date = newDate;
21503                     this.viewDate = newViewDate;
21504                     this.setValue(this.formatDate(this.date));
21505 //                    this.update();
21506                     e.preventDefault();
21507                     dateChanged = true;
21508                 }
21509                 break;
21510             case 13: // enter
21511                 this.setValue(this.formatDate(this.date));
21512                 this.hidePopup();
21513                 e.preventDefault();
21514                 break;
21515             case 9: // tab
21516                 this.setValue(this.formatDate(this.date));
21517                 this.hidePopup();
21518                 break;
21519             case 16: // shift
21520             case 17: // ctrl
21521             case 18: // alt
21522                 break;
21523             default :
21524                 this.hidePopup();
21525                 
21526         }
21527     },
21528     
21529     
21530     onClick: function(e) 
21531     {
21532         e.stopPropagation();
21533         e.preventDefault();
21534         
21535         var target = e.getTarget();
21536         
21537         if(target.nodeName.toLowerCase() === 'i'){
21538             target = Roo.get(target).dom.parentNode;
21539         }
21540         
21541         var nodeName = target.nodeName;
21542         var className = target.className;
21543         var html = target.innerHTML;
21544         //Roo.log(nodeName);
21545         
21546         switch(nodeName.toLowerCase()) {
21547             case 'th':
21548                 switch(className) {
21549                     case 'switch':
21550                         this.showMode(1);
21551                         break;
21552                     case 'prev':
21553                     case 'next':
21554                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21555                         switch(this.viewMode){
21556                                 case 0:
21557                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21558                                         break;
21559                                 case 1:
21560                                 case 2:
21561                                         this.viewDate = this.moveYear(this.viewDate, dir);
21562                                         break;
21563                         }
21564                         this.fill();
21565                         break;
21566                     case 'today':
21567                         var date = new Date();
21568                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21569 //                        this.fill()
21570                         this.setValue(this.formatDate(this.date));
21571                         
21572                         this.hidePopup();
21573                         break;
21574                 }
21575                 break;
21576             case 'span':
21577                 if (className.indexOf('disabled') < 0) {
21578                     this.viewDate.setUTCDate(1);
21579                     if (className.indexOf('month') > -1) {
21580                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21581                     } else {
21582                         var year = parseInt(html, 10) || 0;
21583                         this.viewDate.setUTCFullYear(year);
21584                         
21585                     }
21586                     
21587                     if(this.singleMode){
21588                         this.setValue(this.formatDate(this.viewDate));
21589                         this.hidePopup();
21590                         return;
21591                     }
21592                     
21593                     this.showMode(-1);
21594                     this.fill();
21595                 }
21596                 break;
21597                 
21598             case 'td':
21599                 //Roo.log(className);
21600                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21601                     var day = parseInt(html, 10) || 1;
21602                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21603                         month = (this.viewDate || new Date()).getUTCMonth();
21604
21605                     if (className.indexOf('old') > -1) {
21606                         if(month === 0 ){
21607                             month = 11;
21608                             year -= 1;
21609                         }else{
21610                             month -= 1;
21611                         }
21612                     } else if (className.indexOf('new') > -1) {
21613                         if (month == 11) {
21614                             month = 0;
21615                             year += 1;
21616                         } else {
21617                             month += 1;
21618                         }
21619                     }
21620                     //Roo.log([year,month,day]);
21621                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21622                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21623 //                    this.fill();
21624                     //Roo.log(this.formatDate(this.date));
21625                     this.setValue(this.formatDate(this.date));
21626                     this.hidePopup();
21627                 }
21628                 break;
21629         }
21630     },
21631     
21632     setStartDate: function(startDate)
21633     {
21634         this.startDate = startDate || -Infinity;
21635         if (this.startDate !== -Infinity) {
21636             this.startDate = this.parseDate(this.startDate);
21637         }
21638         this.update();
21639         this.updateNavArrows();
21640     },
21641
21642     setEndDate: function(endDate)
21643     {
21644         this.endDate = endDate || Infinity;
21645         if (this.endDate !== Infinity) {
21646             this.endDate = this.parseDate(this.endDate);
21647         }
21648         this.update();
21649         this.updateNavArrows();
21650     },
21651     
21652     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21653     {
21654         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21655         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21656             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21657         }
21658         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21659             return parseInt(d, 10);
21660         });
21661         this.update();
21662         this.updateNavArrows();
21663     },
21664     
21665     updateNavArrows: function() 
21666     {
21667         if(this.singleMode){
21668             return;
21669         }
21670         
21671         var d = new Date(this.viewDate),
21672         year = d.getUTCFullYear(),
21673         month = d.getUTCMonth();
21674         
21675         Roo.each(this.picker().select('.prev', true).elements, function(v){
21676             v.show();
21677             switch (this.viewMode) {
21678                 case 0:
21679
21680                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21681                         v.hide();
21682                     }
21683                     break;
21684                 case 1:
21685                 case 2:
21686                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21687                         v.hide();
21688                     }
21689                     break;
21690             }
21691         });
21692         
21693         Roo.each(this.picker().select('.next', true).elements, function(v){
21694             v.show();
21695             switch (this.viewMode) {
21696                 case 0:
21697
21698                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21699                         v.hide();
21700                     }
21701                     break;
21702                 case 1:
21703                 case 2:
21704                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21705                         v.hide();
21706                     }
21707                     break;
21708             }
21709         })
21710     },
21711     
21712     moveMonth: function(date, dir)
21713     {
21714         if (!dir) {
21715             return date;
21716         }
21717         var new_date = new Date(date.valueOf()),
21718         day = new_date.getUTCDate(),
21719         month = new_date.getUTCMonth(),
21720         mag = Math.abs(dir),
21721         new_month, test;
21722         dir = dir > 0 ? 1 : -1;
21723         if (mag == 1){
21724             test = dir == -1
21725             // If going back one month, make sure month is not current month
21726             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21727             ? function(){
21728                 return new_date.getUTCMonth() == month;
21729             }
21730             // If going forward one month, make sure month is as expected
21731             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21732             : function(){
21733                 return new_date.getUTCMonth() != new_month;
21734             };
21735             new_month = month + dir;
21736             new_date.setUTCMonth(new_month);
21737             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21738             if (new_month < 0 || new_month > 11) {
21739                 new_month = (new_month + 12) % 12;
21740             }
21741         } else {
21742             // For magnitudes >1, move one month at a time...
21743             for (var i=0; i<mag; i++) {
21744                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21745                 new_date = this.moveMonth(new_date, dir);
21746             }
21747             // ...then reset the day, keeping it in the new month
21748             new_month = new_date.getUTCMonth();
21749             new_date.setUTCDate(day);
21750             test = function(){
21751                 return new_month != new_date.getUTCMonth();
21752             };
21753         }
21754         // Common date-resetting loop -- if date is beyond end of month, make it
21755         // end of month
21756         while (test()){
21757             new_date.setUTCDate(--day);
21758             new_date.setUTCMonth(new_month);
21759         }
21760         return new_date;
21761     },
21762
21763     moveYear: function(date, dir)
21764     {
21765         return this.moveMonth(date, dir*12);
21766     },
21767
21768     dateWithinRange: function(date)
21769     {
21770         return date >= this.startDate && date <= this.endDate;
21771     },
21772
21773     
21774     remove: function() 
21775     {
21776         this.picker().remove();
21777     },
21778     
21779     validateValue : function(value)
21780     {
21781         if(this.getVisibilityEl().hasClass('hidden')){
21782             return true;
21783         }
21784         
21785         if(value.length < 1)  {
21786             if(this.allowBlank){
21787                 return true;
21788             }
21789             return false;
21790         }
21791         
21792         if(value.length < this.minLength){
21793             return false;
21794         }
21795         if(value.length > this.maxLength){
21796             return false;
21797         }
21798         if(this.vtype){
21799             var vt = Roo.form.VTypes;
21800             if(!vt[this.vtype](value, this)){
21801                 return false;
21802             }
21803         }
21804         if(typeof this.validator == "function"){
21805             var msg = this.validator(value);
21806             if(msg !== true){
21807                 return false;
21808             }
21809         }
21810         
21811         if(this.regex && !this.regex.test(value)){
21812             return false;
21813         }
21814         
21815         if(typeof(this.parseDate(value)) == 'undefined'){
21816             return false;
21817         }
21818         
21819         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21820             return false;
21821         }      
21822         
21823         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21824             return false;
21825         } 
21826         
21827         
21828         return true;
21829     },
21830     
21831     reset : function()
21832     {
21833         this.date = this.viewDate = '';
21834         
21835         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21836     }
21837    
21838 });
21839
21840 Roo.apply(Roo.bootstrap.DateField,  {
21841     
21842     head : {
21843         tag: 'thead',
21844         cn: [
21845         {
21846             tag: 'tr',
21847             cn: [
21848             {
21849                 tag: 'th',
21850                 cls: 'prev',
21851                 html: '<i class="fa fa-arrow-left"/>'
21852             },
21853             {
21854                 tag: 'th',
21855                 cls: 'switch',
21856                 colspan: '5'
21857             },
21858             {
21859                 tag: 'th',
21860                 cls: 'next',
21861                 html: '<i class="fa fa-arrow-right"/>'
21862             }
21863
21864             ]
21865         }
21866         ]
21867     },
21868     
21869     content : {
21870         tag: 'tbody',
21871         cn: [
21872         {
21873             tag: 'tr',
21874             cn: [
21875             {
21876                 tag: 'td',
21877                 colspan: '7'
21878             }
21879             ]
21880         }
21881         ]
21882     },
21883     
21884     footer : {
21885         tag: 'tfoot',
21886         cn: [
21887         {
21888             tag: 'tr',
21889             cn: [
21890             {
21891                 tag: 'th',
21892                 colspan: '7',
21893                 cls: 'today'
21894             }
21895                     
21896             ]
21897         }
21898         ]
21899     },
21900     
21901     dates:{
21902         en: {
21903             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21904             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21905             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21906             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21907             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21908             today: "Today"
21909         }
21910     },
21911     
21912     modes: [
21913     {
21914         clsName: 'days',
21915         navFnc: 'Month',
21916         navStep: 1
21917     },
21918     {
21919         clsName: 'months',
21920         navFnc: 'FullYear',
21921         navStep: 1
21922     },
21923     {
21924         clsName: 'years',
21925         navFnc: 'FullYear',
21926         navStep: 10
21927     }]
21928 });
21929
21930 Roo.apply(Roo.bootstrap.DateField,  {
21931   
21932     template : {
21933         tag: 'div',
21934         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21935         cn: [
21936         {
21937             tag: 'div',
21938             cls: 'datepicker-days',
21939             cn: [
21940             {
21941                 tag: 'table',
21942                 cls: 'table-condensed',
21943                 cn:[
21944                 Roo.bootstrap.DateField.head,
21945                 {
21946                     tag: 'tbody'
21947                 },
21948                 Roo.bootstrap.DateField.footer
21949                 ]
21950             }
21951             ]
21952         },
21953         {
21954             tag: 'div',
21955             cls: 'datepicker-months',
21956             cn: [
21957             {
21958                 tag: 'table',
21959                 cls: 'table-condensed',
21960                 cn:[
21961                 Roo.bootstrap.DateField.head,
21962                 Roo.bootstrap.DateField.content,
21963                 Roo.bootstrap.DateField.footer
21964                 ]
21965             }
21966             ]
21967         },
21968         {
21969             tag: 'div',
21970             cls: 'datepicker-years',
21971             cn: [
21972             {
21973                 tag: 'table',
21974                 cls: 'table-condensed',
21975                 cn:[
21976                 Roo.bootstrap.DateField.head,
21977                 Roo.bootstrap.DateField.content,
21978                 Roo.bootstrap.DateField.footer
21979                 ]
21980             }
21981             ]
21982         }
21983         ]
21984     }
21985 });
21986
21987  
21988
21989  /*
21990  * - LGPL
21991  *
21992  * TimeField
21993  * 
21994  */
21995
21996 /**
21997  * @class Roo.bootstrap.TimeField
21998  * @extends Roo.bootstrap.Input
21999  * Bootstrap DateField class
22000  * 
22001  * 
22002  * @constructor
22003  * Create a new TimeField
22004  * @param {Object} config The config object
22005  */
22006
22007 Roo.bootstrap.TimeField = function(config){
22008     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22009     this.addEvents({
22010             /**
22011              * @event show
22012              * Fires when this field show.
22013              * @param {Roo.bootstrap.DateField} thisthis
22014              * @param {Mixed} date The date value
22015              */
22016             show : true,
22017             /**
22018              * @event show
22019              * Fires when this field hide.
22020              * @param {Roo.bootstrap.DateField} this
22021              * @param {Mixed} date The date value
22022              */
22023             hide : true,
22024             /**
22025              * @event select
22026              * Fires when select a date.
22027              * @param {Roo.bootstrap.DateField} this
22028              * @param {Mixed} date The date value
22029              */
22030             select : true
22031         });
22032 };
22033
22034 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22035     
22036     /**
22037      * @cfg {String} format
22038      * The default time format string which can be overriden for localization support.  The format must be
22039      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22040      */
22041     format : "H:i",
22042
22043     getAutoCreate : function()
22044     {
22045         this.after = '<i class="fa far fa-clock"></i>';
22046         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22047         
22048          
22049     },
22050     onRender: function(ct, position)
22051     {
22052         
22053         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22054                 
22055         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22056         
22057         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22058         
22059         this.pop = this.picker().select('>.datepicker-time',true).first();
22060         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22061         
22062         this.picker().on('mousedown', this.onMousedown, this);
22063         this.picker().on('click', this.onClick, this);
22064         
22065         this.picker().addClass('datepicker-dropdown');
22066     
22067         this.fillTime();
22068         this.update();
22069             
22070         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22071         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22072         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22073         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22074         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22075         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22076
22077     },
22078     
22079     fireKey: function(e){
22080         if (!this.picker().isVisible()){
22081             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22082                 this.show();
22083             }
22084             return;
22085         }
22086
22087         e.preventDefault();
22088         
22089         switch(e.keyCode){
22090             case 27: // escape
22091                 this.hide();
22092                 break;
22093             case 37: // left
22094             case 39: // right
22095                 this.onTogglePeriod();
22096                 break;
22097             case 38: // up
22098                 this.onIncrementMinutes();
22099                 break;
22100             case 40: // down
22101                 this.onDecrementMinutes();
22102                 break;
22103             case 13: // enter
22104             case 9: // tab
22105                 this.setTime();
22106                 break;
22107         }
22108     },
22109     
22110     onClick: function(e) {
22111         e.stopPropagation();
22112         e.preventDefault();
22113     },
22114     
22115     picker : function()
22116     {
22117         return this.pickerEl;
22118     },
22119     
22120     fillTime: function()
22121     {    
22122         var time = this.pop.select('tbody', true).first();
22123         
22124         time.dom.innerHTML = '';
22125         
22126         time.createChild({
22127             tag: 'tr',
22128             cn: [
22129                 {
22130                     tag: 'td',
22131                     cn: [
22132                         {
22133                             tag: 'a',
22134                             href: '#',
22135                             cls: 'btn',
22136                             cn: [
22137                                 {
22138                                     tag: 'i',
22139                                     cls: 'hours-up fa fas fa-chevron-up'
22140                                 }
22141                             ]
22142                         } 
22143                     ]
22144                 },
22145                 {
22146                     tag: 'td',
22147                     cls: 'separator'
22148                 },
22149                 {
22150                     tag: 'td',
22151                     cn: [
22152                         {
22153                             tag: 'a',
22154                             href: '#',
22155                             cls: 'btn',
22156                             cn: [
22157                                 {
22158                                     tag: 'i',
22159                                     cls: 'minutes-up fa fas fa-chevron-up'
22160                                 }
22161                             ]
22162                         }
22163                     ]
22164                 },
22165                 {
22166                     tag: 'td',
22167                     cls: 'separator'
22168                 }
22169             ]
22170         });
22171         
22172         time.createChild({
22173             tag: 'tr',
22174             cn: [
22175                 {
22176                     tag: 'td',
22177                     cn: [
22178                         {
22179                             tag: 'span',
22180                             cls: 'timepicker-hour',
22181                             html: '00'
22182                         }  
22183                     ]
22184                 },
22185                 {
22186                     tag: 'td',
22187                     cls: 'separator',
22188                     html: ':'
22189                 },
22190                 {
22191                     tag: 'td',
22192                     cn: [
22193                         {
22194                             tag: 'span',
22195                             cls: 'timepicker-minute',
22196                             html: '00'
22197                         }  
22198                     ]
22199                 },
22200                 {
22201                     tag: 'td',
22202                     cls: 'separator'
22203                 },
22204                 {
22205                     tag: 'td',
22206                     cn: [
22207                         {
22208                             tag: 'button',
22209                             type: 'button',
22210                             cls: 'btn btn-primary period',
22211                             html: 'AM'
22212                             
22213                         }
22214                     ]
22215                 }
22216             ]
22217         });
22218         
22219         time.createChild({
22220             tag: 'tr',
22221             cn: [
22222                 {
22223                     tag: 'td',
22224                     cn: [
22225                         {
22226                             tag: 'a',
22227                             href: '#',
22228                             cls: 'btn',
22229                             cn: [
22230                                 {
22231                                     tag: 'span',
22232                                     cls: 'hours-down fa fas fa-chevron-down'
22233                                 }
22234                             ]
22235                         }
22236                     ]
22237                 },
22238                 {
22239                     tag: 'td',
22240                     cls: 'separator'
22241                 },
22242                 {
22243                     tag: 'td',
22244                     cn: [
22245                         {
22246                             tag: 'a',
22247                             href: '#',
22248                             cls: 'btn',
22249                             cn: [
22250                                 {
22251                                     tag: 'span',
22252                                     cls: 'minutes-down fa fas fa-chevron-down'
22253                                 }
22254                             ]
22255                         }
22256                     ]
22257                 },
22258                 {
22259                     tag: 'td',
22260                     cls: 'separator'
22261                 }
22262             ]
22263         });
22264         
22265     },
22266     
22267     update: function()
22268     {
22269         
22270         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22271         
22272         this.fill();
22273     },
22274     
22275     fill: function() 
22276     {
22277         var hours = this.time.getHours();
22278         var minutes = this.time.getMinutes();
22279         var period = 'AM';
22280         
22281         if(hours > 11){
22282             period = 'PM';
22283         }
22284         
22285         if(hours == 0){
22286             hours = 12;
22287         }
22288         
22289         
22290         if(hours > 12){
22291             hours = hours - 12;
22292         }
22293         
22294         if(hours < 10){
22295             hours = '0' + hours;
22296         }
22297         
22298         if(minutes < 10){
22299             minutes = '0' + minutes;
22300         }
22301         
22302         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22303         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22304         this.pop.select('button', true).first().dom.innerHTML = period;
22305         
22306     },
22307     
22308     place: function()
22309     {   
22310         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22311         
22312         var cls = ['bottom'];
22313         
22314         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22315             cls.pop();
22316             cls.push('top');
22317         }
22318         
22319         cls.push('right');
22320         
22321         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22322             cls.pop();
22323             cls.push('left');
22324         }
22325         //this.picker().setXY(20000,20000);
22326         this.picker().addClass(cls.join('-'));
22327         
22328         var _this = this;
22329         
22330         Roo.each(cls, function(c){
22331             if(c == 'bottom'){
22332                 (function() {
22333                  //  
22334                 }).defer(200);
22335                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22336                 //_this.picker().setTop(_this.inputEl().getHeight());
22337                 return;
22338             }
22339             if(c == 'top'){
22340                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22341                 
22342                 //_this.picker().setTop(0 - _this.picker().getHeight());
22343                 return;
22344             }
22345             /*
22346             if(c == 'left'){
22347                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22348                 return;
22349             }
22350             if(c == 'right'){
22351                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22352                 return;
22353             }
22354             */
22355         });
22356         
22357     },
22358   
22359     onFocus : function()
22360     {
22361         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22362         this.show();
22363     },
22364     
22365     onBlur : function()
22366     {
22367         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22368         this.hide();
22369     },
22370     
22371     show : function()
22372     {
22373         this.picker().show();
22374         this.pop.show();
22375         this.update();
22376         this.place();
22377         
22378         this.fireEvent('show', this, this.date);
22379     },
22380     
22381     hide : function()
22382     {
22383         this.picker().hide();
22384         this.pop.hide();
22385         
22386         this.fireEvent('hide', this, this.date);
22387     },
22388     
22389     setTime : function()
22390     {
22391         this.hide();
22392         this.setValue(this.time.format(this.format));
22393         
22394         this.fireEvent('select', this, this.date);
22395         
22396         
22397     },
22398     
22399     onMousedown: function(e){
22400         e.stopPropagation();
22401         e.preventDefault();
22402     },
22403     
22404     onIncrementHours: function()
22405     {
22406         Roo.log('onIncrementHours');
22407         this.time = this.time.add(Date.HOUR, 1);
22408         this.update();
22409         
22410     },
22411     
22412     onDecrementHours: function()
22413     {
22414         Roo.log('onDecrementHours');
22415         this.time = this.time.add(Date.HOUR, -1);
22416         this.update();
22417     },
22418     
22419     onIncrementMinutes: function()
22420     {
22421         Roo.log('onIncrementMinutes');
22422         this.time = this.time.add(Date.MINUTE, 1);
22423         this.update();
22424     },
22425     
22426     onDecrementMinutes: function()
22427     {
22428         Roo.log('onDecrementMinutes');
22429         this.time = this.time.add(Date.MINUTE, -1);
22430         this.update();
22431     },
22432     
22433     onTogglePeriod: function()
22434     {
22435         Roo.log('onTogglePeriod');
22436         this.time = this.time.add(Date.HOUR, 12);
22437         this.update();
22438     }
22439     
22440    
22441 });
22442  
22443
22444 Roo.apply(Roo.bootstrap.TimeField,  {
22445   
22446     template : {
22447         tag: 'div',
22448         cls: 'datepicker dropdown-menu',
22449         cn: [
22450             {
22451                 tag: 'div',
22452                 cls: 'datepicker-time',
22453                 cn: [
22454                 {
22455                     tag: 'table',
22456                     cls: 'table-condensed',
22457                     cn:[
22458                         {
22459                             tag: 'tbody',
22460                             cn: [
22461                                 {
22462                                     tag: 'tr',
22463                                     cn: [
22464                                     {
22465                                         tag: 'td',
22466                                         colspan: '7'
22467                                     }
22468                                     ]
22469                                 }
22470                             ]
22471                         },
22472                         {
22473                             tag: 'tfoot',
22474                             cn: [
22475                                 {
22476                                     tag: 'tr',
22477                                     cn: [
22478                                     {
22479                                         tag: 'th',
22480                                         colspan: '7',
22481                                         cls: '',
22482                                         cn: [
22483                                             {
22484                                                 tag: 'button',
22485                                                 cls: 'btn btn-info ok',
22486                                                 html: 'OK'
22487                                             }
22488                                         ]
22489                                     }
22490                     
22491                                     ]
22492                                 }
22493                             ]
22494                         }
22495                     ]
22496                 }
22497                 ]
22498             }
22499         ]
22500     }
22501 });
22502
22503  
22504
22505  /*
22506  * - LGPL
22507  *
22508  * MonthField
22509  * 
22510  */
22511
22512 /**
22513  * @class Roo.bootstrap.MonthField
22514  * @extends Roo.bootstrap.Input
22515  * Bootstrap MonthField class
22516  * 
22517  * @cfg {String} language default en
22518  * 
22519  * @constructor
22520  * Create a new MonthField
22521  * @param {Object} config The config object
22522  */
22523
22524 Roo.bootstrap.MonthField = function(config){
22525     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22526     
22527     this.addEvents({
22528         /**
22529          * @event show
22530          * Fires when this field show.
22531          * @param {Roo.bootstrap.MonthField} this
22532          * @param {Mixed} date The date value
22533          */
22534         show : true,
22535         /**
22536          * @event show
22537          * Fires when this field hide.
22538          * @param {Roo.bootstrap.MonthField} this
22539          * @param {Mixed} date The date value
22540          */
22541         hide : true,
22542         /**
22543          * @event select
22544          * Fires when select a date.
22545          * @param {Roo.bootstrap.MonthField} this
22546          * @param {String} oldvalue The old value
22547          * @param {String} newvalue The new value
22548          */
22549         select : true
22550     });
22551 };
22552
22553 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22554     
22555     onRender: function(ct, position)
22556     {
22557         
22558         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22559         
22560         this.language = this.language || 'en';
22561         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22562         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22563         
22564         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22565         this.isInline = false;
22566         this.isInput = true;
22567         this.component = this.el.select('.add-on', true).first() || false;
22568         this.component = (this.component && this.component.length === 0) ? false : this.component;
22569         this.hasInput = this.component && this.inputEL().length;
22570         
22571         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22572         
22573         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22574         
22575         this.picker().on('mousedown', this.onMousedown, this);
22576         this.picker().on('click', this.onClick, this);
22577         
22578         this.picker().addClass('datepicker-dropdown');
22579         
22580         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22581             v.setStyle('width', '189px');
22582         });
22583         
22584         this.fillMonths();
22585         
22586         this.update();
22587         
22588         if(this.isInline) {
22589             this.show();
22590         }
22591         
22592     },
22593     
22594     setValue: function(v, suppressEvent)
22595     {   
22596         var o = this.getValue();
22597         
22598         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22599         
22600         this.update();
22601
22602         if(suppressEvent !== true){
22603             this.fireEvent('select', this, o, v);
22604         }
22605         
22606     },
22607     
22608     getValue: function()
22609     {
22610         return this.value;
22611     },
22612     
22613     onClick: function(e) 
22614     {
22615         e.stopPropagation();
22616         e.preventDefault();
22617         
22618         var target = e.getTarget();
22619         
22620         if(target.nodeName.toLowerCase() === 'i'){
22621             target = Roo.get(target).dom.parentNode;
22622         }
22623         
22624         var nodeName = target.nodeName;
22625         var className = target.className;
22626         var html = target.innerHTML;
22627         
22628         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22629             return;
22630         }
22631         
22632         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22633         
22634         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22635         
22636         this.hide();
22637                         
22638     },
22639     
22640     picker : function()
22641     {
22642         return this.pickerEl;
22643     },
22644     
22645     fillMonths: function()
22646     {    
22647         var i = 0;
22648         var months = this.picker().select('>.datepicker-months td', true).first();
22649         
22650         months.dom.innerHTML = '';
22651         
22652         while (i < 12) {
22653             var month = {
22654                 tag: 'span',
22655                 cls: 'month',
22656                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22657             };
22658             
22659             months.createChild(month);
22660         }
22661         
22662     },
22663     
22664     update: function()
22665     {
22666         var _this = this;
22667         
22668         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22669             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22670         }
22671         
22672         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22673             e.removeClass('active');
22674             
22675             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22676                 e.addClass('active');
22677             }
22678         })
22679     },
22680     
22681     place: function()
22682     {
22683         if(this.isInline) {
22684             return;
22685         }
22686         
22687         this.picker().removeClass(['bottom', 'top']);
22688         
22689         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22690             /*
22691              * place to the top of element!
22692              *
22693              */
22694             
22695             this.picker().addClass('top');
22696             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22697             
22698             return;
22699         }
22700         
22701         this.picker().addClass('bottom');
22702         
22703         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22704     },
22705     
22706     onFocus : function()
22707     {
22708         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22709         this.show();
22710     },
22711     
22712     onBlur : function()
22713     {
22714         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22715         
22716         var d = this.inputEl().getValue();
22717         
22718         this.setValue(d);
22719                 
22720         this.hide();
22721     },
22722     
22723     show : function()
22724     {
22725         this.picker().show();
22726         this.picker().select('>.datepicker-months', true).first().show();
22727         this.update();
22728         this.place();
22729         
22730         this.fireEvent('show', this, this.date);
22731     },
22732     
22733     hide : function()
22734     {
22735         if(this.isInline) {
22736             return;
22737         }
22738         this.picker().hide();
22739         this.fireEvent('hide', this, this.date);
22740         
22741     },
22742     
22743     onMousedown: function(e)
22744     {
22745         e.stopPropagation();
22746         e.preventDefault();
22747     },
22748     
22749     keyup: function(e)
22750     {
22751         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22752         this.update();
22753     },
22754
22755     fireKey: function(e)
22756     {
22757         if (!this.picker().isVisible()){
22758             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22759                 this.show();
22760             }
22761             return;
22762         }
22763         
22764         var dir;
22765         
22766         switch(e.keyCode){
22767             case 27: // escape
22768                 this.hide();
22769                 e.preventDefault();
22770                 break;
22771             case 37: // left
22772             case 39: // right
22773                 dir = e.keyCode == 37 ? -1 : 1;
22774                 
22775                 this.vIndex = this.vIndex + dir;
22776                 
22777                 if(this.vIndex < 0){
22778                     this.vIndex = 0;
22779                 }
22780                 
22781                 if(this.vIndex > 11){
22782                     this.vIndex = 11;
22783                 }
22784                 
22785                 if(isNaN(this.vIndex)){
22786                     this.vIndex = 0;
22787                 }
22788                 
22789                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22790                 
22791                 break;
22792             case 38: // up
22793             case 40: // down
22794                 
22795                 dir = e.keyCode == 38 ? -1 : 1;
22796                 
22797                 this.vIndex = this.vIndex + dir * 4;
22798                 
22799                 if(this.vIndex < 0){
22800                     this.vIndex = 0;
22801                 }
22802                 
22803                 if(this.vIndex > 11){
22804                     this.vIndex = 11;
22805                 }
22806                 
22807                 if(isNaN(this.vIndex)){
22808                     this.vIndex = 0;
22809                 }
22810                 
22811                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22812                 break;
22813                 
22814             case 13: // enter
22815                 
22816                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22817                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22818                 }
22819                 
22820                 this.hide();
22821                 e.preventDefault();
22822                 break;
22823             case 9: // tab
22824                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22825                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22826                 }
22827                 this.hide();
22828                 break;
22829             case 16: // shift
22830             case 17: // ctrl
22831             case 18: // alt
22832                 break;
22833             default :
22834                 this.hide();
22835                 
22836         }
22837     },
22838     
22839     remove: function() 
22840     {
22841         this.picker().remove();
22842     }
22843    
22844 });
22845
22846 Roo.apply(Roo.bootstrap.MonthField,  {
22847     
22848     content : {
22849         tag: 'tbody',
22850         cn: [
22851         {
22852             tag: 'tr',
22853             cn: [
22854             {
22855                 tag: 'td',
22856                 colspan: '7'
22857             }
22858             ]
22859         }
22860         ]
22861     },
22862     
22863     dates:{
22864         en: {
22865             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22866             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22867         }
22868     }
22869 });
22870
22871 Roo.apply(Roo.bootstrap.MonthField,  {
22872   
22873     template : {
22874         tag: 'div',
22875         cls: 'datepicker dropdown-menu roo-dynamic',
22876         cn: [
22877             {
22878                 tag: 'div',
22879                 cls: 'datepicker-months',
22880                 cn: [
22881                 {
22882                     tag: 'table',
22883                     cls: 'table-condensed',
22884                     cn:[
22885                         Roo.bootstrap.DateField.content
22886                     ]
22887                 }
22888                 ]
22889             }
22890         ]
22891     }
22892 });
22893
22894  
22895
22896  
22897  /*
22898  * - LGPL
22899  *
22900  * CheckBox
22901  * 
22902  */
22903
22904 /**
22905  * @class Roo.bootstrap.CheckBox
22906  * @extends Roo.bootstrap.Input
22907  * Bootstrap CheckBox class
22908  * 
22909  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22910  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22911  * @cfg {String} boxLabel The text that appears beside the checkbox
22912  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22913  * @cfg {Boolean} checked initnal the element
22914  * @cfg {Boolean} inline inline the element (default false)
22915  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22916  * @cfg {String} tooltip label tooltip
22917  * 
22918  * @constructor
22919  * Create a new CheckBox
22920  * @param {Object} config The config object
22921  */
22922
22923 Roo.bootstrap.CheckBox = function(config){
22924     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22925    
22926     this.addEvents({
22927         /**
22928         * @event check
22929         * Fires when the element is checked or unchecked.
22930         * @param {Roo.bootstrap.CheckBox} this This input
22931         * @param {Boolean} checked The new checked value
22932         */
22933        check : true,
22934        /**
22935         * @event click
22936         * Fires when the element is click.
22937         * @param {Roo.bootstrap.CheckBox} this This input
22938         */
22939        click : true
22940     });
22941     
22942 };
22943
22944 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22945   
22946     inputType: 'checkbox',
22947     inputValue: 1,
22948     valueOff: 0,
22949     boxLabel: false,
22950     checked: false,
22951     weight : false,
22952     inline: false,
22953     tooltip : '',
22954     
22955     // checkbox success does not make any sense really.. 
22956     invalidClass : "",
22957     validClass : "",
22958     
22959     
22960     getAutoCreate : function()
22961     {
22962         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22963         
22964         var id = Roo.id();
22965         
22966         var cfg = {};
22967         
22968         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22969         
22970         if(this.inline){
22971             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22972         }
22973         
22974         var input =  {
22975             tag: 'input',
22976             id : id,
22977             type : this.inputType,
22978             value : this.inputValue,
22979             cls : 'roo-' + this.inputType, //'form-box',
22980             placeholder : this.placeholder || ''
22981             
22982         };
22983         
22984         if(this.inputType != 'radio'){
22985             var hidden =  {
22986                 tag: 'input',
22987                 type : 'hidden',
22988                 cls : 'roo-hidden-value',
22989                 value : this.checked ? this.inputValue : this.valueOff
22990             };
22991         }
22992         
22993             
22994         if (this.weight) { // Validity check?
22995             cfg.cls += " " + this.inputType + "-" + this.weight;
22996         }
22997         
22998         if (this.disabled) {
22999             input.disabled=true;
23000         }
23001         
23002         if(this.checked){
23003             input.checked = this.checked;
23004         }
23005         
23006         if (this.name) {
23007             
23008             input.name = this.name;
23009             
23010             if(this.inputType != 'radio'){
23011                 hidden.name = this.name;
23012                 input.name = '_hidden_' + this.name;
23013             }
23014         }
23015         
23016         if (this.size) {
23017             input.cls += ' input-' + this.size;
23018         }
23019         
23020         var settings=this;
23021         
23022         ['xs','sm','md','lg'].map(function(size){
23023             if (settings[size]) {
23024                 cfg.cls += ' col-' + size + '-' + settings[size];
23025             }
23026         });
23027         
23028         var inputblock = input;
23029          
23030         if (this.before || this.after) {
23031             
23032             inputblock = {
23033                 cls : 'input-group',
23034                 cn :  [] 
23035             };
23036             
23037             if (this.before) {
23038                 inputblock.cn.push({
23039                     tag :'span',
23040                     cls : 'input-group-addon',
23041                     html : this.before
23042                 });
23043             }
23044             
23045             inputblock.cn.push(input);
23046             
23047             if(this.inputType != 'radio'){
23048                 inputblock.cn.push(hidden);
23049             }
23050             
23051             if (this.after) {
23052                 inputblock.cn.push({
23053                     tag :'span',
23054                     cls : 'input-group-addon',
23055                     html : this.after
23056                 });
23057             }
23058             
23059         }
23060         var boxLabelCfg = false;
23061         
23062         if(this.boxLabel){
23063            
23064             boxLabelCfg = {
23065                 tag: 'label',
23066                 //'for': id, // box label is handled by onclick - so no for...
23067                 cls: 'box-label',
23068                 html: this.boxLabel
23069             };
23070             if(this.tooltip){
23071                 boxLabelCfg.tooltip = this.tooltip;
23072             }
23073              
23074         }
23075         
23076         
23077         if (align ==='left' && this.fieldLabel.length) {
23078 //                Roo.log("left and has label");
23079             cfg.cn = [
23080                 {
23081                     tag: 'label',
23082                     'for' :  id,
23083                     cls : 'control-label',
23084                     html : this.fieldLabel
23085                 },
23086                 {
23087                     cls : "", 
23088                     cn: [
23089                         inputblock
23090                     ]
23091                 }
23092             ];
23093             
23094             if (boxLabelCfg) {
23095                 cfg.cn[1].cn.push(boxLabelCfg);
23096             }
23097             
23098             if(this.labelWidth > 12){
23099                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23100             }
23101             
23102             if(this.labelWidth < 13 && this.labelmd == 0){
23103                 this.labelmd = this.labelWidth;
23104             }
23105             
23106             if(this.labellg > 0){
23107                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23108                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23109             }
23110             
23111             if(this.labelmd > 0){
23112                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23113                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23114             }
23115             
23116             if(this.labelsm > 0){
23117                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23118                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23119             }
23120             
23121             if(this.labelxs > 0){
23122                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23123                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23124             }
23125             
23126         } else if ( this.fieldLabel.length) {
23127 //                Roo.log(" label");
23128                 cfg.cn = [
23129                    
23130                     {
23131                         tag: this.boxLabel ? 'span' : 'label',
23132                         'for': id,
23133                         cls: 'control-label box-input-label',
23134                         //cls : 'input-group-addon',
23135                         html : this.fieldLabel
23136                     },
23137                     
23138                     inputblock
23139                     
23140                 ];
23141                 if (boxLabelCfg) {
23142                     cfg.cn.push(boxLabelCfg);
23143                 }
23144
23145         } else {
23146             
23147 //                Roo.log(" no label && no align");
23148                 cfg.cn = [  inputblock ] ;
23149                 if (boxLabelCfg) {
23150                     cfg.cn.push(boxLabelCfg);
23151                 }
23152
23153                 
23154         }
23155         
23156        
23157         
23158         if(this.inputType != 'radio'){
23159             cfg.cn.push(hidden);
23160         }
23161         
23162         return cfg;
23163         
23164     },
23165     
23166     /**
23167      * return the real input element.
23168      */
23169     inputEl: function ()
23170     {
23171         return this.el.select('input.roo-' + this.inputType,true).first();
23172     },
23173     hiddenEl: function ()
23174     {
23175         return this.el.select('input.roo-hidden-value',true).first();
23176     },
23177     
23178     labelEl: function()
23179     {
23180         return this.el.select('label.control-label',true).first();
23181     },
23182     /* depricated... */
23183     
23184     label: function()
23185     {
23186         return this.labelEl();
23187     },
23188     
23189     boxLabelEl: function()
23190     {
23191         return this.el.select('label.box-label',true).first();
23192     },
23193     
23194     initEvents : function()
23195     {
23196 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23197         
23198         this.inputEl().on('click', this.onClick,  this);
23199         
23200         if (this.boxLabel) { 
23201             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23202         }
23203         
23204         this.startValue = this.getValue();
23205         
23206         if(this.groupId){
23207             Roo.bootstrap.CheckBox.register(this);
23208         }
23209     },
23210     
23211     onClick : function(e)
23212     {   
23213         if(this.fireEvent('click', this, e) !== false){
23214             this.setChecked(!this.checked);
23215         }
23216         
23217     },
23218     
23219     setChecked : function(state,suppressEvent)
23220     {
23221         this.startValue = this.getValue();
23222
23223         if(this.inputType == 'radio'){
23224             
23225             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23226                 e.dom.checked = false;
23227             });
23228             
23229             this.inputEl().dom.checked = true;
23230             
23231             this.inputEl().dom.value = this.inputValue;
23232             
23233             if(suppressEvent !== true){
23234                 this.fireEvent('check', this, true);
23235             }
23236             
23237             this.validate();
23238             
23239             return;
23240         }
23241         
23242         this.checked = state;
23243         
23244         this.inputEl().dom.checked = state;
23245         
23246         
23247         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23248         
23249         if(suppressEvent !== true){
23250             this.fireEvent('check', this, state);
23251         }
23252         
23253         this.validate();
23254     },
23255     
23256     getValue : function()
23257     {
23258         if(this.inputType == 'radio'){
23259             return this.getGroupValue();
23260         }
23261         
23262         return this.hiddenEl().dom.value;
23263         
23264     },
23265     
23266     getGroupValue : function()
23267     {
23268         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23269             return '';
23270         }
23271         
23272         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23273     },
23274     
23275     setValue : function(v,suppressEvent)
23276     {
23277         if(this.inputType == 'radio'){
23278             this.setGroupValue(v, suppressEvent);
23279             return;
23280         }
23281         
23282         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23283         
23284         this.validate();
23285     },
23286     
23287     setGroupValue : function(v, suppressEvent)
23288     {
23289         this.startValue = this.getValue();
23290         
23291         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23292             e.dom.checked = false;
23293             
23294             if(e.dom.value == v){
23295                 e.dom.checked = true;
23296             }
23297         });
23298         
23299         if(suppressEvent !== true){
23300             this.fireEvent('check', this, true);
23301         }
23302
23303         this.validate();
23304         
23305         return;
23306     },
23307     
23308     validate : function()
23309     {
23310         if(this.getVisibilityEl().hasClass('hidden')){
23311             return true;
23312         }
23313         
23314         if(
23315                 this.disabled || 
23316                 (this.inputType == 'radio' && this.validateRadio()) ||
23317                 (this.inputType == 'checkbox' && this.validateCheckbox())
23318         ){
23319             this.markValid();
23320             return true;
23321         }
23322         
23323         this.markInvalid();
23324         return false;
23325     },
23326     
23327     validateRadio : function()
23328     {
23329         if(this.getVisibilityEl().hasClass('hidden')){
23330             return true;
23331         }
23332         
23333         if(this.allowBlank){
23334             return true;
23335         }
23336         
23337         var valid = false;
23338         
23339         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23340             if(!e.dom.checked){
23341                 return;
23342             }
23343             
23344             valid = true;
23345             
23346             return false;
23347         });
23348         
23349         return valid;
23350     },
23351     
23352     validateCheckbox : function()
23353     {
23354         if(!this.groupId){
23355             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23356             //return (this.getValue() == this.inputValue) ? true : false;
23357         }
23358         
23359         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23360         
23361         if(!group){
23362             return false;
23363         }
23364         
23365         var r = false;
23366         
23367         for(var i in group){
23368             if(group[i].el.isVisible(true)){
23369                 r = false;
23370                 break;
23371             }
23372             
23373             r = true;
23374         }
23375         
23376         for(var i in group){
23377             if(r){
23378                 break;
23379             }
23380             
23381             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23382         }
23383         
23384         return r;
23385     },
23386     
23387     /**
23388      * Mark this field as valid
23389      */
23390     markValid : function()
23391     {
23392         var _this = this;
23393         
23394         this.fireEvent('valid', this);
23395         
23396         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23397         
23398         if(this.groupId){
23399             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23400         }
23401         
23402         if(label){
23403             label.markValid();
23404         }
23405
23406         if(this.inputType == 'radio'){
23407             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23408                 var fg = e.findParent('.form-group', false, true);
23409                 if (Roo.bootstrap.version == 3) {
23410                     fg.removeClass([_this.invalidClass, _this.validClass]);
23411                     fg.addClass(_this.validClass);
23412                 } else {
23413                     fg.removeClass(['is-valid', 'is-invalid']);
23414                     fg.addClass('is-valid');
23415                 }
23416             });
23417             
23418             return;
23419         }
23420
23421         if(!this.groupId){
23422             var fg = this.el.findParent('.form-group', false, true);
23423             if (Roo.bootstrap.version == 3) {
23424                 fg.removeClass([this.invalidClass, this.validClass]);
23425                 fg.addClass(this.validClass);
23426             } else {
23427                 fg.removeClass(['is-valid', 'is-invalid']);
23428                 fg.addClass('is-valid');
23429             }
23430             return;
23431         }
23432         
23433         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23434         
23435         if(!group){
23436             return;
23437         }
23438         
23439         for(var i in group){
23440             var fg = group[i].el.findParent('.form-group', false, true);
23441             if (Roo.bootstrap.version == 3) {
23442                 fg.removeClass([this.invalidClass, this.validClass]);
23443                 fg.addClass(this.validClass);
23444             } else {
23445                 fg.removeClass(['is-valid', 'is-invalid']);
23446                 fg.addClass('is-valid');
23447             }
23448         }
23449     },
23450     
23451      /**
23452      * Mark this field as invalid
23453      * @param {String} msg The validation message
23454      */
23455     markInvalid : function(msg)
23456     {
23457         if(this.allowBlank){
23458             return;
23459         }
23460         
23461         var _this = this;
23462         
23463         this.fireEvent('invalid', this, msg);
23464         
23465         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23466         
23467         if(this.groupId){
23468             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23469         }
23470         
23471         if(label){
23472             label.markInvalid();
23473         }
23474             
23475         if(this.inputType == 'radio'){
23476             
23477             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23478                 var fg = e.findParent('.form-group', false, true);
23479                 if (Roo.bootstrap.version == 3) {
23480                     fg.removeClass([_this.invalidClass, _this.validClass]);
23481                     fg.addClass(_this.invalidClass);
23482                 } else {
23483                     fg.removeClass(['is-invalid', 'is-valid']);
23484                     fg.addClass('is-invalid');
23485                 }
23486             });
23487             
23488             return;
23489         }
23490         
23491         if(!this.groupId){
23492             var fg = this.el.findParent('.form-group', false, true);
23493             if (Roo.bootstrap.version == 3) {
23494                 fg.removeClass([_this.invalidClass, _this.validClass]);
23495                 fg.addClass(_this.invalidClass);
23496             } else {
23497                 fg.removeClass(['is-invalid', 'is-valid']);
23498                 fg.addClass('is-invalid');
23499             }
23500             return;
23501         }
23502         
23503         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23504         
23505         if(!group){
23506             return;
23507         }
23508         
23509         for(var i in group){
23510             var fg = group[i].el.findParent('.form-group', false, true);
23511             if (Roo.bootstrap.version == 3) {
23512                 fg.removeClass([_this.invalidClass, _this.validClass]);
23513                 fg.addClass(_this.invalidClass);
23514             } else {
23515                 fg.removeClass(['is-invalid', 'is-valid']);
23516                 fg.addClass('is-invalid');
23517             }
23518         }
23519         
23520     },
23521     
23522     clearInvalid : function()
23523     {
23524         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23525         
23526         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23527         
23528         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23529         
23530         if (label && label.iconEl) {
23531             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23532             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23533         }
23534     },
23535     
23536     disable : function()
23537     {
23538         if(this.inputType != 'radio'){
23539             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23540             return;
23541         }
23542         
23543         var _this = this;
23544         
23545         if(this.rendered){
23546             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23547                 _this.getActionEl().addClass(this.disabledClass);
23548                 e.dom.disabled = true;
23549             });
23550         }
23551         
23552         this.disabled = true;
23553         this.fireEvent("disable", this);
23554         return this;
23555     },
23556
23557     enable : function()
23558     {
23559         if(this.inputType != 'radio'){
23560             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23561             return;
23562         }
23563         
23564         var _this = this;
23565         
23566         if(this.rendered){
23567             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23568                 _this.getActionEl().removeClass(this.disabledClass);
23569                 e.dom.disabled = false;
23570             });
23571         }
23572         
23573         this.disabled = false;
23574         this.fireEvent("enable", this);
23575         return this;
23576     },
23577     
23578     setBoxLabel : function(v)
23579     {
23580         this.boxLabel = v;
23581         
23582         if(this.rendered){
23583             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23584         }
23585     }
23586
23587 });
23588
23589 Roo.apply(Roo.bootstrap.CheckBox, {
23590     
23591     groups: {},
23592     
23593      /**
23594     * register a CheckBox Group
23595     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23596     */
23597     register : function(checkbox)
23598     {
23599         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23600             this.groups[checkbox.groupId] = {};
23601         }
23602         
23603         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23604             return;
23605         }
23606         
23607         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23608         
23609     },
23610     /**
23611     * fetch a CheckBox Group based on the group ID
23612     * @param {string} the group ID
23613     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23614     */
23615     get: function(groupId) {
23616         if (typeof(this.groups[groupId]) == 'undefined') {
23617             return false;
23618         }
23619         
23620         return this.groups[groupId] ;
23621     }
23622     
23623     
23624 });
23625 /*
23626  * - LGPL
23627  *
23628  * RadioItem
23629  * 
23630  */
23631
23632 /**
23633  * @class Roo.bootstrap.Radio
23634  * @extends Roo.bootstrap.Component
23635  * Bootstrap Radio class
23636  * @cfg {String} boxLabel - the label associated
23637  * @cfg {String} value - the value of radio
23638  * 
23639  * @constructor
23640  * Create a new Radio
23641  * @param {Object} config The config object
23642  */
23643 Roo.bootstrap.Radio = function(config){
23644     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23645     
23646 };
23647
23648 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23649     
23650     boxLabel : '',
23651     
23652     value : '',
23653     
23654     getAutoCreate : function()
23655     {
23656         var cfg = {
23657             tag : 'div',
23658             cls : 'form-group radio',
23659             cn : [
23660                 {
23661                     tag : 'label',
23662                     cls : 'box-label',
23663                     html : this.boxLabel
23664                 }
23665             ]
23666         };
23667         
23668         return cfg;
23669     },
23670     
23671     initEvents : function() 
23672     {
23673         this.parent().register(this);
23674         
23675         this.el.on('click', this.onClick, this);
23676         
23677     },
23678     
23679     onClick : function(e)
23680     {
23681         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23682             this.setChecked(true);
23683         }
23684     },
23685     
23686     setChecked : function(state, suppressEvent)
23687     {
23688         this.parent().setValue(this.value, suppressEvent);
23689         
23690     },
23691     
23692     setBoxLabel : function(v)
23693     {
23694         this.boxLabel = v;
23695         
23696         if(this.rendered){
23697             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23698         }
23699     }
23700     
23701 });
23702  
23703
23704  /*
23705  * - LGPL
23706  *
23707  * Input
23708  * 
23709  */
23710
23711 /**
23712  * @class Roo.bootstrap.SecurePass
23713  * @extends Roo.bootstrap.Input
23714  * Bootstrap SecurePass class
23715  *
23716  * 
23717  * @constructor
23718  * Create a new SecurePass
23719  * @param {Object} config The config object
23720  */
23721  
23722 Roo.bootstrap.SecurePass = function (config) {
23723     // these go here, so the translation tool can replace them..
23724     this.errors = {
23725         PwdEmpty: "Please type a password, and then retype it to confirm.",
23726         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23727         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23728         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23729         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23730         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23731         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23732         TooWeak: "Your password is Too Weak."
23733     },
23734     this.meterLabel = "Password strength:";
23735     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23736     this.meterClass = [
23737         "roo-password-meter-tooweak", 
23738         "roo-password-meter-weak", 
23739         "roo-password-meter-medium", 
23740         "roo-password-meter-strong", 
23741         "roo-password-meter-grey"
23742     ];
23743     
23744     this.errors = {};
23745     
23746     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23747 }
23748
23749 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23750     /**
23751      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23752      * {
23753      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23754      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23755      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23756      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23757      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23758      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23759      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23760      * })
23761      */
23762     // private
23763     
23764     meterWidth: 300,
23765     errorMsg :'',    
23766     errors: false,
23767     imageRoot: '/',
23768     /**
23769      * @cfg {String/Object} Label for the strength meter (defaults to
23770      * 'Password strength:')
23771      */
23772     // private
23773     meterLabel: '',
23774     /**
23775      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23776      * ['Weak', 'Medium', 'Strong'])
23777      */
23778     // private    
23779     pwdStrengths: false,    
23780     // private
23781     strength: 0,
23782     // private
23783     _lastPwd: null,
23784     // private
23785     kCapitalLetter: 0,
23786     kSmallLetter: 1,
23787     kDigit: 2,
23788     kPunctuation: 3,
23789     
23790     insecure: false,
23791     // private
23792     initEvents: function ()
23793     {
23794         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23795
23796         if (this.el.is('input[type=password]') && Roo.isSafari) {
23797             this.el.on('keydown', this.SafariOnKeyDown, this);
23798         }
23799
23800         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23801     },
23802     // private
23803     onRender: function (ct, position)
23804     {
23805         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23806         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23807         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23808
23809         this.trigger.createChild({
23810                    cn: [
23811                     {
23812                     //id: 'PwdMeter',
23813                     tag: 'div',
23814                     cls: 'roo-password-meter-grey col-xs-12',
23815                     style: {
23816                         //width: 0,
23817                         //width: this.meterWidth + 'px'                                                
23818                         }
23819                     },
23820                     {                            
23821                          cls: 'roo-password-meter-text'                          
23822                     }
23823                 ]            
23824         });
23825
23826          
23827         if (this.hideTrigger) {
23828             this.trigger.setDisplayed(false);
23829         }
23830         this.setSize(this.width || '', this.height || '');
23831     },
23832     // private
23833     onDestroy: function ()
23834     {
23835         if (this.trigger) {
23836             this.trigger.removeAllListeners();
23837             this.trigger.remove();
23838         }
23839         if (this.wrap) {
23840             this.wrap.remove();
23841         }
23842         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23843     },
23844     // private
23845     checkStrength: function ()
23846     {
23847         var pwd = this.inputEl().getValue();
23848         if (pwd == this._lastPwd) {
23849             return;
23850         }
23851
23852         var strength;
23853         if (this.ClientSideStrongPassword(pwd)) {
23854             strength = 3;
23855         } else if (this.ClientSideMediumPassword(pwd)) {
23856             strength = 2;
23857         } else if (this.ClientSideWeakPassword(pwd)) {
23858             strength = 1;
23859         } else {
23860             strength = 0;
23861         }
23862         
23863         Roo.log('strength1: ' + strength);
23864         
23865         //var pm = this.trigger.child('div/div/div').dom;
23866         var pm = this.trigger.child('div/div');
23867         pm.removeClass(this.meterClass);
23868         pm.addClass(this.meterClass[strength]);
23869                 
23870         
23871         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23872                 
23873         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23874         
23875         this._lastPwd = pwd;
23876     },
23877     reset: function ()
23878     {
23879         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23880         
23881         this._lastPwd = '';
23882         
23883         var pm = this.trigger.child('div/div');
23884         pm.removeClass(this.meterClass);
23885         pm.addClass('roo-password-meter-grey');        
23886         
23887         
23888         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23889         
23890         pt.innerHTML = '';
23891         this.inputEl().dom.type='password';
23892     },
23893     // private
23894     validateValue: function (value)
23895     {
23896         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23897             return false;
23898         }
23899         if (value.length == 0) {
23900             if (this.allowBlank) {
23901                 this.clearInvalid();
23902                 return true;
23903             }
23904
23905             this.markInvalid(this.errors.PwdEmpty);
23906             this.errorMsg = this.errors.PwdEmpty;
23907             return false;
23908         }
23909         
23910         if(this.insecure){
23911             return true;
23912         }
23913         
23914         if (!value.match(/[\x21-\x7e]+/)) {
23915             this.markInvalid(this.errors.PwdBadChar);
23916             this.errorMsg = this.errors.PwdBadChar;
23917             return false;
23918         }
23919         if (value.length < 6) {
23920             this.markInvalid(this.errors.PwdShort);
23921             this.errorMsg = this.errors.PwdShort;
23922             return false;
23923         }
23924         if (value.length > 16) {
23925             this.markInvalid(this.errors.PwdLong);
23926             this.errorMsg = this.errors.PwdLong;
23927             return false;
23928         }
23929         var strength;
23930         if (this.ClientSideStrongPassword(value)) {
23931             strength = 3;
23932         } else if (this.ClientSideMediumPassword(value)) {
23933             strength = 2;
23934         } else if (this.ClientSideWeakPassword(value)) {
23935             strength = 1;
23936         } else {
23937             strength = 0;
23938         }
23939
23940         
23941         if (strength < 2) {
23942             //this.markInvalid(this.errors.TooWeak);
23943             this.errorMsg = this.errors.TooWeak;
23944             //return false;
23945         }
23946         
23947         
23948         console.log('strength2: ' + strength);
23949         
23950         //var pm = this.trigger.child('div/div/div').dom;
23951         
23952         var pm = this.trigger.child('div/div');
23953         pm.removeClass(this.meterClass);
23954         pm.addClass(this.meterClass[strength]);
23955                 
23956         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23957                 
23958         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23959         
23960         this.errorMsg = ''; 
23961         return true;
23962     },
23963     // private
23964     CharacterSetChecks: function (type)
23965     {
23966         this.type = type;
23967         this.fResult = false;
23968     },
23969     // private
23970     isctype: function (character, type)
23971     {
23972         switch (type) {  
23973             case this.kCapitalLetter:
23974                 if (character >= 'A' && character <= 'Z') {
23975                     return true;
23976                 }
23977                 break;
23978             
23979             case this.kSmallLetter:
23980                 if (character >= 'a' && character <= 'z') {
23981                     return true;
23982                 }
23983                 break;
23984             
23985             case this.kDigit:
23986                 if (character >= '0' && character <= '9') {
23987                     return true;
23988                 }
23989                 break;
23990             
23991             case this.kPunctuation:
23992                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23993                     return true;
23994                 }
23995                 break;
23996             
23997             default:
23998                 return false;
23999         }
24000
24001     },
24002     // private
24003     IsLongEnough: function (pwd, size)
24004     {
24005         return !(pwd == null || isNaN(size) || pwd.length < size);
24006     },
24007     // private
24008     SpansEnoughCharacterSets: function (word, nb)
24009     {
24010         if (!this.IsLongEnough(word, nb))
24011         {
24012             return false;
24013         }
24014
24015         var characterSetChecks = new Array(
24016             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24017             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24018         );
24019         
24020         for (var index = 0; index < word.length; ++index) {
24021             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24022                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24023                     characterSetChecks[nCharSet].fResult = true;
24024                     break;
24025                 }
24026             }
24027         }
24028
24029         var nCharSets = 0;
24030         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24031             if (characterSetChecks[nCharSet].fResult) {
24032                 ++nCharSets;
24033             }
24034         }
24035
24036         if (nCharSets < nb) {
24037             return false;
24038         }
24039         return true;
24040     },
24041     // private
24042     ClientSideStrongPassword: function (pwd)
24043     {
24044         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24045     },
24046     // private
24047     ClientSideMediumPassword: function (pwd)
24048     {
24049         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24050     },
24051     // private
24052     ClientSideWeakPassword: function (pwd)
24053     {
24054         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24055     }
24056           
24057 })//<script type="text/javascript">
24058
24059 /*
24060  * Based  Ext JS Library 1.1.1
24061  * Copyright(c) 2006-2007, Ext JS, LLC.
24062  * LGPL
24063  *
24064  */
24065  
24066 /**
24067  * @class Roo.HtmlEditorCore
24068  * @extends Roo.Component
24069  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24070  *
24071  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24072  */
24073
24074 Roo.HtmlEditorCore = function(config){
24075     
24076     
24077     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24078     
24079     
24080     this.addEvents({
24081         /**
24082          * @event initialize
24083          * Fires when the editor is fully initialized (including the iframe)
24084          * @param {Roo.HtmlEditorCore} this
24085          */
24086         initialize: true,
24087         /**
24088          * @event activate
24089          * Fires when the editor is first receives the focus. Any insertion must wait
24090          * until after this event.
24091          * @param {Roo.HtmlEditorCore} this
24092          */
24093         activate: true,
24094          /**
24095          * @event beforesync
24096          * Fires before the textarea is updated with content from the editor iframe. Return false
24097          * to cancel the sync.
24098          * @param {Roo.HtmlEditorCore} this
24099          * @param {String} html
24100          */
24101         beforesync: true,
24102          /**
24103          * @event beforepush
24104          * Fires before the iframe editor is updated with content from the textarea. Return false
24105          * to cancel the push.
24106          * @param {Roo.HtmlEditorCore} this
24107          * @param {String} html
24108          */
24109         beforepush: true,
24110          /**
24111          * @event sync
24112          * Fires when the textarea is updated with content from the editor iframe.
24113          * @param {Roo.HtmlEditorCore} this
24114          * @param {String} html
24115          */
24116         sync: true,
24117          /**
24118          * @event push
24119          * Fires when the iframe editor is updated with content from the textarea.
24120          * @param {Roo.HtmlEditorCore} this
24121          * @param {String} html
24122          */
24123         push: true,
24124         
24125         /**
24126          * @event editorevent
24127          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24128          * @param {Roo.HtmlEditorCore} this
24129          */
24130         editorevent: true
24131         
24132     });
24133     
24134     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24135     
24136     // defaults : white / black...
24137     this.applyBlacklists();
24138     
24139     
24140     
24141 };
24142
24143
24144 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24145
24146
24147      /**
24148      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24149      */
24150     
24151     owner : false,
24152     
24153      /**
24154      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24155      *                        Roo.resizable.
24156      */
24157     resizable : false,
24158      /**
24159      * @cfg {Number} height (in pixels)
24160      */   
24161     height: 300,
24162    /**
24163      * @cfg {Number} width (in pixels)
24164      */   
24165     width: 500,
24166     
24167     /**
24168      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24169      * 
24170      */
24171     stylesheets: false,
24172     
24173     // id of frame..
24174     frameId: false,
24175     
24176     // private properties
24177     validationEvent : false,
24178     deferHeight: true,
24179     initialized : false,
24180     activated : false,
24181     sourceEditMode : false,
24182     onFocus : Roo.emptyFn,
24183     iframePad:3,
24184     hideMode:'offsets',
24185     
24186     clearUp: true,
24187     
24188     // blacklist + whitelisted elements..
24189     black: false,
24190     white: false,
24191      
24192     bodyCls : '',
24193
24194     /**
24195      * Protected method that will not generally be called directly. It
24196      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24197      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24198      */
24199     getDocMarkup : function(){
24200         // body styles..
24201         var st = '';
24202         
24203         // inherit styels from page...?? 
24204         if (this.stylesheets === false) {
24205             
24206             Roo.get(document.head).select('style').each(function(node) {
24207                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24208             });
24209             
24210             Roo.get(document.head).select('link').each(function(node) { 
24211                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24212             });
24213             
24214         } else if (!this.stylesheets.length) {
24215                 // simple..
24216                 st = '<style type="text/css">' +
24217                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24218                    '</style>';
24219         } else {
24220             for (var i in this.stylesheets) { 
24221                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24222             }
24223             
24224         }
24225         
24226         st +=  '<style type="text/css">' +
24227             'IMG { cursor: pointer } ' +
24228         '</style>';
24229
24230         var cls = 'roo-htmleditor-body';
24231         
24232         if(this.bodyCls.length){
24233             cls += ' ' + this.bodyCls;
24234         }
24235         
24236         return '<html><head>' + st  +
24237             //<style type="text/css">' +
24238             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24239             //'</style>' +
24240             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24241     },
24242
24243     // private
24244     onRender : function(ct, position)
24245     {
24246         var _t = this;
24247         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24248         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24249         
24250         
24251         this.el.dom.style.border = '0 none';
24252         this.el.dom.setAttribute('tabIndex', -1);
24253         this.el.addClass('x-hidden hide');
24254         
24255         
24256         
24257         if(Roo.isIE){ // fix IE 1px bogus margin
24258             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24259         }
24260        
24261         
24262         this.frameId = Roo.id();
24263         
24264          
24265         
24266         var iframe = this.owner.wrap.createChild({
24267             tag: 'iframe',
24268             cls: 'form-control', // bootstrap..
24269             id: this.frameId,
24270             name: this.frameId,
24271             frameBorder : 'no',
24272             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24273         }, this.el
24274         );
24275         
24276         
24277         this.iframe = iframe.dom;
24278
24279          this.assignDocWin();
24280         
24281         this.doc.designMode = 'on';
24282        
24283         this.doc.open();
24284         this.doc.write(this.getDocMarkup());
24285         this.doc.close();
24286
24287         
24288         var task = { // must defer to wait for browser to be ready
24289             run : function(){
24290                 //console.log("run task?" + this.doc.readyState);
24291                 this.assignDocWin();
24292                 if(this.doc.body || this.doc.readyState == 'complete'){
24293                     try {
24294                         this.doc.designMode="on";
24295                     } catch (e) {
24296                         return;
24297                     }
24298                     Roo.TaskMgr.stop(task);
24299                     this.initEditor.defer(10, this);
24300                 }
24301             },
24302             interval : 10,
24303             duration: 10000,
24304             scope: this
24305         };
24306         Roo.TaskMgr.start(task);
24307
24308     },
24309
24310     // private
24311     onResize : function(w, h)
24312     {
24313          Roo.log('resize: ' +w + ',' + h );
24314         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24315         if(!this.iframe){
24316             return;
24317         }
24318         if(typeof w == 'number'){
24319             
24320             this.iframe.style.width = w + 'px';
24321         }
24322         if(typeof h == 'number'){
24323             
24324             this.iframe.style.height = h + 'px';
24325             if(this.doc){
24326                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24327             }
24328         }
24329         
24330     },
24331
24332     /**
24333      * Toggles the editor between standard and source edit mode.
24334      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24335      */
24336     toggleSourceEdit : function(sourceEditMode){
24337         
24338         this.sourceEditMode = sourceEditMode === true;
24339         
24340         if(this.sourceEditMode){
24341  
24342             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24343             
24344         }else{
24345             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24346             //this.iframe.className = '';
24347             this.deferFocus();
24348         }
24349         //this.setSize(this.owner.wrap.getSize());
24350         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24351     },
24352
24353     
24354   
24355
24356     /**
24357      * Protected method that will not generally be called directly. If you need/want
24358      * custom HTML cleanup, this is the method you should override.
24359      * @param {String} html The HTML to be cleaned
24360      * return {String} The cleaned HTML
24361      */
24362     cleanHtml : function(html){
24363         html = String(html);
24364         if(html.length > 5){
24365             if(Roo.isSafari){ // strip safari nonsense
24366                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24367             }
24368         }
24369         if(html == '&nbsp;'){
24370             html = '';
24371         }
24372         return html;
24373     },
24374
24375     /**
24376      * HTML Editor -> Textarea
24377      * Protected method that will not generally be called directly. Syncs the contents
24378      * of the editor iframe with the textarea.
24379      */
24380     syncValue : function(){
24381         if(this.initialized){
24382             var bd = (this.doc.body || this.doc.documentElement);
24383             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24384             var html = bd.innerHTML;
24385             if(Roo.isSafari){
24386                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24387                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24388                 if(m && m[1]){
24389                     html = '<div style="'+m[0]+'">' + html + '</div>';
24390                 }
24391             }
24392             html = this.cleanHtml(html);
24393             // fix up the special chars.. normaly like back quotes in word...
24394             // however we do not want to do this with chinese..
24395             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24396                 
24397                 var cc = match.charCodeAt();
24398
24399                 // Get the character value, handling surrogate pairs
24400                 if (match.length == 2) {
24401                     // It's a surrogate pair, calculate the Unicode code point
24402                     var high = match.charCodeAt(0) - 0xD800;
24403                     var low  = match.charCodeAt(1) - 0xDC00;
24404                     cc = (high * 0x400) + low + 0x10000;
24405                 }  else if (
24406                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24407                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24408                     (cc >= 0xf900 && cc < 0xfb00 )
24409                 ) {
24410                         return match;
24411                 }  
24412          
24413                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24414                 return "&#" + cc + ";";
24415                 
24416                 
24417             });
24418             
24419             
24420              
24421             if(this.owner.fireEvent('beforesync', this, html) !== false){
24422                 this.el.dom.value = html;
24423                 this.owner.fireEvent('sync', this, html);
24424             }
24425         }
24426     },
24427
24428     /**
24429      * Protected method that will not generally be called directly. Pushes the value of the textarea
24430      * into the iframe editor.
24431      */
24432     pushValue : function(){
24433         if(this.initialized){
24434             var v = this.el.dom.value.trim();
24435             
24436 //            if(v.length < 1){
24437 //                v = '&#160;';
24438 //            }
24439             
24440             if(this.owner.fireEvent('beforepush', this, v) !== false){
24441                 var d = (this.doc.body || this.doc.documentElement);
24442                 d.innerHTML = v;
24443                 this.cleanUpPaste();
24444                 this.el.dom.value = d.innerHTML;
24445                 this.owner.fireEvent('push', this, v);
24446             }
24447         }
24448     },
24449
24450     // private
24451     deferFocus : function(){
24452         this.focus.defer(10, this);
24453     },
24454
24455     // doc'ed in Field
24456     focus : function(){
24457         if(this.win && !this.sourceEditMode){
24458             this.win.focus();
24459         }else{
24460             this.el.focus();
24461         }
24462     },
24463     
24464     assignDocWin: function()
24465     {
24466         var iframe = this.iframe;
24467         
24468          if(Roo.isIE){
24469             this.doc = iframe.contentWindow.document;
24470             this.win = iframe.contentWindow;
24471         } else {
24472 //            if (!Roo.get(this.frameId)) {
24473 //                return;
24474 //            }
24475 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24476 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24477             
24478             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24479                 return;
24480             }
24481             
24482             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24483             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24484         }
24485     },
24486     
24487     // private
24488     initEditor : function(){
24489         //console.log("INIT EDITOR");
24490         this.assignDocWin();
24491         
24492         
24493         
24494         this.doc.designMode="on";
24495         this.doc.open();
24496         this.doc.write(this.getDocMarkup());
24497         this.doc.close();
24498         
24499         var dbody = (this.doc.body || this.doc.documentElement);
24500         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24501         // this copies styles from the containing element into thsi one..
24502         // not sure why we need all of this..
24503         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24504         
24505         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24506         //ss['background-attachment'] = 'fixed'; // w3c
24507         dbody.bgProperties = 'fixed'; // ie
24508         //Roo.DomHelper.applyStyles(dbody, ss);
24509         Roo.EventManager.on(this.doc, {
24510             //'mousedown': this.onEditorEvent,
24511             'mouseup': this.onEditorEvent,
24512             'dblclick': this.onEditorEvent,
24513             'click': this.onEditorEvent,
24514             'keyup': this.onEditorEvent,
24515             buffer:100,
24516             scope: this
24517         });
24518         if(Roo.isGecko){
24519             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24520         }
24521         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24522             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24523         }
24524         this.initialized = true;
24525
24526         this.owner.fireEvent('initialize', this);
24527         this.pushValue();
24528     },
24529
24530     // private
24531     onDestroy : function(){
24532         
24533         
24534         
24535         if(this.rendered){
24536             
24537             //for (var i =0; i < this.toolbars.length;i++) {
24538             //    // fixme - ask toolbars for heights?
24539             //    this.toolbars[i].onDestroy();
24540            // }
24541             
24542             //this.wrap.dom.innerHTML = '';
24543             //this.wrap.remove();
24544         }
24545     },
24546
24547     // private
24548     onFirstFocus : function(){
24549         
24550         this.assignDocWin();
24551         
24552         
24553         this.activated = true;
24554          
24555     
24556         if(Roo.isGecko){ // prevent silly gecko errors
24557             this.win.focus();
24558             var s = this.win.getSelection();
24559             if(!s.focusNode || s.focusNode.nodeType != 3){
24560                 var r = s.getRangeAt(0);
24561                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24562                 r.collapse(true);
24563                 this.deferFocus();
24564             }
24565             try{
24566                 this.execCmd('useCSS', true);
24567                 this.execCmd('styleWithCSS', false);
24568             }catch(e){}
24569         }
24570         this.owner.fireEvent('activate', this);
24571     },
24572
24573     // private
24574     adjustFont: function(btn){
24575         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24576         //if(Roo.isSafari){ // safari
24577         //    adjust *= 2;
24578        // }
24579         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24580         if(Roo.isSafari){ // safari
24581             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24582             v =  (v < 10) ? 10 : v;
24583             v =  (v > 48) ? 48 : v;
24584             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24585             
24586         }
24587         
24588         
24589         v = Math.max(1, v+adjust);
24590         
24591         this.execCmd('FontSize', v  );
24592     },
24593
24594     onEditorEvent : function(e)
24595     {
24596         this.owner.fireEvent('editorevent', this, e);
24597       //  this.updateToolbar();
24598         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24599     },
24600
24601     insertTag : function(tg)
24602     {
24603         // could be a bit smarter... -> wrap the current selected tRoo..
24604         if (tg.toLowerCase() == 'span' ||
24605             tg.toLowerCase() == 'code' ||
24606             tg.toLowerCase() == 'sup' ||
24607             tg.toLowerCase() == 'sub' 
24608             ) {
24609             
24610             range = this.createRange(this.getSelection());
24611             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24612             wrappingNode.appendChild(range.extractContents());
24613             range.insertNode(wrappingNode);
24614
24615             return;
24616             
24617             
24618             
24619         }
24620         this.execCmd("formatblock",   tg);
24621         
24622     },
24623     
24624     insertText : function(txt)
24625     {
24626         
24627         
24628         var range = this.createRange();
24629         range.deleteContents();
24630                //alert(Sender.getAttribute('label'));
24631                
24632         range.insertNode(this.doc.createTextNode(txt));
24633     } ,
24634     
24635      
24636
24637     /**
24638      * Executes a Midas editor command on the editor document and performs necessary focus and
24639      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24640      * @param {String} cmd The Midas command
24641      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24642      */
24643     relayCmd : function(cmd, value){
24644         this.win.focus();
24645         this.execCmd(cmd, value);
24646         this.owner.fireEvent('editorevent', this);
24647         //this.updateToolbar();
24648         this.owner.deferFocus();
24649     },
24650
24651     /**
24652      * Executes a Midas editor command directly on the editor document.
24653      * For visual commands, you should use {@link #relayCmd} instead.
24654      * <b>This should only be called after the editor is initialized.</b>
24655      * @param {String} cmd The Midas command
24656      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24657      */
24658     execCmd : function(cmd, value){
24659         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24660         this.syncValue();
24661     },
24662  
24663  
24664    
24665     /**
24666      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24667      * to insert tRoo.
24668      * @param {String} text | dom node.. 
24669      */
24670     insertAtCursor : function(text)
24671     {
24672         
24673         if(!this.activated){
24674             return;
24675         }
24676         /*
24677         if(Roo.isIE){
24678             this.win.focus();
24679             var r = this.doc.selection.createRange();
24680             if(r){
24681                 r.collapse(true);
24682                 r.pasteHTML(text);
24683                 this.syncValue();
24684                 this.deferFocus();
24685             
24686             }
24687             return;
24688         }
24689         */
24690         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24691             this.win.focus();
24692             
24693             
24694             // from jquery ui (MIT licenced)
24695             var range, node;
24696             var win = this.win;
24697             
24698             if (win.getSelection && win.getSelection().getRangeAt) {
24699                 range = win.getSelection().getRangeAt(0);
24700                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24701                 range.insertNode(node);
24702             } else if (win.document.selection && win.document.selection.createRange) {
24703                 // no firefox support
24704                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24705                 win.document.selection.createRange().pasteHTML(txt);
24706             } else {
24707                 // no firefox support
24708                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24709                 this.execCmd('InsertHTML', txt);
24710             } 
24711             
24712             this.syncValue();
24713             
24714             this.deferFocus();
24715         }
24716     },
24717  // private
24718     mozKeyPress : function(e){
24719         if(e.ctrlKey){
24720             var c = e.getCharCode(), cmd;
24721           
24722             if(c > 0){
24723                 c = String.fromCharCode(c).toLowerCase();
24724                 switch(c){
24725                     case 'b':
24726                         cmd = 'bold';
24727                         break;
24728                     case 'i':
24729                         cmd = 'italic';
24730                         break;
24731                     
24732                     case 'u':
24733                         cmd = 'underline';
24734                         break;
24735                     
24736                     case 'v':
24737                         this.cleanUpPaste.defer(100, this);
24738                         return;
24739                         
24740                 }
24741                 if(cmd){
24742                     this.win.focus();
24743                     this.execCmd(cmd);
24744                     this.deferFocus();
24745                     e.preventDefault();
24746                 }
24747                 
24748             }
24749         }
24750     },
24751
24752     // private
24753     fixKeys : function(){ // load time branching for fastest keydown performance
24754         if(Roo.isIE){
24755             return function(e){
24756                 var k = e.getKey(), r;
24757                 if(k == e.TAB){
24758                     e.stopEvent();
24759                     r = this.doc.selection.createRange();
24760                     if(r){
24761                         r.collapse(true);
24762                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24763                         this.deferFocus();
24764                     }
24765                     return;
24766                 }
24767                 
24768                 if(k == e.ENTER){
24769                     r = this.doc.selection.createRange();
24770                     if(r){
24771                         var target = r.parentElement();
24772                         if(!target || target.tagName.toLowerCase() != 'li'){
24773                             e.stopEvent();
24774                             r.pasteHTML('<br />');
24775                             r.collapse(false);
24776                             r.select();
24777                         }
24778                     }
24779                 }
24780                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24781                     this.cleanUpPaste.defer(100, this);
24782                     return;
24783                 }
24784                 
24785                 
24786             };
24787         }else if(Roo.isOpera){
24788             return function(e){
24789                 var k = e.getKey();
24790                 if(k == e.TAB){
24791                     e.stopEvent();
24792                     this.win.focus();
24793                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24794                     this.deferFocus();
24795                 }
24796                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24797                     this.cleanUpPaste.defer(100, this);
24798                     return;
24799                 }
24800                 
24801             };
24802         }else if(Roo.isSafari){
24803             return function(e){
24804                 var k = e.getKey();
24805                 
24806                 if(k == e.TAB){
24807                     e.stopEvent();
24808                     this.execCmd('InsertText','\t');
24809                     this.deferFocus();
24810                     return;
24811                 }
24812                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24813                     this.cleanUpPaste.defer(100, this);
24814                     return;
24815                 }
24816                 
24817              };
24818         }
24819     }(),
24820     
24821     getAllAncestors: function()
24822     {
24823         var p = this.getSelectedNode();
24824         var a = [];
24825         if (!p) {
24826             a.push(p); // push blank onto stack..
24827             p = this.getParentElement();
24828         }
24829         
24830         
24831         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24832             a.push(p);
24833             p = p.parentNode;
24834         }
24835         a.push(this.doc.body);
24836         return a;
24837     },
24838     lastSel : false,
24839     lastSelNode : false,
24840     
24841     
24842     getSelection : function() 
24843     {
24844         this.assignDocWin();
24845         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24846     },
24847     
24848     getSelectedNode: function() 
24849     {
24850         // this may only work on Gecko!!!
24851         
24852         // should we cache this!!!!
24853         
24854         
24855         
24856          
24857         var range = this.createRange(this.getSelection()).cloneRange();
24858         
24859         if (Roo.isIE) {
24860             var parent = range.parentElement();
24861             while (true) {
24862                 var testRange = range.duplicate();
24863                 testRange.moveToElementText(parent);
24864                 if (testRange.inRange(range)) {
24865                     break;
24866                 }
24867                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24868                     break;
24869                 }
24870                 parent = parent.parentElement;
24871             }
24872             return parent;
24873         }
24874         
24875         // is ancestor a text element.
24876         var ac =  range.commonAncestorContainer;
24877         if (ac.nodeType == 3) {
24878             ac = ac.parentNode;
24879         }
24880         
24881         var ar = ac.childNodes;
24882          
24883         var nodes = [];
24884         var other_nodes = [];
24885         var has_other_nodes = false;
24886         for (var i=0;i<ar.length;i++) {
24887             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24888                 continue;
24889             }
24890             // fullly contained node.
24891             
24892             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24893                 nodes.push(ar[i]);
24894                 continue;
24895             }
24896             
24897             // probably selected..
24898             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24899                 other_nodes.push(ar[i]);
24900                 continue;
24901             }
24902             // outer..
24903             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24904                 continue;
24905             }
24906             
24907             
24908             has_other_nodes = true;
24909         }
24910         if (!nodes.length && other_nodes.length) {
24911             nodes= other_nodes;
24912         }
24913         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24914             return false;
24915         }
24916         
24917         return nodes[0];
24918     },
24919     createRange: function(sel)
24920     {
24921         // this has strange effects when using with 
24922         // top toolbar - not sure if it's a great idea.
24923         //this.editor.contentWindow.focus();
24924         if (typeof sel != "undefined") {
24925             try {
24926                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24927             } catch(e) {
24928                 return this.doc.createRange();
24929             }
24930         } else {
24931             return this.doc.createRange();
24932         }
24933     },
24934     getParentElement: function()
24935     {
24936         
24937         this.assignDocWin();
24938         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24939         
24940         var range = this.createRange(sel);
24941          
24942         try {
24943             var p = range.commonAncestorContainer;
24944             while (p.nodeType == 3) { // text node
24945                 p = p.parentNode;
24946             }
24947             return p;
24948         } catch (e) {
24949             return null;
24950         }
24951     
24952     },
24953     /***
24954      *
24955      * Range intersection.. the hard stuff...
24956      *  '-1' = before
24957      *  '0' = hits..
24958      *  '1' = after.
24959      *         [ -- selected range --- ]
24960      *   [fail]                        [fail]
24961      *
24962      *    basically..
24963      *      if end is before start or  hits it. fail.
24964      *      if start is after end or hits it fail.
24965      *
24966      *   if either hits (but other is outside. - then it's not 
24967      *   
24968      *    
24969      **/
24970     
24971     
24972     // @see http://www.thismuchiknow.co.uk/?p=64.
24973     rangeIntersectsNode : function(range, node)
24974     {
24975         var nodeRange = node.ownerDocument.createRange();
24976         try {
24977             nodeRange.selectNode(node);
24978         } catch (e) {
24979             nodeRange.selectNodeContents(node);
24980         }
24981     
24982         var rangeStartRange = range.cloneRange();
24983         rangeStartRange.collapse(true);
24984     
24985         var rangeEndRange = range.cloneRange();
24986         rangeEndRange.collapse(false);
24987     
24988         var nodeStartRange = nodeRange.cloneRange();
24989         nodeStartRange.collapse(true);
24990     
24991         var nodeEndRange = nodeRange.cloneRange();
24992         nodeEndRange.collapse(false);
24993     
24994         return rangeStartRange.compareBoundaryPoints(
24995                  Range.START_TO_START, nodeEndRange) == -1 &&
24996                rangeEndRange.compareBoundaryPoints(
24997                  Range.START_TO_START, nodeStartRange) == 1;
24998         
24999          
25000     },
25001     rangeCompareNode : function(range, node)
25002     {
25003         var nodeRange = node.ownerDocument.createRange();
25004         try {
25005             nodeRange.selectNode(node);
25006         } catch (e) {
25007             nodeRange.selectNodeContents(node);
25008         }
25009         
25010         
25011         range.collapse(true);
25012     
25013         nodeRange.collapse(true);
25014      
25015         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25016         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25017          
25018         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25019         
25020         var nodeIsBefore   =  ss == 1;
25021         var nodeIsAfter    = ee == -1;
25022         
25023         if (nodeIsBefore && nodeIsAfter) {
25024             return 0; // outer
25025         }
25026         if (!nodeIsBefore && nodeIsAfter) {
25027             return 1; //right trailed.
25028         }
25029         
25030         if (nodeIsBefore && !nodeIsAfter) {
25031             return 2;  // left trailed.
25032         }
25033         // fully contined.
25034         return 3;
25035     },
25036
25037     // private? - in a new class?
25038     cleanUpPaste :  function()
25039     {
25040         // cleans up the whole document..
25041         Roo.log('cleanuppaste');
25042         
25043         this.cleanUpChildren(this.doc.body);
25044         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25045         if (clean != this.doc.body.innerHTML) {
25046             this.doc.body.innerHTML = clean;
25047         }
25048         
25049     },
25050     
25051     cleanWordChars : function(input) {// change the chars to hex code
25052         var he = Roo.HtmlEditorCore;
25053         
25054         var output = input;
25055         Roo.each(he.swapCodes, function(sw) { 
25056             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25057             
25058             output = output.replace(swapper, sw[1]);
25059         });
25060         
25061         return output;
25062     },
25063     
25064     
25065     cleanUpChildren : function (n)
25066     {
25067         if (!n.childNodes.length) {
25068             return;
25069         }
25070         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25071            this.cleanUpChild(n.childNodes[i]);
25072         }
25073     },
25074     
25075     
25076         
25077     
25078     cleanUpChild : function (node)
25079     {
25080         var ed = this;
25081         //console.log(node);
25082         if (node.nodeName == "#text") {
25083             // clean up silly Windows -- stuff?
25084             return; 
25085         }
25086         if (node.nodeName == "#comment") {
25087             node.parentNode.removeChild(node);
25088             // clean up silly Windows -- stuff?
25089             return; 
25090         }
25091         var lcname = node.tagName.toLowerCase();
25092         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25093         // whitelist of tags..
25094         
25095         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25096             // remove node.
25097             node.parentNode.removeChild(node);
25098             return;
25099             
25100         }
25101         
25102         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25103         
25104         // spans with no attributes - just remove them..
25105         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25106             remove_keep_children = true;
25107         }
25108         
25109         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25110         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25111         
25112         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25113         //    remove_keep_children = true;
25114         //}
25115         
25116         if (remove_keep_children) {
25117             this.cleanUpChildren(node);
25118             // inserts everything just before this node...
25119             while (node.childNodes.length) {
25120                 var cn = node.childNodes[0];
25121                 node.removeChild(cn);
25122                 node.parentNode.insertBefore(cn, node);
25123             }
25124             node.parentNode.removeChild(node);
25125             return;
25126         }
25127         
25128         if (!node.attributes || !node.attributes.length) {
25129             
25130           
25131             
25132             
25133             this.cleanUpChildren(node);
25134             return;
25135         }
25136         
25137         function cleanAttr(n,v)
25138         {
25139             
25140             if (v.match(/^\./) || v.match(/^\//)) {
25141                 return;
25142             }
25143             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25144                 return;
25145             }
25146             if (v.match(/^#/)) {
25147                 return;
25148             }
25149             if (v.match(/^\{/)) { // allow template editing.
25150                 return;
25151             }
25152 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25153             node.removeAttribute(n);
25154             
25155         }
25156         
25157         var cwhite = this.cwhite;
25158         var cblack = this.cblack;
25159             
25160         function cleanStyle(n,v)
25161         {
25162             if (v.match(/expression/)) { //XSS?? should we even bother..
25163                 node.removeAttribute(n);
25164                 return;
25165             }
25166             
25167             var parts = v.split(/;/);
25168             var clean = [];
25169             
25170             Roo.each(parts, function(p) {
25171                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25172                 if (!p.length) {
25173                     return true;
25174                 }
25175                 var l = p.split(':').shift().replace(/\s+/g,'');
25176                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25177                 
25178                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25179 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25180                     //node.removeAttribute(n);
25181                     return true;
25182                 }
25183                 //Roo.log()
25184                 // only allow 'c whitelisted system attributes'
25185                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25186 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25187                     //node.removeAttribute(n);
25188                     return true;
25189                 }
25190                 
25191                 
25192                  
25193                 
25194                 clean.push(p);
25195                 return true;
25196             });
25197             if (clean.length) { 
25198                 node.setAttribute(n, clean.join(';'));
25199             } else {
25200                 node.removeAttribute(n);
25201             }
25202             
25203         }
25204         
25205         
25206         for (var i = node.attributes.length-1; i > -1 ; i--) {
25207             var a = node.attributes[i];
25208             //console.log(a);
25209             
25210             if (a.name.toLowerCase().substr(0,2)=='on')  {
25211                 node.removeAttribute(a.name);
25212                 continue;
25213             }
25214             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25215                 node.removeAttribute(a.name);
25216                 continue;
25217             }
25218             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25219                 cleanAttr(a.name,a.value); // fixme..
25220                 continue;
25221             }
25222             if (a.name == 'style') {
25223                 cleanStyle(a.name,a.value);
25224                 continue;
25225             }
25226             /// clean up MS crap..
25227             // tecnically this should be a list of valid class'es..
25228             
25229             
25230             if (a.name == 'class') {
25231                 if (a.value.match(/^Mso/)) {
25232                     node.removeAttribute('class');
25233                 }
25234                 
25235                 if (a.value.match(/^body$/)) {
25236                     node.removeAttribute('class');
25237                 }
25238                 continue;
25239             }
25240             
25241             // style cleanup!?
25242             // class cleanup?
25243             
25244         }
25245         
25246         
25247         this.cleanUpChildren(node);
25248         
25249         
25250     },
25251     
25252     /**
25253      * Clean up MS wordisms...
25254      */
25255     cleanWord : function(node)
25256     {
25257         if (!node) {
25258             this.cleanWord(this.doc.body);
25259             return;
25260         }
25261         
25262         if(
25263                 node.nodeName == 'SPAN' &&
25264                 !node.hasAttributes() &&
25265                 node.childNodes.length == 1 &&
25266                 node.firstChild.nodeName == "#text"  
25267         ) {
25268             var textNode = node.firstChild;
25269             node.removeChild(textNode);
25270             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25271                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25272             }
25273             node.parentNode.insertBefore(textNode, node);
25274             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25275                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25276             }
25277             node.parentNode.removeChild(node);
25278         }
25279         
25280         if (node.nodeName == "#text") {
25281             // clean up silly Windows -- stuff?
25282             return; 
25283         }
25284         if (node.nodeName == "#comment") {
25285             node.parentNode.removeChild(node);
25286             // clean up silly Windows -- stuff?
25287             return; 
25288         }
25289         
25290         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25291             node.parentNode.removeChild(node);
25292             return;
25293         }
25294         //Roo.log(node.tagName);
25295         // remove - but keep children..
25296         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25297             //Roo.log('-- removed');
25298             while (node.childNodes.length) {
25299                 var cn = node.childNodes[0];
25300                 node.removeChild(cn);
25301                 node.parentNode.insertBefore(cn, node);
25302                 // move node to parent - and clean it..
25303                 this.cleanWord(cn);
25304             }
25305             node.parentNode.removeChild(node);
25306             /// no need to iterate chidlren = it's got none..
25307             //this.iterateChildren(node, this.cleanWord);
25308             return;
25309         }
25310         // clean styles
25311         if (node.className.length) {
25312             
25313             var cn = node.className.split(/\W+/);
25314             var cna = [];
25315             Roo.each(cn, function(cls) {
25316                 if (cls.match(/Mso[a-zA-Z]+/)) {
25317                     return;
25318                 }
25319                 cna.push(cls);
25320             });
25321             node.className = cna.length ? cna.join(' ') : '';
25322             if (!cna.length) {
25323                 node.removeAttribute("class");
25324             }
25325         }
25326         
25327         if (node.hasAttribute("lang")) {
25328             node.removeAttribute("lang");
25329         }
25330         
25331         if (node.hasAttribute("style")) {
25332             
25333             var styles = node.getAttribute("style").split(";");
25334             var nstyle = [];
25335             Roo.each(styles, function(s) {
25336                 if (!s.match(/:/)) {
25337                     return;
25338                 }
25339                 var kv = s.split(":");
25340                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25341                     return;
25342                 }
25343                 // what ever is left... we allow.
25344                 nstyle.push(s);
25345             });
25346             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25347             if (!nstyle.length) {
25348                 node.removeAttribute('style');
25349             }
25350         }
25351         this.iterateChildren(node, this.cleanWord);
25352         
25353         
25354         
25355     },
25356     /**
25357      * iterateChildren of a Node, calling fn each time, using this as the scole..
25358      * @param {DomNode} node node to iterate children of.
25359      * @param {Function} fn method of this class to call on each item.
25360      */
25361     iterateChildren : function(node, fn)
25362     {
25363         if (!node.childNodes.length) {
25364                 return;
25365         }
25366         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25367            fn.call(this, node.childNodes[i])
25368         }
25369     },
25370     
25371     
25372     /**
25373      * cleanTableWidths.
25374      *
25375      * Quite often pasting from word etc.. results in tables with column and widths.
25376      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25377      *
25378      */
25379     cleanTableWidths : function(node)
25380     {
25381          
25382          
25383         if (!node) {
25384             this.cleanTableWidths(this.doc.body);
25385             return;
25386         }
25387         
25388         // ignore list...
25389         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25390             return; 
25391         }
25392         Roo.log(node.tagName);
25393         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25394             this.iterateChildren(node, this.cleanTableWidths);
25395             return;
25396         }
25397         if (node.hasAttribute('width')) {
25398             node.removeAttribute('width');
25399         }
25400         
25401          
25402         if (node.hasAttribute("style")) {
25403             // pretty basic...
25404             
25405             var styles = node.getAttribute("style").split(";");
25406             var nstyle = [];
25407             Roo.each(styles, function(s) {
25408                 if (!s.match(/:/)) {
25409                     return;
25410                 }
25411                 var kv = s.split(":");
25412                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25413                     return;
25414                 }
25415                 // what ever is left... we allow.
25416                 nstyle.push(s);
25417             });
25418             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25419             if (!nstyle.length) {
25420                 node.removeAttribute('style');
25421             }
25422         }
25423         
25424         this.iterateChildren(node, this.cleanTableWidths);
25425         
25426         
25427     },
25428     
25429     
25430     
25431     
25432     domToHTML : function(currentElement, depth, nopadtext) {
25433         
25434         depth = depth || 0;
25435         nopadtext = nopadtext || false;
25436     
25437         if (!currentElement) {
25438             return this.domToHTML(this.doc.body);
25439         }
25440         
25441         //Roo.log(currentElement);
25442         var j;
25443         var allText = false;
25444         var nodeName = currentElement.nodeName;
25445         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25446         
25447         if  (nodeName == '#text') {
25448             
25449             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25450         }
25451         
25452         
25453         var ret = '';
25454         if (nodeName != 'BODY') {
25455              
25456             var i = 0;
25457             // Prints the node tagName, such as <A>, <IMG>, etc
25458             if (tagName) {
25459                 var attr = [];
25460                 for(i = 0; i < currentElement.attributes.length;i++) {
25461                     // quoting?
25462                     var aname = currentElement.attributes.item(i).name;
25463                     if (!currentElement.attributes.item(i).value.length) {
25464                         continue;
25465                     }
25466                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25467                 }
25468                 
25469                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25470             } 
25471             else {
25472                 
25473                 // eack
25474             }
25475         } else {
25476             tagName = false;
25477         }
25478         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25479             return ret;
25480         }
25481         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25482             nopadtext = true;
25483         }
25484         
25485         
25486         // Traverse the tree
25487         i = 0;
25488         var currentElementChild = currentElement.childNodes.item(i);
25489         var allText = true;
25490         var innerHTML  = '';
25491         lastnode = '';
25492         while (currentElementChild) {
25493             // Formatting code (indent the tree so it looks nice on the screen)
25494             var nopad = nopadtext;
25495             if (lastnode == 'SPAN') {
25496                 nopad  = true;
25497             }
25498             // text
25499             if  (currentElementChild.nodeName == '#text') {
25500                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25501                 toadd = nopadtext ? toadd : toadd.trim();
25502                 if (!nopad && toadd.length > 80) {
25503                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25504                 }
25505                 innerHTML  += toadd;
25506                 
25507                 i++;
25508                 currentElementChild = currentElement.childNodes.item(i);
25509                 lastNode = '';
25510                 continue;
25511             }
25512             allText = false;
25513             
25514             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25515                 
25516             // Recursively traverse the tree structure of the child node
25517             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25518             lastnode = currentElementChild.nodeName;
25519             i++;
25520             currentElementChild=currentElement.childNodes.item(i);
25521         }
25522         
25523         ret += innerHTML;
25524         
25525         if (!allText) {
25526                 // The remaining code is mostly for formatting the tree
25527             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25528         }
25529         
25530         
25531         if (tagName) {
25532             ret+= "</"+tagName+">";
25533         }
25534         return ret;
25535         
25536     },
25537         
25538     applyBlacklists : function()
25539     {
25540         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25541         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25542         
25543         this.white = [];
25544         this.black = [];
25545         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25546             if (b.indexOf(tag) > -1) {
25547                 return;
25548             }
25549             this.white.push(tag);
25550             
25551         }, this);
25552         
25553         Roo.each(w, function(tag) {
25554             if (b.indexOf(tag) > -1) {
25555                 return;
25556             }
25557             if (this.white.indexOf(tag) > -1) {
25558                 return;
25559             }
25560             this.white.push(tag);
25561             
25562         }, this);
25563         
25564         
25565         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25566             if (w.indexOf(tag) > -1) {
25567                 return;
25568             }
25569             this.black.push(tag);
25570             
25571         }, this);
25572         
25573         Roo.each(b, function(tag) {
25574             if (w.indexOf(tag) > -1) {
25575                 return;
25576             }
25577             if (this.black.indexOf(tag) > -1) {
25578                 return;
25579             }
25580             this.black.push(tag);
25581             
25582         }, this);
25583         
25584         
25585         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25586         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25587         
25588         this.cwhite = [];
25589         this.cblack = [];
25590         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25591             if (b.indexOf(tag) > -1) {
25592                 return;
25593             }
25594             this.cwhite.push(tag);
25595             
25596         }, this);
25597         
25598         Roo.each(w, function(tag) {
25599             if (b.indexOf(tag) > -1) {
25600                 return;
25601             }
25602             if (this.cwhite.indexOf(tag) > -1) {
25603                 return;
25604             }
25605             this.cwhite.push(tag);
25606             
25607         }, this);
25608         
25609         
25610         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25611             if (w.indexOf(tag) > -1) {
25612                 return;
25613             }
25614             this.cblack.push(tag);
25615             
25616         }, this);
25617         
25618         Roo.each(b, function(tag) {
25619             if (w.indexOf(tag) > -1) {
25620                 return;
25621             }
25622             if (this.cblack.indexOf(tag) > -1) {
25623                 return;
25624             }
25625             this.cblack.push(tag);
25626             
25627         }, this);
25628     },
25629     
25630     setStylesheets : function(stylesheets)
25631     {
25632         if(typeof(stylesheets) == 'string'){
25633             Roo.get(this.iframe.contentDocument.head).createChild({
25634                 tag : 'link',
25635                 rel : 'stylesheet',
25636                 type : 'text/css',
25637                 href : stylesheets
25638             });
25639             
25640             return;
25641         }
25642         var _this = this;
25643      
25644         Roo.each(stylesheets, function(s) {
25645             if(!s.length){
25646                 return;
25647             }
25648             
25649             Roo.get(_this.iframe.contentDocument.head).createChild({
25650                 tag : 'link',
25651                 rel : 'stylesheet',
25652                 type : 'text/css',
25653                 href : s
25654             });
25655         });
25656
25657         
25658     },
25659     
25660     removeStylesheets : function()
25661     {
25662         var _this = this;
25663         
25664         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25665             s.remove();
25666         });
25667     },
25668     
25669     setStyle : function(style)
25670     {
25671         Roo.get(this.iframe.contentDocument.head).createChild({
25672             tag : 'style',
25673             type : 'text/css',
25674             html : style
25675         });
25676
25677         return;
25678     }
25679     
25680     // hide stuff that is not compatible
25681     /**
25682      * @event blur
25683      * @hide
25684      */
25685     /**
25686      * @event change
25687      * @hide
25688      */
25689     /**
25690      * @event focus
25691      * @hide
25692      */
25693     /**
25694      * @event specialkey
25695      * @hide
25696      */
25697     /**
25698      * @cfg {String} fieldClass @hide
25699      */
25700     /**
25701      * @cfg {String} focusClass @hide
25702      */
25703     /**
25704      * @cfg {String} autoCreate @hide
25705      */
25706     /**
25707      * @cfg {String} inputType @hide
25708      */
25709     /**
25710      * @cfg {String} invalidClass @hide
25711      */
25712     /**
25713      * @cfg {String} invalidText @hide
25714      */
25715     /**
25716      * @cfg {String} msgFx @hide
25717      */
25718     /**
25719      * @cfg {String} validateOnBlur @hide
25720      */
25721 });
25722
25723 Roo.HtmlEditorCore.white = [
25724         'area', 'br', 'img', 'input', 'hr', 'wbr',
25725         
25726        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25727        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25728        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25729        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25730        'table',   'ul',         'xmp', 
25731        
25732        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25733       'thead',   'tr', 
25734      
25735       'dir', 'menu', 'ol', 'ul', 'dl',
25736        
25737       'embed',  'object'
25738 ];
25739
25740
25741 Roo.HtmlEditorCore.black = [
25742     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25743         'applet', // 
25744         'base',   'basefont', 'bgsound', 'blink',  'body', 
25745         'frame',  'frameset', 'head',    'html',   'ilayer', 
25746         'iframe', 'layer',  'link',     'meta',    'object',   
25747         'script', 'style' ,'title',  'xml' // clean later..
25748 ];
25749 Roo.HtmlEditorCore.clean = [
25750     'script', 'style', 'title', 'xml'
25751 ];
25752 Roo.HtmlEditorCore.remove = [
25753     'font'
25754 ];
25755 // attributes..
25756
25757 Roo.HtmlEditorCore.ablack = [
25758     'on'
25759 ];
25760     
25761 Roo.HtmlEditorCore.aclean = [ 
25762     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25763 ];
25764
25765 // protocols..
25766 Roo.HtmlEditorCore.pwhite= [
25767         'http',  'https',  'mailto'
25768 ];
25769
25770 // white listed style attributes.
25771 Roo.HtmlEditorCore.cwhite= [
25772       //  'text-align', /// default is to allow most things..
25773       
25774          
25775 //        'font-size'//??
25776 ];
25777
25778 // black listed style attributes.
25779 Roo.HtmlEditorCore.cblack= [
25780       //  'font-size' -- this can be set by the project 
25781 ];
25782
25783
25784 Roo.HtmlEditorCore.swapCodes   =[ 
25785     [    8211, "--" ], 
25786     [    8212, "--" ], 
25787     [    8216,  "'" ],  
25788     [    8217, "'" ],  
25789     [    8220, '"' ],  
25790     [    8221, '"' ],  
25791     [    8226, "*" ],  
25792     [    8230, "..." ]
25793 ]; 
25794
25795     /*
25796  * - LGPL
25797  *
25798  * HtmlEditor
25799  * 
25800  */
25801
25802 /**
25803  * @class Roo.bootstrap.HtmlEditor
25804  * @extends Roo.bootstrap.TextArea
25805  * Bootstrap HtmlEditor class
25806
25807  * @constructor
25808  * Create a new HtmlEditor
25809  * @param {Object} config The config object
25810  */
25811
25812 Roo.bootstrap.HtmlEditor = function(config){
25813     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25814     if (!this.toolbars) {
25815         this.toolbars = [];
25816     }
25817     
25818     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25819     this.addEvents({
25820             /**
25821              * @event initialize
25822              * Fires when the editor is fully initialized (including the iframe)
25823              * @param {HtmlEditor} this
25824              */
25825             initialize: true,
25826             /**
25827              * @event activate
25828              * Fires when the editor is first receives the focus. Any insertion must wait
25829              * until after this event.
25830              * @param {HtmlEditor} this
25831              */
25832             activate: true,
25833              /**
25834              * @event beforesync
25835              * Fires before the textarea is updated with content from the editor iframe. Return false
25836              * to cancel the sync.
25837              * @param {HtmlEditor} this
25838              * @param {String} html
25839              */
25840             beforesync: true,
25841              /**
25842              * @event beforepush
25843              * Fires before the iframe editor is updated with content from the textarea. Return false
25844              * to cancel the push.
25845              * @param {HtmlEditor} this
25846              * @param {String} html
25847              */
25848             beforepush: true,
25849              /**
25850              * @event sync
25851              * Fires when the textarea is updated with content from the editor iframe.
25852              * @param {HtmlEditor} this
25853              * @param {String} html
25854              */
25855             sync: true,
25856              /**
25857              * @event push
25858              * Fires when the iframe editor is updated with content from the textarea.
25859              * @param {HtmlEditor} this
25860              * @param {String} html
25861              */
25862             push: true,
25863              /**
25864              * @event editmodechange
25865              * Fires when the editor switches edit modes
25866              * @param {HtmlEditor} this
25867              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25868              */
25869             editmodechange: true,
25870             /**
25871              * @event editorevent
25872              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25873              * @param {HtmlEditor} this
25874              */
25875             editorevent: true,
25876             /**
25877              * @event firstfocus
25878              * Fires when on first focus - needed by toolbars..
25879              * @param {HtmlEditor} this
25880              */
25881             firstfocus: true,
25882             /**
25883              * @event autosave
25884              * Auto save the htmlEditor value as a file into Events
25885              * @param {HtmlEditor} this
25886              */
25887             autosave: true,
25888             /**
25889              * @event savedpreview
25890              * preview the saved version of htmlEditor
25891              * @param {HtmlEditor} this
25892              */
25893             savedpreview: true
25894         });
25895 };
25896
25897
25898 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25899     
25900     
25901       /**
25902      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25903      */
25904     toolbars : false,
25905     
25906      /**
25907     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25908     */
25909     btns : [],
25910    
25911      /**
25912      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25913      *                        Roo.resizable.
25914      */
25915     resizable : false,
25916      /**
25917      * @cfg {Number} height (in pixels)
25918      */   
25919     height: 300,
25920    /**
25921      * @cfg {Number} width (in pixels)
25922      */   
25923     width: false,
25924     
25925     /**
25926      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25927      * 
25928      */
25929     stylesheets: false,
25930     
25931     // id of frame..
25932     frameId: false,
25933     
25934     // private properties
25935     validationEvent : false,
25936     deferHeight: true,
25937     initialized : false,
25938     activated : false,
25939     
25940     onFocus : Roo.emptyFn,
25941     iframePad:3,
25942     hideMode:'offsets',
25943     
25944     tbContainer : false,
25945     
25946     bodyCls : '',
25947     
25948     toolbarContainer :function() {
25949         return this.wrap.select('.x-html-editor-tb',true).first();
25950     },
25951
25952     /**
25953      * Protected method that will not generally be called directly. It
25954      * is called when the editor creates its toolbar. Override this method if you need to
25955      * add custom toolbar buttons.
25956      * @param {HtmlEditor} editor
25957      */
25958     createToolbar : function(){
25959         Roo.log('renewing');
25960         Roo.log("create toolbars");
25961         
25962         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25963         this.toolbars[0].render(this.toolbarContainer());
25964         
25965         return;
25966         
25967 //        if (!editor.toolbars || !editor.toolbars.length) {
25968 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25969 //        }
25970 //        
25971 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25972 //            editor.toolbars[i] = Roo.factory(
25973 //                    typeof(editor.toolbars[i]) == 'string' ?
25974 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25975 //                Roo.bootstrap.HtmlEditor);
25976 //            editor.toolbars[i].init(editor);
25977 //        }
25978     },
25979
25980      
25981     // private
25982     onRender : function(ct, position)
25983     {
25984        // Roo.log("Call onRender: " + this.xtype);
25985         var _t = this;
25986         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25987       
25988         this.wrap = this.inputEl().wrap({
25989             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25990         });
25991         
25992         this.editorcore.onRender(ct, position);
25993          
25994         if (this.resizable) {
25995             this.resizeEl = new Roo.Resizable(this.wrap, {
25996                 pinned : true,
25997                 wrap: true,
25998                 dynamic : true,
25999                 minHeight : this.height,
26000                 height: this.height,
26001                 handles : this.resizable,
26002                 width: this.width,
26003                 listeners : {
26004                     resize : function(r, w, h) {
26005                         _t.onResize(w,h); // -something
26006                     }
26007                 }
26008             });
26009             
26010         }
26011         this.createToolbar(this);
26012        
26013         
26014         if(!this.width && this.resizable){
26015             this.setSize(this.wrap.getSize());
26016         }
26017         if (this.resizeEl) {
26018             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26019             // should trigger onReize..
26020         }
26021         
26022     },
26023
26024     // private
26025     onResize : function(w, h)
26026     {
26027         Roo.log('resize: ' +w + ',' + h );
26028         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26029         var ew = false;
26030         var eh = false;
26031         
26032         if(this.inputEl() ){
26033             if(typeof w == 'number'){
26034                 var aw = w - this.wrap.getFrameWidth('lr');
26035                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26036                 ew = aw;
26037             }
26038             if(typeof h == 'number'){
26039                  var tbh = -11;  // fixme it needs to tool bar size!
26040                 for (var i =0; i < this.toolbars.length;i++) {
26041                     // fixme - ask toolbars for heights?
26042                     tbh += this.toolbars[i].el.getHeight();
26043                     //if (this.toolbars[i].footer) {
26044                     //    tbh += this.toolbars[i].footer.el.getHeight();
26045                     //}
26046                 }
26047               
26048                 
26049                 
26050                 
26051                 
26052                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26053                 ah -= 5; // knock a few pixes off for look..
26054                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26055                 var eh = ah;
26056             }
26057         }
26058         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26059         this.editorcore.onResize(ew,eh);
26060         
26061     },
26062
26063     /**
26064      * Toggles the editor between standard and source edit mode.
26065      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26066      */
26067     toggleSourceEdit : function(sourceEditMode)
26068     {
26069         this.editorcore.toggleSourceEdit(sourceEditMode);
26070         
26071         if(this.editorcore.sourceEditMode){
26072             Roo.log('editor - showing textarea');
26073             
26074 //            Roo.log('in');
26075 //            Roo.log(this.syncValue());
26076             this.syncValue();
26077             this.inputEl().removeClass(['hide', 'x-hidden']);
26078             this.inputEl().dom.removeAttribute('tabIndex');
26079             this.inputEl().focus();
26080         }else{
26081             Roo.log('editor - hiding textarea');
26082 //            Roo.log('out')
26083 //            Roo.log(this.pushValue()); 
26084             this.pushValue();
26085             
26086             this.inputEl().addClass(['hide', 'x-hidden']);
26087             this.inputEl().dom.setAttribute('tabIndex', -1);
26088             //this.deferFocus();
26089         }
26090          
26091         if(this.resizable){
26092             this.setSize(this.wrap.getSize());
26093         }
26094         
26095         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26096     },
26097  
26098     // private (for BoxComponent)
26099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26100
26101     // private (for BoxComponent)
26102     getResizeEl : function(){
26103         return this.wrap;
26104     },
26105
26106     // private (for BoxComponent)
26107     getPositionEl : function(){
26108         return this.wrap;
26109     },
26110
26111     // private
26112     initEvents : function(){
26113         this.originalValue = this.getValue();
26114     },
26115
26116 //    /**
26117 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26118 //     * @method
26119 //     */
26120 //    markInvalid : Roo.emptyFn,
26121 //    /**
26122 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26123 //     * @method
26124 //     */
26125 //    clearInvalid : Roo.emptyFn,
26126
26127     setValue : function(v){
26128         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26129         this.editorcore.pushValue();
26130     },
26131
26132      
26133     // private
26134     deferFocus : function(){
26135         this.focus.defer(10, this);
26136     },
26137
26138     // doc'ed in Field
26139     focus : function(){
26140         this.editorcore.focus();
26141         
26142     },
26143       
26144
26145     // private
26146     onDestroy : function(){
26147         
26148         
26149         
26150         if(this.rendered){
26151             
26152             for (var i =0; i < this.toolbars.length;i++) {
26153                 // fixme - ask toolbars for heights?
26154                 this.toolbars[i].onDestroy();
26155             }
26156             
26157             this.wrap.dom.innerHTML = '';
26158             this.wrap.remove();
26159         }
26160     },
26161
26162     // private
26163     onFirstFocus : function(){
26164         //Roo.log("onFirstFocus");
26165         this.editorcore.onFirstFocus();
26166          for (var i =0; i < this.toolbars.length;i++) {
26167             this.toolbars[i].onFirstFocus();
26168         }
26169         
26170     },
26171     
26172     // private
26173     syncValue : function()
26174     {   
26175         this.editorcore.syncValue();
26176     },
26177     
26178     pushValue : function()
26179     {   
26180         this.editorcore.pushValue();
26181     }
26182      
26183     
26184     // hide stuff that is not compatible
26185     /**
26186      * @event blur
26187      * @hide
26188      */
26189     /**
26190      * @event change
26191      * @hide
26192      */
26193     /**
26194      * @event focus
26195      * @hide
26196      */
26197     /**
26198      * @event specialkey
26199      * @hide
26200      */
26201     /**
26202      * @cfg {String} fieldClass @hide
26203      */
26204     /**
26205      * @cfg {String} focusClass @hide
26206      */
26207     /**
26208      * @cfg {String} autoCreate @hide
26209      */
26210     /**
26211      * @cfg {String} inputType @hide
26212      */
26213      
26214     /**
26215      * @cfg {String} invalidText @hide
26216      */
26217     /**
26218      * @cfg {String} msgFx @hide
26219      */
26220     /**
26221      * @cfg {String} validateOnBlur @hide
26222      */
26223 });
26224  
26225     
26226    
26227    
26228    
26229       
26230 Roo.namespace('Roo.bootstrap.htmleditor');
26231 /**
26232  * @class Roo.bootstrap.HtmlEditorToolbar1
26233  * Basic Toolbar
26234  * 
26235  * @example
26236  * Usage:
26237  *
26238  new Roo.bootstrap.HtmlEditor({
26239     ....
26240     toolbars : [
26241         new Roo.bootstrap.HtmlEditorToolbar1({
26242             disable : { fonts: 1 , format: 1, ..., ... , ...],
26243             btns : [ .... ]
26244         })
26245     }
26246      
26247  * 
26248  * @cfg {Object} disable List of elements to disable..
26249  * @cfg {Array} btns List of additional buttons.
26250  * 
26251  * 
26252  * NEEDS Extra CSS? 
26253  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26254  */
26255  
26256 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26257 {
26258     
26259     Roo.apply(this, config);
26260     
26261     // default disabled, based on 'good practice'..
26262     this.disable = this.disable || {};
26263     Roo.applyIf(this.disable, {
26264         fontSize : true,
26265         colors : true,
26266         specialElements : true
26267     });
26268     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26269     
26270     this.editor = config.editor;
26271     this.editorcore = config.editor.editorcore;
26272     
26273     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26274     
26275     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26276     // dont call parent... till later.
26277 }
26278 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26279      
26280     bar : true,
26281     
26282     editor : false,
26283     editorcore : false,
26284     
26285     
26286     formats : [
26287         "p" ,  
26288         "h1","h2","h3","h4","h5","h6", 
26289         "pre", "code", 
26290         "abbr", "acronym", "address", "cite", "samp", "var",
26291         'div','span'
26292     ],
26293     
26294     onRender : function(ct, position)
26295     {
26296        // Roo.log("Call onRender: " + this.xtype);
26297         
26298        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26299        Roo.log(this.el);
26300        this.el.dom.style.marginBottom = '0';
26301        var _this = this;
26302        var editorcore = this.editorcore;
26303        var editor= this.editor;
26304        
26305        var children = [];
26306        var btn = function(id,cmd , toggle, handler, html){
26307        
26308             var  event = toggle ? 'toggle' : 'click';
26309        
26310             var a = {
26311                 size : 'sm',
26312                 xtype: 'Button',
26313                 xns: Roo.bootstrap,
26314                 //glyphicon : id,
26315                 fa: id,
26316                 cmd : id || cmd,
26317                 enableToggle:toggle !== false,
26318                 html : html || '',
26319                 pressed : toggle ? false : null,
26320                 listeners : {}
26321             };
26322             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26323                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26324             };
26325             children.push(a);
26326             return a;
26327        }
26328        
26329     //    var cb_box = function...
26330         
26331         var style = {
26332                 xtype: 'Button',
26333                 size : 'sm',
26334                 xns: Roo.bootstrap,
26335                 fa : 'font',
26336                 //html : 'submit'
26337                 menu : {
26338                     xtype: 'Menu',
26339                     xns: Roo.bootstrap,
26340                     items:  []
26341                 }
26342         };
26343         Roo.each(this.formats, function(f) {
26344             style.menu.items.push({
26345                 xtype :'MenuItem',
26346                 xns: Roo.bootstrap,
26347                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26348                 tagname : f,
26349                 listeners : {
26350                     click : function()
26351                     {
26352                         editorcore.insertTag(this.tagname);
26353                         editor.focus();
26354                     }
26355                 }
26356                 
26357             });
26358         });
26359         children.push(style);   
26360         
26361         btn('bold',false,true);
26362         btn('italic',false,true);
26363         btn('align-left', 'justifyleft',true);
26364         btn('align-center', 'justifycenter',true);
26365         btn('align-right' , 'justifyright',true);
26366         btn('link', false, false, function(btn) {
26367             //Roo.log("create link?");
26368             var url = prompt(this.createLinkText, this.defaultLinkValue);
26369             if(url && url != 'http:/'+'/'){
26370                 this.editorcore.relayCmd('createlink', url);
26371             }
26372         }),
26373         btn('list','insertunorderedlist',true);
26374         btn('pencil', false,true, function(btn){
26375                 Roo.log(this);
26376                 this.toggleSourceEdit(btn.pressed);
26377         });
26378         
26379         if (this.editor.btns.length > 0) {
26380             for (var i = 0; i<this.editor.btns.length; i++) {
26381                 children.push(this.editor.btns[i]);
26382             }
26383         }
26384         
26385         /*
26386         var cog = {
26387                 xtype: 'Button',
26388                 size : 'sm',
26389                 xns: Roo.bootstrap,
26390                 glyphicon : 'cog',
26391                 //html : 'submit'
26392                 menu : {
26393                     xtype: 'Menu',
26394                     xns: Roo.bootstrap,
26395                     items:  []
26396                 }
26397         };
26398         
26399         cog.menu.items.push({
26400             xtype :'MenuItem',
26401             xns: Roo.bootstrap,
26402             html : Clean styles,
26403             tagname : f,
26404             listeners : {
26405                 click : function()
26406                 {
26407                     editorcore.insertTag(this.tagname);
26408                     editor.focus();
26409                 }
26410             }
26411             
26412         });
26413        */
26414         
26415          
26416        this.xtype = 'NavSimplebar';
26417         
26418         for(var i=0;i< children.length;i++) {
26419             
26420             this.buttons.add(this.addxtypeChild(children[i]));
26421             
26422         }
26423         
26424         editor.on('editorevent', this.updateToolbar, this);
26425     },
26426     onBtnClick : function(id)
26427     {
26428        this.editorcore.relayCmd(id);
26429        this.editorcore.focus();
26430     },
26431     
26432     /**
26433      * Protected method that will not generally be called directly. It triggers
26434      * a toolbar update by reading the markup state of the current selection in the editor.
26435      */
26436     updateToolbar: function(){
26437
26438         if(!this.editorcore.activated){
26439             this.editor.onFirstFocus(); // is this neeed?
26440             return;
26441         }
26442
26443         var btns = this.buttons; 
26444         var doc = this.editorcore.doc;
26445         btns.get('bold').setActive(doc.queryCommandState('bold'));
26446         btns.get('italic').setActive(doc.queryCommandState('italic'));
26447         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26448         
26449         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26450         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26451         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26452         
26453         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26454         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26455          /*
26456         
26457         var ans = this.editorcore.getAllAncestors();
26458         if (this.formatCombo) {
26459             
26460             
26461             var store = this.formatCombo.store;
26462             this.formatCombo.setValue("");
26463             for (var i =0; i < ans.length;i++) {
26464                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26465                     // select it..
26466                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26467                     break;
26468                 }
26469             }
26470         }
26471         
26472         
26473         
26474         // hides menus... - so this cant be on a menu...
26475         Roo.bootstrap.MenuMgr.hideAll();
26476         */
26477         Roo.bootstrap.MenuMgr.hideAll();
26478         //this.editorsyncValue();
26479     },
26480     onFirstFocus: function() {
26481         this.buttons.each(function(item){
26482            item.enable();
26483         });
26484     },
26485     toggleSourceEdit : function(sourceEditMode){
26486         
26487           
26488         if(sourceEditMode){
26489             Roo.log("disabling buttons");
26490            this.buttons.each( function(item){
26491                 if(item.cmd != 'pencil'){
26492                     item.disable();
26493                 }
26494             });
26495           
26496         }else{
26497             Roo.log("enabling buttons");
26498             if(this.editorcore.initialized){
26499                 this.buttons.each( function(item){
26500                     item.enable();
26501                 });
26502             }
26503             
26504         }
26505         Roo.log("calling toggole on editor");
26506         // tell the editor that it's been pressed..
26507         this.editor.toggleSourceEdit(sourceEditMode);
26508        
26509     }
26510 });
26511
26512
26513
26514
26515  
26516 /*
26517  * - LGPL
26518  */
26519
26520 /**
26521  * @class Roo.bootstrap.Markdown
26522  * @extends Roo.bootstrap.TextArea
26523  * Bootstrap Showdown editable area
26524  * @cfg {string} content
26525  * 
26526  * @constructor
26527  * Create a new Showdown
26528  */
26529
26530 Roo.bootstrap.Markdown = function(config){
26531     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26532    
26533 };
26534
26535 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26536     
26537     editing :false,
26538     
26539     initEvents : function()
26540     {
26541         
26542         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26543         this.markdownEl = this.el.createChild({
26544             cls : 'roo-markdown-area'
26545         });
26546         this.inputEl().addClass('d-none');
26547         if (this.getValue() == '') {
26548             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26549             
26550         } else {
26551             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26552         }
26553         this.markdownEl.on('click', this.toggleTextEdit, this);
26554         this.on('blur', this.toggleTextEdit, this);
26555         this.on('specialkey', this.resizeTextArea, this);
26556     },
26557     
26558     toggleTextEdit : function()
26559     {
26560         var sh = this.markdownEl.getHeight();
26561         this.inputEl().addClass('d-none');
26562         this.markdownEl.addClass('d-none');
26563         if (!this.editing) {
26564             // show editor?
26565             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26566             this.inputEl().removeClass('d-none');
26567             this.inputEl().focus();
26568             this.editing = true;
26569             return;
26570         }
26571         // show showdown...
26572         this.updateMarkdown();
26573         this.markdownEl.removeClass('d-none');
26574         this.editing = false;
26575         return;
26576     },
26577     updateMarkdown : function()
26578     {
26579         if (this.getValue() == '') {
26580             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26581             return;
26582         }
26583  
26584         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26585     },
26586     
26587     resizeTextArea: function () {
26588         
26589         var sh = 100;
26590         Roo.log([sh, this.getValue().split("\n").length * 30]);
26591         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26592     },
26593     setValue : function(val)
26594     {
26595         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26596         if (!this.editing) {
26597             this.updateMarkdown();
26598         }
26599         
26600     },
26601     focus : function()
26602     {
26603         if (!this.editing) {
26604             this.toggleTextEdit();
26605         }
26606         
26607     }
26608
26609
26610 });
26611 /**
26612  * @class Roo.bootstrap.Table.AbstractSelectionModel
26613  * @extends Roo.util.Observable
26614  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26615  * implemented by descendant classes.  This class should not be directly instantiated.
26616  * @constructor
26617  */
26618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26619     this.locked = false;
26620     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26621 };
26622
26623
26624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26625     /** @ignore Called by the grid automatically. Do not call directly. */
26626     init : function(grid){
26627         this.grid = grid;
26628         this.initEvents();
26629     },
26630
26631     /**
26632      * Locks the selections.
26633      */
26634     lock : function(){
26635         this.locked = true;
26636     },
26637
26638     /**
26639      * Unlocks the selections.
26640      */
26641     unlock : function(){
26642         this.locked = false;
26643     },
26644
26645     /**
26646      * Returns true if the selections are locked.
26647      * @return {Boolean}
26648      */
26649     isLocked : function(){
26650         return this.locked;
26651     },
26652     
26653     
26654     initEvents : function ()
26655     {
26656         
26657     }
26658 });
26659 /**
26660  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26661  * @class Roo.bootstrap.Table.RowSelectionModel
26662  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26663  * It supports multiple selections and keyboard selection/navigation. 
26664  * @constructor
26665  * @param {Object} config
26666  */
26667
26668 Roo.bootstrap.Table.RowSelectionModel = function(config){
26669     Roo.apply(this, config);
26670     this.selections = new Roo.util.MixedCollection(false, function(o){
26671         return o.id;
26672     });
26673
26674     this.last = false;
26675     this.lastActive = false;
26676
26677     this.addEvents({
26678         /**
26679              * @event selectionchange
26680              * Fires when the selection changes
26681              * @param {SelectionModel} this
26682              */
26683             "selectionchange" : true,
26684         /**
26685              * @event afterselectionchange
26686              * Fires after the selection changes (eg. by key press or clicking)
26687              * @param {SelectionModel} this
26688              */
26689             "afterselectionchange" : true,
26690         /**
26691              * @event beforerowselect
26692              * Fires when a row is selected being selected, return false to cancel.
26693              * @param {SelectionModel} this
26694              * @param {Number} rowIndex The selected index
26695              * @param {Boolean} keepExisting False if other selections will be cleared
26696              */
26697             "beforerowselect" : true,
26698         /**
26699              * @event rowselect
26700              * Fires when a row is selected.
26701              * @param {SelectionModel} this
26702              * @param {Number} rowIndex The selected index
26703              * @param {Roo.data.Record} r The record
26704              */
26705             "rowselect" : true,
26706         /**
26707              * @event rowdeselect
26708              * Fires when a row is deselected.
26709              * @param {SelectionModel} this
26710              * @param {Number} rowIndex The selected index
26711              */
26712         "rowdeselect" : true
26713     });
26714     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26715     this.locked = false;
26716  };
26717
26718 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26719     /**
26720      * @cfg {Boolean} singleSelect
26721      * True to allow selection of only one row at a time (defaults to false)
26722      */
26723     singleSelect : false,
26724
26725     // private
26726     initEvents : function()
26727     {
26728
26729         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26730         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26731         //}else{ // allow click to work like normal
26732          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26733         //}
26734         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26735         this.grid.on("rowclick", this.handleMouseDown, this);
26736         
26737         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26738             "up" : function(e){
26739                 if(!e.shiftKey){
26740                     this.selectPrevious(e.shiftKey);
26741                 }else if(this.last !== false && this.lastActive !== false){
26742                     var last = this.last;
26743                     this.selectRange(this.last,  this.lastActive-1);
26744                     this.grid.getView().focusRow(this.lastActive);
26745                     if(last !== false){
26746                         this.last = last;
26747                     }
26748                 }else{
26749                     this.selectFirstRow();
26750                 }
26751                 this.fireEvent("afterselectionchange", this);
26752             },
26753             "down" : function(e){
26754                 if(!e.shiftKey){
26755                     this.selectNext(e.shiftKey);
26756                 }else if(this.last !== false && this.lastActive !== false){
26757                     var last = this.last;
26758                     this.selectRange(this.last,  this.lastActive+1);
26759                     this.grid.getView().focusRow(this.lastActive);
26760                     if(last !== false){
26761                         this.last = last;
26762                     }
26763                 }else{
26764                     this.selectFirstRow();
26765                 }
26766                 this.fireEvent("afterselectionchange", this);
26767             },
26768             scope: this
26769         });
26770         this.grid.store.on('load', function(){
26771             this.selections.clear();
26772         },this);
26773         /*
26774         var view = this.grid.view;
26775         view.on("refresh", this.onRefresh, this);
26776         view.on("rowupdated", this.onRowUpdated, this);
26777         view.on("rowremoved", this.onRemove, this);
26778         */
26779     },
26780
26781     // private
26782     onRefresh : function()
26783     {
26784         var ds = this.grid.store, i, v = this.grid.view;
26785         var s = this.selections;
26786         s.each(function(r){
26787             if((i = ds.indexOfId(r.id)) != -1){
26788                 v.onRowSelect(i);
26789             }else{
26790                 s.remove(r);
26791             }
26792         });
26793     },
26794
26795     // private
26796     onRemove : function(v, index, r){
26797         this.selections.remove(r);
26798     },
26799
26800     // private
26801     onRowUpdated : function(v, index, r){
26802         if(this.isSelected(r)){
26803             v.onRowSelect(index);
26804         }
26805     },
26806
26807     /**
26808      * Select records.
26809      * @param {Array} records The records to select
26810      * @param {Boolean} keepExisting (optional) True to keep existing selections
26811      */
26812     selectRecords : function(records, keepExisting)
26813     {
26814         if(!keepExisting){
26815             this.clearSelections();
26816         }
26817             var ds = this.grid.store;
26818         for(var i = 0, len = records.length; i < len; i++){
26819             this.selectRow(ds.indexOf(records[i]), true);
26820         }
26821     },
26822
26823     /**
26824      * Gets the number of selected rows.
26825      * @return {Number}
26826      */
26827     getCount : function(){
26828         return this.selections.length;
26829     },
26830
26831     /**
26832      * Selects the first row in the grid.
26833      */
26834     selectFirstRow : function(){
26835         this.selectRow(0);
26836     },
26837
26838     /**
26839      * Select the last row.
26840      * @param {Boolean} keepExisting (optional) True to keep existing selections
26841      */
26842     selectLastRow : function(keepExisting){
26843         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26844         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26845     },
26846
26847     /**
26848      * Selects the row immediately following the last selected row.
26849      * @param {Boolean} keepExisting (optional) True to keep existing selections
26850      */
26851     selectNext : function(keepExisting)
26852     {
26853             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26854             this.selectRow(this.last+1, keepExisting);
26855             this.grid.getView().focusRow(this.last);
26856         }
26857     },
26858
26859     /**
26860      * Selects the row that precedes the last selected row.
26861      * @param {Boolean} keepExisting (optional) True to keep existing selections
26862      */
26863     selectPrevious : function(keepExisting){
26864         if(this.last){
26865             this.selectRow(this.last-1, keepExisting);
26866             this.grid.getView().focusRow(this.last);
26867         }
26868     },
26869
26870     /**
26871      * Returns the selected records
26872      * @return {Array} Array of selected records
26873      */
26874     getSelections : function(){
26875         return [].concat(this.selections.items);
26876     },
26877
26878     /**
26879      * Returns the first selected record.
26880      * @return {Record}
26881      */
26882     getSelected : function(){
26883         return this.selections.itemAt(0);
26884     },
26885
26886
26887     /**
26888      * Clears all selections.
26889      */
26890     clearSelections : function(fast)
26891     {
26892         if(this.locked) {
26893             return;
26894         }
26895         if(fast !== true){
26896                 var ds = this.grid.store;
26897             var s = this.selections;
26898             s.each(function(r){
26899                 this.deselectRow(ds.indexOfId(r.id));
26900             }, this);
26901             s.clear();
26902         }else{
26903             this.selections.clear();
26904         }
26905         this.last = false;
26906     },
26907
26908
26909     /**
26910      * Selects all rows.
26911      */
26912     selectAll : function(){
26913         if(this.locked) {
26914             return;
26915         }
26916         this.selections.clear();
26917         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26918             this.selectRow(i, true);
26919         }
26920     },
26921
26922     /**
26923      * Returns True if there is a selection.
26924      * @return {Boolean}
26925      */
26926     hasSelection : function(){
26927         return this.selections.length > 0;
26928     },
26929
26930     /**
26931      * Returns True if the specified row is selected.
26932      * @param {Number/Record} record The record or index of the record to check
26933      * @return {Boolean}
26934      */
26935     isSelected : function(index){
26936             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26937         return (r && this.selections.key(r.id) ? true : false);
26938     },
26939
26940     /**
26941      * Returns True if the specified record id is selected.
26942      * @param {String} id The id of record to check
26943      * @return {Boolean}
26944      */
26945     isIdSelected : function(id){
26946         return (this.selections.key(id) ? true : false);
26947     },
26948
26949
26950     // private
26951     handleMouseDBClick : function(e, t){
26952         
26953     },
26954     // private
26955     handleMouseDown : function(e, t)
26956     {
26957             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26958         if(this.isLocked() || rowIndex < 0 ){
26959             return;
26960         };
26961         if(e.shiftKey && this.last !== false){
26962             var last = this.last;
26963             this.selectRange(last, rowIndex, e.ctrlKey);
26964             this.last = last; // reset the last
26965             t.focus();
26966     
26967         }else{
26968             var isSelected = this.isSelected(rowIndex);
26969             //Roo.log("select row:" + rowIndex);
26970             if(isSelected){
26971                 this.deselectRow(rowIndex);
26972             } else {
26973                         this.selectRow(rowIndex, true);
26974             }
26975     
26976             /*
26977                 if(e.button !== 0 && isSelected){
26978                 alert('rowIndex 2: ' + rowIndex);
26979                     view.focusRow(rowIndex);
26980                 }else if(e.ctrlKey && isSelected){
26981                     this.deselectRow(rowIndex);
26982                 }else if(!isSelected){
26983                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26984                     view.focusRow(rowIndex);
26985                 }
26986             */
26987         }
26988         this.fireEvent("afterselectionchange", this);
26989     },
26990     // private
26991     handleDragableRowClick :  function(grid, rowIndex, e) 
26992     {
26993         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26994             this.selectRow(rowIndex, false);
26995             grid.view.focusRow(rowIndex);
26996              this.fireEvent("afterselectionchange", this);
26997         }
26998     },
26999     
27000     /**
27001      * Selects multiple rows.
27002      * @param {Array} rows Array of the indexes of the row to select
27003      * @param {Boolean} keepExisting (optional) True to keep existing selections
27004      */
27005     selectRows : function(rows, keepExisting){
27006         if(!keepExisting){
27007             this.clearSelections();
27008         }
27009         for(var i = 0, len = rows.length; i < len; i++){
27010             this.selectRow(rows[i], true);
27011         }
27012     },
27013
27014     /**
27015      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27016      * @param {Number} startRow The index of the first row in the range
27017      * @param {Number} endRow The index of the last row in the range
27018      * @param {Boolean} keepExisting (optional) True to retain existing selections
27019      */
27020     selectRange : function(startRow, endRow, keepExisting){
27021         if(this.locked) {
27022             return;
27023         }
27024         if(!keepExisting){
27025             this.clearSelections();
27026         }
27027         if(startRow <= endRow){
27028             for(var i = startRow; i <= endRow; i++){
27029                 this.selectRow(i, true);
27030             }
27031         }else{
27032             for(var i = startRow; i >= endRow; i--){
27033                 this.selectRow(i, true);
27034             }
27035         }
27036     },
27037
27038     /**
27039      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27040      * @param {Number} startRow The index of the first row in the range
27041      * @param {Number} endRow The index of the last row in the range
27042      */
27043     deselectRange : function(startRow, endRow, preventViewNotify){
27044         if(this.locked) {
27045             return;
27046         }
27047         for(var i = startRow; i <= endRow; i++){
27048             this.deselectRow(i, preventViewNotify);
27049         }
27050     },
27051
27052     /**
27053      * Selects a row.
27054      * @param {Number} row The index of the row to select
27055      * @param {Boolean} keepExisting (optional) True to keep existing selections
27056      */
27057     selectRow : function(index, keepExisting, preventViewNotify)
27058     {
27059             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27060             return;
27061         }
27062         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27063             if(!keepExisting || this.singleSelect){
27064                 this.clearSelections();
27065             }
27066             
27067             var r = this.grid.store.getAt(index);
27068             //console.log('selectRow - record id :' + r.id);
27069             
27070             this.selections.add(r);
27071             this.last = this.lastActive = index;
27072             if(!preventViewNotify){
27073                 var proxy = new Roo.Element(
27074                                 this.grid.getRowDom(index)
27075                 );
27076                 proxy.addClass('bg-info info');
27077             }
27078             this.fireEvent("rowselect", this, index, r);
27079             this.fireEvent("selectionchange", this);
27080         }
27081     },
27082
27083     /**
27084      * Deselects a row.
27085      * @param {Number} row The index of the row to deselect
27086      */
27087     deselectRow : function(index, preventViewNotify)
27088     {
27089         if(this.locked) {
27090             return;
27091         }
27092         if(this.last == index){
27093             this.last = false;
27094         }
27095         if(this.lastActive == index){
27096             this.lastActive = false;
27097         }
27098         
27099         var r = this.grid.store.getAt(index);
27100         if (!r) {
27101             return;
27102         }
27103         
27104         this.selections.remove(r);
27105         //.console.log('deselectRow - record id :' + r.id);
27106         if(!preventViewNotify){
27107         
27108             var proxy = new Roo.Element(
27109                 this.grid.getRowDom(index)
27110             );
27111             proxy.removeClass('bg-info info');
27112         }
27113         this.fireEvent("rowdeselect", this, index);
27114         this.fireEvent("selectionchange", this);
27115     },
27116
27117     // private
27118     restoreLast : function(){
27119         if(this._last){
27120             this.last = this._last;
27121         }
27122     },
27123
27124     // private
27125     acceptsNav : function(row, col, cm){
27126         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27127     },
27128
27129     // private
27130     onEditorKey : function(field, e){
27131         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27132         if(k == e.TAB){
27133             e.stopEvent();
27134             ed.completeEdit();
27135             if(e.shiftKey){
27136                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27137             }else{
27138                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27139             }
27140         }else if(k == e.ENTER && !e.ctrlKey){
27141             e.stopEvent();
27142             ed.completeEdit();
27143             if(e.shiftKey){
27144                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27145             }else{
27146                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27147             }
27148         }else if(k == e.ESC){
27149             ed.cancelEdit();
27150         }
27151         if(newCell){
27152             g.startEditing(newCell[0], newCell[1]);
27153         }
27154     }
27155 });
27156 /*
27157  * Based on:
27158  * Ext JS Library 1.1.1
27159  * Copyright(c) 2006-2007, Ext JS, LLC.
27160  *
27161  * Originally Released Under LGPL - original licence link has changed is not relivant.
27162  *
27163  * Fork - LGPL
27164  * <script type="text/javascript">
27165  */
27166  
27167 /**
27168  * @class Roo.bootstrap.PagingToolbar
27169  * @extends Roo.bootstrap.NavSimplebar
27170  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27171  * @constructor
27172  * Create a new PagingToolbar
27173  * @param {Object} config The config object
27174  * @param {Roo.data.Store} store
27175  */
27176 Roo.bootstrap.PagingToolbar = function(config)
27177 {
27178     // old args format still supported... - xtype is prefered..
27179         // created from xtype...
27180     
27181     this.ds = config.dataSource;
27182     
27183     if (config.store && !this.ds) {
27184         this.store= Roo.factory(config.store, Roo.data);
27185         this.ds = this.store;
27186         this.ds.xmodule = this.xmodule || false;
27187     }
27188     
27189     this.toolbarItems = [];
27190     if (config.items) {
27191         this.toolbarItems = config.items;
27192     }
27193     
27194     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27195     
27196     this.cursor = 0;
27197     
27198     if (this.ds) { 
27199         this.bind(this.ds);
27200     }
27201     
27202     if (Roo.bootstrap.version == 4) {
27203         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27204     } else {
27205         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27206     }
27207     
27208 };
27209
27210 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27211     /**
27212      * @cfg {Roo.data.Store} dataSource
27213      * The underlying data store providing the paged data
27214      */
27215     /**
27216      * @cfg {String/HTMLElement/Element} container
27217      * container The id or element that will contain the toolbar
27218      */
27219     /**
27220      * @cfg {Boolean} displayInfo
27221      * True to display the displayMsg (defaults to false)
27222      */
27223     /**
27224      * @cfg {Number} pageSize
27225      * The number of records to display per page (defaults to 20)
27226      */
27227     pageSize: 20,
27228     /**
27229      * @cfg {String} displayMsg
27230      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27231      */
27232     displayMsg : 'Displaying {0} - {1} of {2}',
27233     /**
27234      * @cfg {String} emptyMsg
27235      * The message to display when no records are found (defaults to "No data to display")
27236      */
27237     emptyMsg : 'No data to display',
27238     /**
27239      * Customizable piece of the default paging text (defaults to "Page")
27240      * @type String
27241      */
27242     beforePageText : "Page",
27243     /**
27244      * Customizable piece of the default paging text (defaults to "of %0")
27245      * @type String
27246      */
27247     afterPageText : "of {0}",
27248     /**
27249      * Customizable piece of the default paging text (defaults to "First Page")
27250      * @type String
27251      */
27252     firstText : "First Page",
27253     /**
27254      * Customizable piece of the default paging text (defaults to "Previous Page")
27255      * @type String
27256      */
27257     prevText : "Previous Page",
27258     /**
27259      * Customizable piece of the default paging text (defaults to "Next Page")
27260      * @type String
27261      */
27262     nextText : "Next Page",
27263     /**
27264      * Customizable piece of the default paging text (defaults to "Last Page")
27265      * @type String
27266      */
27267     lastText : "Last Page",
27268     /**
27269      * Customizable piece of the default paging text (defaults to "Refresh")
27270      * @type String
27271      */
27272     refreshText : "Refresh",
27273
27274     buttons : false,
27275     // private
27276     onRender : function(ct, position) 
27277     {
27278         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27279         this.navgroup.parentId = this.id;
27280         this.navgroup.onRender(this.el, null);
27281         // add the buttons to the navgroup
27282         
27283         if(this.displayInfo){
27284             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27285             this.displayEl = this.el.select('.x-paging-info', true).first();
27286 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27287 //            this.displayEl = navel.el.select('span',true).first();
27288         }
27289         
27290         var _this = this;
27291         
27292         if(this.buttons){
27293             Roo.each(_this.buttons, function(e){ // this might need to use render????
27294                Roo.factory(e).render(_this.el);
27295             });
27296         }
27297             
27298         Roo.each(_this.toolbarItems, function(e) {
27299             _this.navgroup.addItem(e);
27300         });
27301         
27302         
27303         this.first = this.navgroup.addItem({
27304             tooltip: this.firstText,
27305             cls: "prev btn-outline-secondary",
27306             html : ' <i class="fa fa-step-backward"></i>',
27307             disabled: true,
27308             preventDefault: true,
27309             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27310         });
27311         
27312         this.prev =  this.navgroup.addItem({
27313             tooltip: this.prevText,
27314             cls: "prev btn-outline-secondary",
27315             html : ' <i class="fa fa-backward"></i>',
27316             disabled: true,
27317             preventDefault: true,
27318             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27319         });
27320     //this.addSeparator();
27321         
27322         
27323         var field = this.navgroup.addItem( {
27324             tagtype : 'span',
27325             cls : 'x-paging-position  btn-outline-secondary',
27326              disabled: true,
27327             html : this.beforePageText  +
27328                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27329                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27330          } ); //?? escaped?
27331         
27332         this.field = field.el.select('input', true).first();
27333         this.field.on("keydown", this.onPagingKeydown, this);
27334         this.field.on("focus", function(){this.dom.select();});
27335     
27336     
27337         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27338         //this.field.setHeight(18);
27339         //this.addSeparator();
27340         this.next = this.navgroup.addItem({
27341             tooltip: this.nextText,
27342             cls: "next btn-outline-secondary",
27343             html : ' <i class="fa fa-forward"></i>',
27344             disabled: true,
27345             preventDefault: true,
27346             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27347         });
27348         this.last = this.navgroup.addItem({
27349             tooltip: this.lastText,
27350             html : ' <i class="fa fa-step-forward"></i>',
27351             cls: "next btn-outline-secondary",
27352             disabled: true,
27353             preventDefault: true,
27354             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27355         });
27356     //this.addSeparator();
27357         this.loading = this.navgroup.addItem({
27358             tooltip: this.refreshText,
27359             cls: "btn-outline-secondary",
27360             html : ' <i class="fa fa-refresh"></i>',
27361             preventDefault: true,
27362             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27363         });
27364         
27365     },
27366
27367     // private
27368     updateInfo : function(){
27369         if(this.displayEl){
27370             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27371             var msg = count == 0 ?
27372                 this.emptyMsg :
27373                 String.format(
27374                     this.displayMsg,
27375                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27376                 );
27377             this.displayEl.update(msg);
27378         }
27379     },
27380
27381     // private
27382     onLoad : function(ds, r, o)
27383     {
27384         this.cursor = o.params && o.params.start ? o.params.start : 0;
27385         
27386         var d = this.getPageData(),
27387             ap = d.activePage,
27388             ps = d.pages;
27389         
27390         
27391         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27392         this.field.dom.value = ap;
27393         this.first.setDisabled(ap == 1);
27394         this.prev.setDisabled(ap == 1);
27395         this.next.setDisabled(ap == ps);
27396         this.last.setDisabled(ap == ps);
27397         this.loading.enable();
27398         this.updateInfo();
27399     },
27400
27401     // private
27402     getPageData : function(){
27403         var total = this.ds.getTotalCount();
27404         return {
27405             total : total,
27406             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27407             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27408         };
27409     },
27410
27411     // private
27412     onLoadError : function(){
27413         this.loading.enable();
27414     },
27415
27416     // private
27417     onPagingKeydown : function(e){
27418         var k = e.getKey();
27419         var d = this.getPageData();
27420         if(k == e.RETURN){
27421             var v = this.field.dom.value, pageNum;
27422             if(!v || isNaN(pageNum = parseInt(v, 10))){
27423                 this.field.dom.value = d.activePage;
27424                 return;
27425             }
27426             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27427             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27428             e.stopEvent();
27429         }
27430         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))
27431         {
27432           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27433           this.field.dom.value = pageNum;
27434           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27435           e.stopEvent();
27436         }
27437         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27438         {
27439           var v = this.field.dom.value, pageNum; 
27440           var increment = (e.shiftKey) ? 10 : 1;
27441           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27442                 increment *= -1;
27443           }
27444           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27445             this.field.dom.value = d.activePage;
27446             return;
27447           }
27448           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27449           {
27450             this.field.dom.value = parseInt(v, 10) + increment;
27451             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27452             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27453           }
27454           e.stopEvent();
27455         }
27456     },
27457
27458     // private
27459     beforeLoad : function(){
27460         if(this.loading){
27461             this.loading.disable();
27462         }
27463     },
27464
27465     // private
27466     onClick : function(which){
27467         
27468         var ds = this.ds;
27469         if (!ds) {
27470             return;
27471         }
27472         
27473         switch(which){
27474             case "first":
27475                 ds.load({params:{start: 0, limit: this.pageSize}});
27476             break;
27477             case "prev":
27478                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27479             break;
27480             case "next":
27481                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27482             break;
27483             case "last":
27484                 var total = ds.getTotalCount();
27485                 var extra = total % this.pageSize;
27486                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27487                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27488             break;
27489             case "refresh":
27490                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27491             break;
27492         }
27493     },
27494
27495     /**
27496      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27497      * @param {Roo.data.Store} store The data store to unbind
27498      */
27499     unbind : function(ds){
27500         ds.un("beforeload", this.beforeLoad, this);
27501         ds.un("load", this.onLoad, this);
27502         ds.un("loadexception", this.onLoadError, this);
27503         ds.un("remove", this.updateInfo, this);
27504         ds.un("add", this.updateInfo, this);
27505         this.ds = undefined;
27506     },
27507
27508     /**
27509      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27510      * @param {Roo.data.Store} store The data store to bind
27511      */
27512     bind : function(ds){
27513         ds.on("beforeload", this.beforeLoad, this);
27514         ds.on("load", this.onLoad, this);
27515         ds.on("loadexception", this.onLoadError, this);
27516         ds.on("remove", this.updateInfo, this);
27517         ds.on("add", this.updateInfo, this);
27518         this.ds = ds;
27519     }
27520 });/*
27521  * - LGPL
27522  *
27523  * element
27524  * 
27525  */
27526
27527 /**
27528  * @class Roo.bootstrap.MessageBar
27529  * @extends Roo.bootstrap.Component
27530  * Bootstrap MessageBar class
27531  * @cfg {String} html contents of the MessageBar
27532  * @cfg {String} weight (info | success | warning | danger) default info
27533  * @cfg {String} beforeClass insert the bar before the given class
27534  * @cfg {Boolean} closable (true | false) default false
27535  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27536  * 
27537  * @constructor
27538  * Create a new Element
27539  * @param {Object} config The config object
27540  */
27541
27542 Roo.bootstrap.MessageBar = function(config){
27543     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27544 };
27545
27546 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27547     
27548     html: '',
27549     weight: 'info',
27550     closable: false,
27551     fixed: false,
27552     beforeClass: 'bootstrap-sticky-wrap',
27553     
27554     getAutoCreate : function(){
27555         
27556         var cfg = {
27557             tag: 'div',
27558             cls: 'alert alert-dismissable alert-' + this.weight,
27559             cn: [
27560                 {
27561                     tag: 'span',
27562                     cls: 'message',
27563                     html: this.html || ''
27564                 }
27565             ]
27566         };
27567         
27568         if(this.fixed){
27569             cfg.cls += ' alert-messages-fixed';
27570         }
27571         
27572         if(this.closable){
27573             cfg.cn.push({
27574                 tag: 'button',
27575                 cls: 'close',
27576                 html: 'x'
27577             });
27578         }
27579         
27580         return cfg;
27581     },
27582     
27583     onRender : function(ct, position)
27584     {
27585         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27586         
27587         if(!this.el){
27588             var cfg = Roo.apply({},  this.getAutoCreate());
27589             cfg.id = Roo.id();
27590             
27591             if (this.cls) {
27592                 cfg.cls += ' ' + this.cls;
27593             }
27594             if (this.style) {
27595                 cfg.style = this.style;
27596             }
27597             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27598             
27599             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27600         }
27601         
27602         this.el.select('>button.close').on('click', this.hide, this);
27603         
27604     },
27605     
27606     show : function()
27607     {
27608         if (!this.rendered) {
27609             this.render();
27610         }
27611         
27612         this.el.show();
27613         
27614         this.fireEvent('show', this);
27615         
27616     },
27617     
27618     hide : function()
27619     {
27620         if (!this.rendered) {
27621             this.render();
27622         }
27623         
27624         this.el.hide();
27625         
27626         this.fireEvent('hide', this);
27627     },
27628     
27629     update : function()
27630     {
27631 //        var e = this.el.dom.firstChild;
27632 //        
27633 //        if(this.closable){
27634 //            e = e.nextSibling;
27635 //        }
27636 //        
27637 //        e.data = this.html || '';
27638
27639         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27640     }
27641    
27642 });
27643
27644  
27645
27646      /*
27647  * - LGPL
27648  *
27649  * Graph
27650  * 
27651  */
27652
27653
27654 /**
27655  * @class Roo.bootstrap.Graph
27656  * @extends Roo.bootstrap.Component
27657  * Bootstrap Graph class
27658 > Prameters
27659  -sm {number} sm 4
27660  -md {number} md 5
27661  @cfg {String} graphtype  bar | vbar | pie
27662  @cfg {number} g_x coodinator | centre x (pie)
27663  @cfg {number} g_y coodinator | centre y (pie)
27664  @cfg {number} g_r radius (pie)
27665  @cfg {number} g_height height of the chart (respected by all elements in the set)
27666  @cfg {number} g_width width of the chart (respected by all elements in the set)
27667  @cfg {Object} title The title of the chart
27668     
27669  -{Array}  values
27670  -opts (object) options for the chart 
27671      o {
27672      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27673      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27674      o vgutter (number)
27675      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.
27676      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27677      o to
27678      o stretch (boolean)
27679      o }
27680  -opts (object) options for the pie
27681      o{
27682      o cut
27683      o startAngle (number)
27684      o endAngle (number)
27685      } 
27686  *
27687  * @constructor
27688  * Create a new Input
27689  * @param {Object} config The config object
27690  */
27691
27692 Roo.bootstrap.Graph = function(config){
27693     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27694     
27695     this.addEvents({
27696         // img events
27697         /**
27698          * @event click
27699          * The img click event for the img.
27700          * @param {Roo.EventObject} e
27701          */
27702         "click" : true
27703     });
27704 };
27705
27706 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27707     
27708     sm: 4,
27709     md: 5,
27710     graphtype: 'bar',
27711     g_height: 250,
27712     g_width: 400,
27713     g_x: 50,
27714     g_y: 50,
27715     g_r: 30,
27716     opts:{
27717         //g_colors: this.colors,
27718         g_type: 'soft',
27719         g_gutter: '20%'
27720
27721     },
27722     title : false,
27723
27724     getAutoCreate : function(){
27725         
27726         var cfg = {
27727             tag: 'div',
27728             html : null
27729         };
27730         
27731         
27732         return  cfg;
27733     },
27734
27735     onRender : function(ct,position){
27736         
27737         
27738         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27739         
27740         if (typeof(Raphael) == 'undefined') {
27741             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27742             return;
27743         }
27744         
27745         this.raphael = Raphael(this.el.dom);
27746         
27747                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27748                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27749                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27750                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27751                 /*
27752                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27753                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27754                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27755                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27756                 
27757                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27758                 r.barchart(330, 10, 300, 220, data1);
27759                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27760                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27761                 */
27762                 
27763                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27764                 // r.barchart(30, 30, 560, 250,  xdata, {
27765                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27766                 //     axis : "0 0 1 1",
27767                 //     axisxlabels :  xdata
27768                 //     //yvalues : cols,
27769                    
27770                 // });
27771 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27772 //        
27773 //        this.load(null,xdata,{
27774 //                axis : "0 0 1 1",
27775 //                axisxlabels :  xdata
27776 //                });
27777
27778     },
27779
27780     load : function(graphtype,xdata,opts)
27781     {
27782         this.raphael.clear();
27783         if(!graphtype) {
27784             graphtype = this.graphtype;
27785         }
27786         if(!opts){
27787             opts = this.opts;
27788         }
27789         var r = this.raphael,
27790             fin = function () {
27791                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27792             },
27793             fout = function () {
27794                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27795             },
27796             pfin = function() {
27797                 this.sector.stop();
27798                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27799
27800                 if (this.label) {
27801                     this.label[0].stop();
27802                     this.label[0].attr({ r: 7.5 });
27803                     this.label[1].attr({ "font-weight": 800 });
27804                 }
27805             },
27806             pfout = function() {
27807                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27808
27809                 if (this.label) {
27810                     this.label[0].animate({ r: 5 }, 500, "bounce");
27811                     this.label[1].attr({ "font-weight": 400 });
27812                 }
27813             };
27814
27815         switch(graphtype){
27816             case 'bar':
27817                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27818                 break;
27819             case 'hbar':
27820                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27821                 break;
27822             case 'pie':
27823 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27824 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27825 //            
27826                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27827                 
27828                 break;
27829
27830         }
27831         
27832         if(this.title){
27833             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27834         }
27835         
27836     },
27837     
27838     setTitle: function(o)
27839     {
27840         this.title = o;
27841     },
27842     
27843     initEvents: function() {
27844         
27845         if(!this.href){
27846             this.el.on('click', this.onClick, this);
27847         }
27848     },
27849     
27850     onClick : function(e)
27851     {
27852         Roo.log('img onclick');
27853         this.fireEvent('click', this, e);
27854     }
27855    
27856 });
27857
27858  
27859 /*
27860  * - LGPL
27861  *
27862  * numberBox
27863  * 
27864  */
27865 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27866
27867 /**
27868  * @class Roo.bootstrap.dash.NumberBox
27869  * @extends Roo.bootstrap.Component
27870  * Bootstrap NumberBox class
27871  * @cfg {String} headline Box headline
27872  * @cfg {String} content Box content
27873  * @cfg {String} icon Box icon
27874  * @cfg {String} footer Footer text
27875  * @cfg {String} fhref Footer href
27876  * 
27877  * @constructor
27878  * Create a new NumberBox
27879  * @param {Object} config The config object
27880  */
27881
27882
27883 Roo.bootstrap.dash.NumberBox = function(config){
27884     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27885     
27886 };
27887
27888 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27889     
27890     headline : '',
27891     content : '',
27892     icon : '',
27893     footer : '',
27894     fhref : '',
27895     ficon : '',
27896     
27897     getAutoCreate : function(){
27898         
27899         var cfg = {
27900             tag : 'div',
27901             cls : 'small-box ',
27902             cn : [
27903                 {
27904                     tag : 'div',
27905                     cls : 'inner',
27906                     cn :[
27907                         {
27908                             tag : 'h3',
27909                             cls : 'roo-headline',
27910                             html : this.headline
27911                         },
27912                         {
27913                             tag : 'p',
27914                             cls : 'roo-content',
27915                             html : this.content
27916                         }
27917                     ]
27918                 }
27919             ]
27920         };
27921         
27922         if(this.icon){
27923             cfg.cn.push({
27924                 tag : 'div',
27925                 cls : 'icon',
27926                 cn :[
27927                     {
27928                         tag : 'i',
27929                         cls : 'ion ' + this.icon
27930                     }
27931                 ]
27932             });
27933         }
27934         
27935         if(this.footer){
27936             var footer = {
27937                 tag : 'a',
27938                 cls : 'small-box-footer',
27939                 href : this.fhref || '#',
27940                 html : this.footer
27941             };
27942             
27943             cfg.cn.push(footer);
27944             
27945         }
27946         
27947         return  cfg;
27948     },
27949
27950     onRender : function(ct,position){
27951         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27952
27953
27954        
27955                 
27956     },
27957
27958     setHeadline: function (value)
27959     {
27960         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27961     },
27962     
27963     setFooter: function (value, href)
27964     {
27965         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27966         
27967         if(href){
27968             this.el.select('a.small-box-footer',true).first().attr('href', href);
27969         }
27970         
27971     },
27972
27973     setContent: function (value)
27974     {
27975         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27976     },
27977
27978     initEvents: function() 
27979     {   
27980         
27981     }
27982     
27983 });
27984
27985  
27986 /*
27987  * - LGPL
27988  *
27989  * TabBox
27990  * 
27991  */
27992 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27993
27994 /**
27995  * @class Roo.bootstrap.dash.TabBox
27996  * @extends Roo.bootstrap.Component
27997  * Bootstrap TabBox class
27998  * @cfg {String} title Title of the TabBox
27999  * @cfg {String} icon Icon of the TabBox
28000  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28001  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28002  * 
28003  * @constructor
28004  * Create a new TabBox
28005  * @param {Object} config The config object
28006  */
28007
28008
28009 Roo.bootstrap.dash.TabBox = function(config){
28010     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28011     this.addEvents({
28012         // raw events
28013         /**
28014          * @event addpane
28015          * When a pane is added
28016          * @param {Roo.bootstrap.dash.TabPane} pane
28017          */
28018         "addpane" : true,
28019         /**
28020          * @event activatepane
28021          * When a pane is activated
28022          * @param {Roo.bootstrap.dash.TabPane} pane
28023          */
28024         "activatepane" : true
28025         
28026          
28027     });
28028     
28029     this.panes = [];
28030 };
28031
28032 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28033
28034     title : '',
28035     icon : false,
28036     showtabs : true,
28037     tabScrollable : false,
28038     
28039     getChildContainer : function()
28040     {
28041         return this.el.select('.tab-content', true).first();
28042     },
28043     
28044     getAutoCreate : function(){
28045         
28046         var header = {
28047             tag: 'li',
28048             cls: 'pull-left header',
28049             html: this.title,
28050             cn : []
28051         };
28052         
28053         if(this.icon){
28054             header.cn.push({
28055                 tag: 'i',
28056                 cls: 'fa ' + this.icon
28057             });
28058         }
28059         
28060         var h = {
28061             tag: 'ul',
28062             cls: 'nav nav-tabs pull-right',
28063             cn: [
28064                 header
28065             ]
28066         };
28067         
28068         if(this.tabScrollable){
28069             h = {
28070                 tag: 'div',
28071                 cls: 'tab-header',
28072                 cn: [
28073                     {
28074                         tag: 'ul',
28075                         cls: 'nav nav-tabs pull-right',
28076                         cn: [
28077                             header
28078                         ]
28079                     }
28080                 ]
28081             };
28082         }
28083         
28084         var cfg = {
28085             tag: 'div',
28086             cls: 'nav-tabs-custom',
28087             cn: [
28088                 h,
28089                 {
28090                     tag: 'div',
28091                     cls: 'tab-content no-padding',
28092                     cn: []
28093                 }
28094             ]
28095         };
28096
28097         return  cfg;
28098     },
28099     initEvents : function()
28100     {
28101         //Roo.log('add add pane handler');
28102         this.on('addpane', this.onAddPane, this);
28103     },
28104      /**
28105      * Updates the box title
28106      * @param {String} html to set the title to.
28107      */
28108     setTitle : function(value)
28109     {
28110         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28111     },
28112     onAddPane : function(pane)
28113     {
28114         this.panes.push(pane);
28115         //Roo.log('addpane');
28116         //Roo.log(pane);
28117         // tabs are rendere left to right..
28118         if(!this.showtabs){
28119             return;
28120         }
28121         
28122         var ctr = this.el.select('.nav-tabs', true).first();
28123          
28124          
28125         var existing = ctr.select('.nav-tab',true);
28126         var qty = existing.getCount();;
28127         
28128         
28129         var tab = ctr.createChild({
28130             tag : 'li',
28131             cls : 'nav-tab' + (qty ? '' : ' active'),
28132             cn : [
28133                 {
28134                     tag : 'a',
28135                     href:'#',
28136                     html : pane.title
28137                 }
28138             ]
28139         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28140         pane.tab = tab;
28141         
28142         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28143         if (!qty) {
28144             pane.el.addClass('active');
28145         }
28146         
28147                 
28148     },
28149     onTabClick : function(ev,un,ob,pane)
28150     {
28151         //Roo.log('tab - prev default');
28152         ev.preventDefault();
28153         
28154         
28155         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28156         pane.tab.addClass('active');
28157         //Roo.log(pane.title);
28158         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28159         // technically we should have a deactivate event.. but maybe add later.
28160         // and it should not de-activate the selected tab...
28161         this.fireEvent('activatepane', pane);
28162         pane.el.addClass('active');
28163         pane.fireEvent('activate');
28164         
28165         
28166     },
28167     
28168     getActivePane : function()
28169     {
28170         var r = false;
28171         Roo.each(this.panes, function(p) {
28172             if(p.el.hasClass('active')){
28173                 r = p;
28174                 return false;
28175             }
28176             
28177             return;
28178         });
28179         
28180         return r;
28181     }
28182     
28183     
28184 });
28185
28186  
28187 /*
28188  * - LGPL
28189  *
28190  * Tab pane
28191  * 
28192  */
28193 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28194 /**
28195  * @class Roo.bootstrap.TabPane
28196  * @extends Roo.bootstrap.Component
28197  * Bootstrap TabPane class
28198  * @cfg {Boolean} active (false | true) Default false
28199  * @cfg {String} title title of panel
28200
28201  * 
28202  * @constructor
28203  * Create a new TabPane
28204  * @param {Object} config The config object
28205  */
28206
28207 Roo.bootstrap.dash.TabPane = function(config){
28208     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28209     
28210     this.addEvents({
28211         // raw events
28212         /**
28213          * @event activate
28214          * When a pane is activated
28215          * @param {Roo.bootstrap.dash.TabPane} pane
28216          */
28217         "activate" : true
28218          
28219     });
28220 };
28221
28222 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28223     
28224     active : false,
28225     title : '',
28226     
28227     // the tabBox that this is attached to.
28228     tab : false,
28229      
28230     getAutoCreate : function() 
28231     {
28232         var cfg = {
28233             tag: 'div',
28234             cls: 'tab-pane'
28235         };
28236         
28237         if(this.active){
28238             cfg.cls += ' active';
28239         }
28240         
28241         return cfg;
28242     },
28243     initEvents  : function()
28244     {
28245         //Roo.log('trigger add pane handler');
28246         this.parent().fireEvent('addpane', this)
28247     },
28248     
28249      /**
28250      * Updates the tab title 
28251      * @param {String} html to set the title to.
28252      */
28253     setTitle: function(str)
28254     {
28255         if (!this.tab) {
28256             return;
28257         }
28258         this.title = str;
28259         this.tab.select('a', true).first().dom.innerHTML = str;
28260         
28261     }
28262     
28263     
28264     
28265 });
28266
28267  
28268
28269
28270  /*
28271  * - LGPL
28272  *
28273  * menu
28274  * 
28275  */
28276 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28277
28278 /**
28279  * @class Roo.bootstrap.menu.Menu
28280  * @extends Roo.bootstrap.Component
28281  * Bootstrap Menu class - container for Menu
28282  * @cfg {String} html Text of the menu
28283  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28284  * @cfg {String} icon Font awesome icon
28285  * @cfg {String} pos Menu align to (top | bottom) default bottom
28286  * 
28287  * 
28288  * @constructor
28289  * Create a new Menu
28290  * @param {Object} config The config object
28291  */
28292
28293
28294 Roo.bootstrap.menu.Menu = function(config){
28295     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28296     
28297     this.addEvents({
28298         /**
28299          * @event beforeshow
28300          * Fires before this menu is displayed
28301          * @param {Roo.bootstrap.menu.Menu} this
28302          */
28303         beforeshow : true,
28304         /**
28305          * @event beforehide
28306          * Fires before this menu is hidden
28307          * @param {Roo.bootstrap.menu.Menu} this
28308          */
28309         beforehide : true,
28310         /**
28311          * @event show
28312          * Fires after this menu is displayed
28313          * @param {Roo.bootstrap.menu.Menu} this
28314          */
28315         show : true,
28316         /**
28317          * @event hide
28318          * Fires after this menu is hidden
28319          * @param {Roo.bootstrap.menu.Menu} this
28320          */
28321         hide : true,
28322         /**
28323          * @event click
28324          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28325          * @param {Roo.bootstrap.menu.Menu} this
28326          * @param {Roo.EventObject} e
28327          */
28328         click : true
28329     });
28330     
28331 };
28332
28333 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28334     
28335     submenu : false,
28336     html : '',
28337     weight : 'default',
28338     icon : false,
28339     pos : 'bottom',
28340     
28341     
28342     getChildContainer : function() {
28343         if(this.isSubMenu){
28344             return this.el;
28345         }
28346         
28347         return this.el.select('ul.dropdown-menu', true).first();  
28348     },
28349     
28350     getAutoCreate : function()
28351     {
28352         var text = [
28353             {
28354                 tag : 'span',
28355                 cls : 'roo-menu-text',
28356                 html : this.html
28357             }
28358         ];
28359         
28360         if(this.icon){
28361             text.unshift({
28362                 tag : 'i',
28363                 cls : 'fa ' + this.icon
28364             })
28365         }
28366         
28367         
28368         var cfg = {
28369             tag : 'div',
28370             cls : 'btn-group',
28371             cn : [
28372                 {
28373                     tag : 'button',
28374                     cls : 'dropdown-button btn btn-' + this.weight,
28375                     cn : text
28376                 },
28377                 {
28378                     tag : 'button',
28379                     cls : 'dropdown-toggle btn btn-' + this.weight,
28380                     cn : [
28381                         {
28382                             tag : 'span',
28383                             cls : 'caret'
28384                         }
28385                     ]
28386                 },
28387                 {
28388                     tag : 'ul',
28389                     cls : 'dropdown-menu'
28390                 }
28391             ]
28392             
28393         };
28394         
28395         if(this.pos == 'top'){
28396             cfg.cls += ' dropup';
28397         }
28398         
28399         if(this.isSubMenu){
28400             cfg = {
28401                 tag : 'ul',
28402                 cls : 'dropdown-menu'
28403             }
28404         }
28405         
28406         return cfg;
28407     },
28408     
28409     onRender : function(ct, position)
28410     {
28411         this.isSubMenu = ct.hasClass('dropdown-submenu');
28412         
28413         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28414     },
28415     
28416     initEvents : function() 
28417     {
28418         if(this.isSubMenu){
28419             return;
28420         }
28421         
28422         this.hidden = true;
28423         
28424         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28425         this.triggerEl.on('click', this.onTriggerPress, this);
28426         
28427         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28428         this.buttonEl.on('click', this.onClick, this);
28429         
28430     },
28431     
28432     list : function()
28433     {
28434         if(this.isSubMenu){
28435             return this.el;
28436         }
28437         
28438         return this.el.select('ul.dropdown-menu', true).first();
28439     },
28440     
28441     onClick : function(e)
28442     {
28443         this.fireEvent("click", this, e);
28444     },
28445     
28446     onTriggerPress  : function(e)
28447     {   
28448         if (this.isVisible()) {
28449             this.hide();
28450         } else {
28451             this.show();
28452         }
28453     },
28454     
28455     isVisible : function(){
28456         return !this.hidden;
28457     },
28458     
28459     show : function()
28460     {
28461         this.fireEvent("beforeshow", this);
28462         
28463         this.hidden = false;
28464         this.el.addClass('open');
28465         
28466         Roo.get(document).on("mouseup", this.onMouseUp, this);
28467         
28468         this.fireEvent("show", this);
28469         
28470         
28471     },
28472     
28473     hide : function()
28474     {
28475         this.fireEvent("beforehide", this);
28476         
28477         this.hidden = true;
28478         this.el.removeClass('open');
28479         
28480         Roo.get(document).un("mouseup", this.onMouseUp);
28481         
28482         this.fireEvent("hide", this);
28483     },
28484     
28485     onMouseUp : function()
28486     {
28487         this.hide();
28488     }
28489     
28490 });
28491
28492  
28493  /*
28494  * - LGPL
28495  *
28496  * menu item
28497  * 
28498  */
28499 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28500
28501 /**
28502  * @class Roo.bootstrap.menu.Item
28503  * @extends Roo.bootstrap.Component
28504  * Bootstrap MenuItem class
28505  * @cfg {Boolean} submenu (true | false) default false
28506  * @cfg {String} html text of the item
28507  * @cfg {String} href the link
28508  * @cfg {Boolean} disable (true | false) default false
28509  * @cfg {Boolean} preventDefault (true | false) default true
28510  * @cfg {String} icon Font awesome icon
28511  * @cfg {String} pos Submenu align to (left | right) default right 
28512  * 
28513  * 
28514  * @constructor
28515  * Create a new Item
28516  * @param {Object} config The config object
28517  */
28518
28519
28520 Roo.bootstrap.menu.Item = function(config){
28521     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28522     this.addEvents({
28523         /**
28524          * @event mouseover
28525          * Fires when the mouse is hovering over this menu
28526          * @param {Roo.bootstrap.menu.Item} this
28527          * @param {Roo.EventObject} e
28528          */
28529         mouseover : true,
28530         /**
28531          * @event mouseout
28532          * Fires when the mouse exits this menu
28533          * @param {Roo.bootstrap.menu.Item} this
28534          * @param {Roo.EventObject} e
28535          */
28536         mouseout : true,
28537         // raw events
28538         /**
28539          * @event click
28540          * The raw click event for the entire grid.
28541          * @param {Roo.EventObject} e
28542          */
28543         click : true
28544     });
28545 };
28546
28547 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28548     
28549     submenu : false,
28550     href : '',
28551     html : '',
28552     preventDefault: true,
28553     disable : false,
28554     icon : false,
28555     pos : 'right',
28556     
28557     getAutoCreate : function()
28558     {
28559         var text = [
28560             {
28561                 tag : 'span',
28562                 cls : 'roo-menu-item-text',
28563                 html : this.html
28564             }
28565         ];
28566         
28567         if(this.icon){
28568             text.unshift({
28569                 tag : 'i',
28570                 cls : 'fa ' + this.icon
28571             })
28572         }
28573         
28574         var cfg = {
28575             tag : 'li',
28576             cn : [
28577                 {
28578                     tag : 'a',
28579                     href : this.href || '#',
28580                     cn : text
28581                 }
28582             ]
28583         };
28584         
28585         if(this.disable){
28586             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28587         }
28588         
28589         if(this.submenu){
28590             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28591             
28592             if(this.pos == 'left'){
28593                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28594             }
28595         }
28596         
28597         return cfg;
28598     },
28599     
28600     initEvents : function() 
28601     {
28602         this.el.on('mouseover', this.onMouseOver, this);
28603         this.el.on('mouseout', this.onMouseOut, this);
28604         
28605         this.el.select('a', true).first().on('click', this.onClick, this);
28606         
28607     },
28608     
28609     onClick : function(e)
28610     {
28611         if(this.preventDefault){
28612             e.preventDefault();
28613         }
28614         
28615         this.fireEvent("click", this, e);
28616     },
28617     
28618     onMouseOver : function(e)
28619     {
28620         if(this.submenu && this.pos == 'left'){
28621             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28622         }
28623         
28624         this.fireEvent("mouseover", this, e);
28625     },
28626     
28627     onMouseOut : function(e)
28628     {
28629         this.fireEvent("mouseout", this, e);
28630     }
28631 });
28632
28633  
28634
28635  /*
28636  * - LGPL
28637  *
28638  * menu separator
28639  * 
28640  */
28641 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28642
28643 /**
28644  * @class Roo.bootstrap.menu.Separator
28645  * @extends Roo.bootstrap.Component
28646  * Bootstrap Separator class
28647  * 
28648  * @constructor
28649  * Create a new Separator
28650  * @param {Object} config The config object
28651  */
28652
28653
28654 Roo.bootstrap.menu.Separator = function(config){
28655     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28656 };
28657
28658 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28659     
28660     getAutoCreate : function(){
28661         var cfg = {
28662             tag : 'li',
28663             cls: 'divider'
28664         };
28665         
28666         return cfg;
28667     }
28668    
28669 });
28670
28671  
28672
28673  /*
28674  * - LGPL
28675  *
28676  * Tooltip
28677  * 
28678  */
28679
28680 /**
28681  * @class Roo.bootstrap.Tooltip
28682  * Bootstrap Tooltip class
28683  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28684  * to determine which dom element triggers the tooltip.
28685  * 
28686  * It needs to add support for additional attributes like tooltip-position
28687  * 
28688  * @constructor
28689  * Create a new Toolti
28690  * @param {Object} config The config object
28691  */
28692
28693 Roo.bootstrap.Tooltip = function(config){
28694     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28695     
28696     this.alignment = Roo.bootstrap.Tooltip.alignment;
28697     
28698     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28699         this.alignment = config.alignment;
28700     }
28701     
28702 };
28703
28704 Roo.apply(Roo.bootstrap.Tooltip, {
28705     /**
28706      * @function init initialize tooltip monitoring.
28707      * @static
28708      */
28709     currentEl : false,
28710     currentTip : false,
28711     currentRegion : false,
28712     
28713     //  init : delay?
28714     
28715     init : function()
28716     {
28717         Roo.get(document).on('mouseover', this.enter ,this);
28718         Roo.get(document).on('mouseout', this.leave, this);
28719          
28720         
28721         this.currentTip = new Roo.bootstrap.Tooltip();
28722     },
28723     
28724     enter : function(ev)
28725     {
28726         var dom = ev.getTarget();
28727         
28728         //Roo.log(['enter',dom]);
28729         var el = Roo.fly(dom);
28730         if (this.currentEl) {
28731             //Roo.log(dom);
28732             //Roo.log(this.currentEl);
28733             //Roo.log(this.currentEl.contains(dom));
28734             if (this.currentEl == el) {
28735                 return;
28736             }
28737             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28738                 return;
28739             }
28740
28741         }
28742         
28743         if (this.currentTip.el) {
28744             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28745         }    
28746         //Roo.log(ev);
28747         
28748         if(!el || el.dom == document){
28749             return;
28750         }
28751         
28752         var bindEl = el;
28753         
28754         // you can not look for children, as if el is the body.. then everythign is the child..
28755         if (!el.attr('tooltip')) { //
28756             if (!el.select("[tooltip]").elements.length) {
28757                 return;
28758             }
28759             // is the mouse over this child...?
28760             bindEl = el.select("[tooltip]").first();
28761             var xy = ev.getXY();
28762             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28763                 //Roo.log("not in region.");
28764                 return;
28765             }
28766             //Roo.log("child element over..");
28767             
28768         }
28769         this.currentEl = bindEl;
28770         this.currentTip.bind(bindEl);
28771         this.currentRegion = Roo.lib.Region.getRegion(dom);
28772         this.currentTip.enter();
28773         
28774     },
28775     leave : function(ev)
28776     {
28777         var dom = ev.getTarget();
28778         //Roo.log(['leave',dom]);
28779         if (!this.currentEl) {
28780             return;
28781         }
28782         
28783         
28784         if (dom != this.currentEl.dom) {
28785             return;
28786         }
28787         var xy = ev.getXY();
28788         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28789             return;
28790         }
28791         // only activate leave if mouse cursor is outside... bounding box..
28792         
28793         
28794         
28795         
28796         if (this.currentTip) {
28797             this.currentTip.leave();
28798         }
28799         //Roo.log('clear currentEl');
28800         this.currentEl = false;
28801         
28802         
28803     },
28804     alignment : {
28805         'left' : ['r-l', [-2,0], 'right'],
28806         'right' : ['l-r', [2,0], 'left'],
28807         'bottom' : ['t-b', [0,2], 'top'],
28808         'top' : [ 'b-t', [0,-2], 'bottom']
28809     }
28810     
28811 });
28812
28813
28814 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28815     
28816     
28817     bindEl : false,
28818     
28819     delay : null, // can be { show : 300 , hide: 500}
28820     
28821     timeout : null,
28822     
28823     hoverState : null, //???
28824     
28825     placement : 'bottom', 
28826     
28827     alignment : false,
28828     
28829     getAutoCreate : function(){
28830     
28831         var cfg = {
28832            cls : 'tooltip',   
28833            role : 'tooltip',
28834            cn : [
28835                 {
28836                     cls : 'tooltip-arrow arrow'
28837                 },
28838                 {
28839                     cls : 'tooltip-inner'
28840                 }
28841            ]
28842         };
28843         
28844         return cfg;
28845     },
28846     bind : function(el)
28847     {
28848         this.bindEl = el;
28849     },
28850     
28851     initEvents : function()
28852     {
28853         this.arrowEl = this.el.select('.arrow', true).first();
28854         this.innerEl = this.el.select('.tooltip-inner', true).first();
28855     },
28856     
28857     enter : function () {
28858        
28859         if (this.timeout != null) {
28860             clearTimeout(this.timeout);
28861         }
28862         
28863         this.hoverState = 'in';
28864          //Roo.log("enter - show");
28865         if (!this.delay || !this.delay.show) {
28866             this.show();
28867             return;
28868         }
28869         var _t = this;
28870         this.timeout = setTimeout(function () {
28871             if (_t.hoverState == 'in') {
28872                 _t.show();
28873             }
28874         }, this.delay.show);
28875     },
28876     leave : function()
28877     {
28878         clearTimeout(this.timeout);
28879     
28880         this.hoverState = 'out';
28881          if (!this.delay || !this.delay.hide) {
28882             this.hide();
28883             return;
28884         }
28885        
28886         var _t = this;
28887         this.timeout = setTimeout(function () {
28888             //Roo.log("leave - timeout");
28889             
28890             if (_t.hoverState == 'out') {
28891                 _t.hide();
28892                 Roo.bootstrap.Tooltip.currentEl = false;
28893             }
28894         }, delay);
28895     },
28896     
28897     show : function (msg)
28898     {
28899         if (!this.el) {
28900             this.render(document.body);
28901         }
28902         // set content.
28903         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28904         
28905         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28906         
28907         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28908         
28909         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28910                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28911         
28912         var placement = typeof this.placement == 'function' ?
28913             this.placement.call(this, this.el, on_el) :
28914             this.placement;
28915             
28916         var autoToken = /\s?auto?\s?/i;
28917         var autoPlace = autoToken.test(placement);
28918         if (autoPlace) {
28919             placement = placement.replace(autoToken, '') || 'top';
28920         }
28921         
28922         //this.el.detach()
28923         //this.el.setXY([0,0]);
28924         this.el.show();
28925         //this.el.dom.style.display='block';
28926         
28927         //this.el.appendTo(on_el);
28928         
28929         var p = this.getPosition();
28930         var box = this.el.getBox();
28931         
28932         if (autoPlace) {
28933             // fixme..
28934         }
28935         
28936         var align = this.alignment[placement];
28937         
28938         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28939         
28940         if(placement == 'top' || placement == 'bottom'){
28941             if(xy[0] < 0){
28942                 placement = 'right';
28943             }
28944             
28945             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28946                 placement = 'left';
28947             }
28948             
28949             var scroll = Roo.select('body', true).first().getScroll();
28950             
28951             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28952                 placement = 'top';
28953             }
28954             
28955             align = this.alignment[placement];
28956             
28957             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28958             
28959         }
28960         
28961         this.el.alignTo(this.bindEl, align[0],align[1]);
28962         //var arrow = this.el.select('.arrow',true).first();
28963         //arrow.set(align[2], 
28964         
28965         this.el.addClass(placement);
28966         this.el.addClass("bs-tooltip-"+ placement);
28967         
28968         this.el.addClass('in fade show');
28969         
28970         this.hoverState = null;
28971         
28972         if (this.el.hasClass('fade')) {
28973             // fade it?
28974         }
28975         
28976         
28977         
28978         
28979         
28980     },
28981     hide : function()
28982     {
28983          
28984         if (!this.el) {
28985             return;
28986         }
28987         //this.el.setXY([0,0]);
28988         this.el.removeClass(['show', 'in']);
28989         //this.el.hide();
28990         
28991     }
28992     
28993 });
28994  
28995
28996  /*
28997  * - LGPL
28998  *
28999  * Location Picker
29000  * 
29001  */
29002
29003 /**
29004  * @class Roo.bootstrap.LocationPicker
29005  * @extends Roo.bootstrap.Component
29006  * Bootstrap LocationPicker class
29007  * @cfg {Number} latitude Position when init default 0
29008  * @cfg {Number} longitude Position when init default 0
29009  * @cfg {Number} zoom default 15
29010  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29011  * @cfg {Boolean} mapTypeControl default false
29012  * @cfg {Boolean} disableDoubleClickZoom default false
29013  * @cfg {Boolean} scrollwheel default true
29014  * @cfg {Boolean} streetViewControl default false
29015  * @cfg {Number} radius default 0
29016  * @cfg {String} locationName
29017  * @cfg {Boolean} draggable default true
29018  * @cfg {Boolean} enableAutocomplete default false
29019  * @cfg {Boolean} enableReverseGeocode default true
29020  * @cfg {String} markerTitle
29021  * 
29022  * @constructor
29023  * Create a new LocationPicker
29024  * @param {Object} config The config object
29025  */
29026
29027
29028 Roo.bootstrap.LocationPicker = function(config){
29029     
29030     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29031     
29032     this.addEvents({
29033         /**
29034          * @event initial
29035          * Fires when the picker initialized.
29036          * @param {Roo.bootstrap.LocationPicker} this
29037          * @param {Google Location} location
29038          */
29039         initial : true,
29040         /**
29041          * @event positionchanged
29042          * Fires when the picker position changed.
29043          * @param {Roo.bootstrap.LocationPicker} this
29044          * @param {Google Location} location
29045          */
29046         positionchanged : true,
29047         /**
29048          * @event resize
29049          * Fires when the map resize.
29050          * @param {Roo.bootstrap.LocationPicker} this
29051          */
29052         resize : true,
29053         /**
29054          * @event show
29055          * Fires when the map show.
29056          * @param {Roo.bootstrap.LocationPicker} this
29057          */
29058         show : true,
29059         /**
29060          * @event hide
29061          * Fires when the map hide.
29062          * @param {Roo.bootstrap.LocationPicker} this
29063          */
29064         hide : true,
29065         /**
29066          * @event mapClick
29067          * Fires when click the map.
29068          * @param {Roo.bootstrap.LocationPicker} this
29069          * @param {Map event} e
29070          */
29071         mapClick : true,
29072         /**
29073          * @event mapRightClick
29074          * Fires when right click the map.
29075          * @param {Roo.bootstrap.LocationPicker} this
29076          * @param {Map event} e
29077          */
29078         mapRightClick : true,
29079         /**
29080          * @event markerClick
29081          * Fires when click the marker.
29082          * @param {Roo.bootstrap.LocationPicker} this
29083          * @param {Map event} e
29084          */
29085         markerClick : true,
29086         /**
29087          * @event markerRightClick
29088          * Fires when right click the marker.
29089          * @param {Roo.bootstrap.LocationPicker} this
29090          * @param {Map event} e
29091          */
29092         markerRightClick : true,
29093         /**
29094          * @event OverlayViewDraw
29095          * Fires when OverlayView Draw
29096          * @param {Roo.bootstrap.LocationPicker} this
29097          */
29098         OverlayViewDraw : true,
29099         /**
29100          * @event OverlayViewOnAdd
29101          * Fires when OverlayView Draw
29102          * @param {Roo.bootstrap.LocationPicker} this
29103          */
29104         OverlayViewOnAdd : true,
29105         /**
29106          * @event OverlayViewOnRemove
29107          * Fires when OverlayView Draw
29108          * @param {Roo.bootstrap.LocationPicker} this
29109          */
29110         OverlayViewOnRemove : true,
29111         /**
29112          * @event OverlayViewShow
29113          * Fires when OverlayView Draw
29114          * @param {Roo.bootstrap.LocationPicker} this
29115          * @param {Pixel} cpx
29116          */
29117         OverlayViewShow : true,
29118         /**
29119          * @event OverlayViewHide
29120          * Fires when OverlayView Draw
29121          * @param {Roo.bootstrap.LocationPicker} this
29122          */
29123         OverlayViewHide : true,
29124         /**
29125          * @event loadexception
29126          * Fires when load google lib failed.
29127          * @param {Roo.bootstrap.LocationPicker} this
29128          */
29129         loadexception : true
29130     });
29131         
29132 };
29133
29134 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29135     
29136     gMapContext: false,
29137     
29138     latitude: 0,
29139     longitude: 0,
29140     zoom: 15,
29141     mapTypeId: false,
29142     mapTypeControl: false,
29143     disableDoubleClickZoom: false,
29144     scrollwheel: true,
29145     streetViewControl: false,
29146     radius: 0,
29147     locationName: '',
29148     draggable: true,
29149     enableAutocomplete: false,
29150     enableReverseGeocode: true,
29151     markerTitle: '',
29152     
29153     getAutoCreate: function()
29154     {
29155
29156         var cfg = {
29157             tag: 'div',
29158             cls: 'roo-location-picker'
29159         };
29160         
29161         return cfg
29162     },
29163     
29164     initEvents: function(ct, position)
29165     {       
29166         if(!this.el.getWidth() || this.isApplied()){
29167             return;
29168         }
29169         
29170         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29171         
29172         this.initial();
29173     },
29174     
29175     initial: function()
29176     {
29177         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29178             this.fireEvent('loadexception', this);
29179             return;
29180         }
29181         
29182         if(!this.mapTypeId){
29183             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29184         }
29185         
29186         this.gMapContext = this.GMapContext();
29187         
29188         this.initOverlayView();
29189         
29190         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29191         
29192         var _this = this;
29193                 
29194         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29195             _this.setPosition(_this.gMapContext.marker.position);
29196         });
29197         
29198         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29199             _this.fireEvent('mapClick', this, event);
29200             
29201         });
29202
29203         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29204             _this.fireEvent('mapRightClick', this, event);
29205             
29206         });
29207         
29208         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29209             _this.fireEvent('markerClick', this, event);
29210             
29211         });
29212
29213         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29214             _this.fireEvent('markerRightClick', this, event);
29215             
29216         });
29217         
29218         this.setPosition(this.gMapContext.location);
29219         
29220         this.fireEvent('initial', this, this.gMapContext.location);
29221     },
29222     
29223     initOverlayView: function()
29224     {
29225         var _this = this;
29226         
29227         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29228             
29229             draw: function()
29230             {
29231                 _this.fireEvent('OverlayViewDraw', _this);
29232             },
29233             
29234             onAdd: function()
29235             {
29236                 _this.fireEvent('OverlayViewOnAdd', _this);
29237             },
29238             
29239             onRemove: function()
29240             {
29241                 _this.fireEvent('OverlayViewOnRemove', _this);
29242             },
29243             
29244             show: function(cpx)
29245             {
29246                 _this.fireEvent('OverlayViewShow', _this, cpx);
29247             },
29248             
29249             hide: function()
29250             {
29251                 _this.fireEvent('OverlayViewHide', _this);
29252             }
29253             
29254         });
29255     },
29256     
29257     fromLatLngToContainerPixel: function(event)
29258     {
29259         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29260     },
29261     
29262     isApplied: function() 
29263     {
29264         return this.getGmapContext() == false ? false : true;
29265     },
29266     
29267     getGmapContext: function() 
29268     {
29269         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29270     },
29271     
29272     GMapContext: function() 
29273     {
29274         var position = new google.maps.LatLng(this.latitude, this.longitude);
29275         
29276         var _map = new google.maps.Map(this.el.dom, {
29277             center: position,
29278             zoom: this.zoom,
29279             mapTypeId: this.mapTypeId,
29280             mapTypeControl: this.mapTypeControl,
29281             disableDoubleClickZoom: this.disableDoubleClickZoom,
29282             scrollwheel: this.scrollwheel,
29283             streetViewControl: this.streetViewControl,
29284             locationName: this.locationName,
29285             draggable: this.draggable,
29286             enableAutocomplete: this.enableAutocomplete,
29287             enableReverseGeocode: this.enableReverseGeocode
29288         });
29289         
29290         var _marker = new google.maps.Marker({
29291             position: position,
29292             map: _map,
29293             title: this.markerTitle,
29294             draggable: this.draggable
29295         });
29296         
29297         return {
29298             map: _map,
29299             marker: _marker,
29300             circle: null,
29301             location: position,
29302             radius: this.radius,
29303             locationName: this.locationName,
29304             addressComponents: {
29305                 formatted_address: null,
29306                 addressLine1: null,
29307                 addressLine2: null,
29308                 streetName: null,
29309                 streetNumber: null,
29310                 city: null,
29311                 district: null,
29312                 state: null,
29313                 stateOrProvince: null
29314             },
29315             settings: this,
29316             domContainer: this.el.dom,
29317             geodecoder: new google.maps.Geocoder()
29318         };
29319     },
29320     
29321     drawCircle: function(center, radius, options) 
29322     {
29323         if (this.gMapContext.circle != null) {
29324             this.gMapContext.circle.setMap(null);
29325         }
29326         if (radius > 0) {
29327             radius *= 1;
29328             options = Roo.apply({}, options, {
29329                 strokeColor: "#0000FF",
29330                 strokeOpacity: .35,
29331                 strokeWeight: 2,
29332                 fillColor: "#0000FF",
29333                 fillOpacity: .2
29334             });
29335             
29336             options.map = this.gMapContext.map;
29337             options.radius = radius;
29338             options.center = center;
29339             this.gMapContext.circle = new google.maps.Circle(options);
29340             return this.gMapContext.circle;
29341         }
29342         
29343         return null;
29344     },
29345     
29346     setPosition: function(location) 
29347     {
29348         this.gMapContext.location = location;
29349         this.gMapContext.marker.setPosition(location);
29350         this.gMapContext.map.panTo(location);
29351         this.drawCircle(location, this.gMapContext.radius, {});
29352         
29353         var _this = this;
29354         
29355         if (this.gMapContext.settings.enableReverseGeocode) {
29356             this.gMapContext.geodecoder.geocode({
29357                 latLng: this.gMapContext.location
29358             }, function(results, status) {
29359                 
29360                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29361                     _this.gMapContext.locationName = results[0].formatted_address;
29362                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29363                     
29364                     _this.fireEvent('positionchanged', this, location);
29365                 }
29366             });
29367             
29368             return;
29369         }
29370         
29371         this.fireEvent('positionchanged', this, location);
29372     },
29373     
29374     resize: function()
29375     {
29376         google.maps.event.trigger(this.gMapContext.map, "resize");
29377         
29378         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29379         
29380         this.fireEvent('resize', this);
29381     },
29382     
29383     setPositionByLatLng: function(latitude, longitude)
29384     {
29385         this.setPosition(new google.maps.LatLng(latitude, longitude));
29386     },
29387     
29388     getCurrentPosition: function() 
29389     {
29390         return {
29391             latitude: this.gMapContext.location.lat(),
29392             longitude: this.gMapContext.location.lng()
29393         };
29394     },
29395     
29396     getAddressName: function() 
29397     {
29398         return this.gMapContext.locationName;
29399     },
29400     
29401     getAddressComponents: function() 
29402     {
29403         return this.gMapContext.addressComponents;
29404     },
29405     
29406     address_component_from_google_geocode: function(address_components) 
29407     {
29408         var result = {};
29409         
29410         for (var i = 0; i < address_components.length; i++) {
29411             var component = address_components[i];
29412             if (component.types.indexOf("postal_code") >= 0) {
29413                 result.postalCode = component.short_name;
29414             } else if (component.types.indexOf("street_number") >= 0) {
29415                 result.streetNumber = component.short_name;
29416             } else if (component.types.indexOf("route") >= 0) {
29417                 result.streetName = component.short_name;
29418             } else if (component.types.indexOf("neighborhood") >= 0) {
29419                 result.city = component.short_name;
29420             } else if (component.types.indexOf("locality") >= 0) {
29421                 result.city = component.short_name;
29422             } else if (component.types.indexOf("sublocality") >= 0) {
29423                 result.district = component.short_name;
29424             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29425                 result.stateOrProvince = component.short_name;
29426             } else if (component.types.indexOf("country") >= 0) {
29427                 result.country = component.short_name;
29428             }
29429         }
29430         
29431         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29432         result.addressLine2 = "";
29433         return result;
29434     },
29435     
29436     setZoomLevel: function(zoom)
29437     {
29438         this.gMapContext.map.setZoom(zoom);
29439     },
29440     
29441     show: function()
29442     {
29443         if(!this.el){
29444             return;
29445         }
29446         
29447         this.el.show();
29448         
29449         this.resize();
29450         
29451         this.fireEvent('show', this);
29452     },
29453     
29454     hide: function()
29455     {
29456         if(!this.el){
29457             return;
29458         }
29459         
29460         this.el.hide();
29461         
29462         this.fireEvent('hide', this);
29463     }
29464     
29465 });
29466
29467 Roo.apply(Roo.bootstrap.LocationPicker, {
29468     
29469     OverlayView : function(map, options)
29470     {
29471         options = options || {};
29472         
29473         this.setMap(map);
29474     }
29475     
29476     
29477 });/**
29478  * @class Roo.bootstrap.Alert
29479  * @extends Roo.bootstrap.Component
29480  * Bootstrap Alert class - shows an alert area box
29481  * eg
29482  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29483   Enter a valid email address
29484 </div>
29485  * @licence LGPL
29486  * @cfg {String} title The title of alert
29487  * @cfg {String} html The content of alert
29488  * @cfg {String} weight (  success | info | warning | danger )
29489  * @cfg {String} faicon font-awesomeicon
29490  * 
29491  * @constructor
29492  * Create a new alert
29493  * @param {Object} config The config object
29494  */
29495
29496
29497 Roo.bootstrap.Alert = function(config){
29498     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29499     
29500 };
29501
29502 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29503     
29504     title: '',
29505     html: '',
29506     weight: false,
29507     faicon: false,
29508     
29509     getAutoCreate : function()
29510     {
29511         
29512         var cfg = {
29513             tag : 'div',
29514             cls : 'alert',
29515             cn : [
29516                 {
29517                     tag : 'i',
29518                     cls : 'roo-alert-icon'
29519                     
29520                 },
29521                 {
29522                     tag : 'b',
29523                     cls : 'roo-alert-title',
29524                     html : this.title
29525                 },
29526                 {
29527                     tag : 'span',
29528                     cls : 'roo-alert-text',
29529                     html : this.html
29530                 }
29531             ]
29532         };
29533         
29534         if(this.faicon){
29535             cfg.cn[0].cls += ' fa ' + this.faicon;
29536         }
29537         
29538         if(this.weight){
29539             cfg.cls += ' alert-' + this.weight;
29540         }
29541         
29542         return cfg;
29543     },
29544     
29545     initEvents: function() 
29546     {
29547         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29548     },
29549     
29550     setTitle : function(str)
29551     {
29552         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29553     },
29554     
29555     setText : function(str)
29556     {
29557         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29558     },
29559     
29560     setWeight : function(weight)
29561     {
29562         if(this.weight){
29563             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29564         }
29565         
29566         this.weight = weight;
29567         
29568         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29569     },
29570     
29571     setIcon : function(icon)
29572     {
29573         if(this.faicon){
29574             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29575         }
29576         
29577         this.faicon = icon;
29578         
29579         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29580     },
29581     
29582     hide: function() 
29583     {
29584         this.el.hide();   
29585     },
29586     
29587     show: function() 
29588     {  
29589         this.el.show();   
29590     }
29591     
29592 });
29593
29594  
29595 /*
29596 * Licence: LGPL
29597 */
29598
29599 /**
29600  * @class Roo.bootstrap.UploadCropbox
29601  * @extends Roo.bootstrap.Component
29602  * Bootstrap UploadCropbox class
29603  * @cfg {String} emptyText show when image has been loaded
29604  * @cfg {String} rotateNotify show when image too small to rotate
29605  * @cfg {Number} errorTimeout default 3000
29606  * @cfg {Number} minWidth default 300
29607  * @cfg {Number} minHeight default 300
29608  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29609  * @cfg {Boolean} isDocument (true|false) default false
29610  * @cfg {String} url action url
29611  * @cfg {String} paramName default 'imageUpload'
29612  * @cfg {String} method default POST
29613  * @cfg {Boolean} loadMask (true|false) default true
29614  * @cfg {Boolean} loadingText default 'Loading...'
29615  * 
29616  * @constructor
29617  * Create a new UploadCropbox
29618  * @param {Object} config The config object
29619  */
29620
29621 Roo.bootstrap.UploadCropbox = function(config){
29622     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29623     
29624     this.addEvents({
29625         /**
29626          * @event beforeselectfile
29627          * Fire before select file
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          */
29630         "beforeselectfile" : true,
29631         /**
29632          * @event initial
29633          * Fire after initEvent
29634          * @param {Roo.bootstrap.UploadCropbox} this
29635          */
29636         "initial" : true,
29637         /**
29638          * @event crop
29639          * Fire after initEvent
29640          * @param {Roo.bootstrap.UploadCropbox} this
29641          * @param {String} data
29642          */
29643         "crop" : true,
29644         /**
29645          * @event prepare
29646          * Fire when preparing the file data
29647          * @param {Roo.bootstrap.UploadCropbox} this
29648          * @param {Object} file
29649          */
29650         "prepare" : true,
29651         /**
29652          * @event exception
29653          * Fire when get exception
29654          * @param {Roo.bootstrap.UploadCropbox} this
29655          * @param {XMLHttpRequest} xhr
29656          */
29657         "exception" : true,
29658         /**
29659          * @event beforeloadcanvas
29660          * Fire before load the canvas
29661          * @param {Roo.bootstrap.UploadCropbox} this
29662          * @param {String} src
29663          */
29664         "beforeloadcanvas" : true,
29665         /**
29666          * @event trash
29667          * Fire when trash image
29668          * @param {Roo.bootstrap.UploadCropbox} this
29669          */
29670         "trash" : true,
29671         /**
29672          * @event download
29673          * Fire when download the image
29674          * @param {Roo.bootstrap.UploadCropbox} this
29675          */
29676         "download" : true,
29677         /**
29678          * @event footerbuttonclick
29679          * Fire when footerbuttonclick
29680          * @param {Roo.bootstrap.UploadCropbox} this
29681          * @param {String} type
29682          */
29683         "footerbuttonclick" : true,
29684         /**
29685          * @event resize
29686          * Fire when resize
29687          * @param {Roo.bootstrap.UploadCropbox} this
29688          */
29689         "resize" : true,
29690         /**
29691          * @event rotate
29692          * Fire when rotate the image
29693          * @param {Roo.bootstrap.UploadCropbox} this
29694          * @param {String} pos
29695          */
29696         "rotate" : true,
29697         /**
29698          * @event inspect
29699          * Fire when inspect the file
29700          * @param {Roo.bootstrap.UploadCropbox} this
29701          * @param {Object} file
29702          */
29703         "inspect" : true,
29704         /**
29705          * @event upload
29706          * Fire when xhr upload the file
29707          * @param {Roo.bootstrap.UploadCropbox} this
29708          * @param {Object} data
29709          */
29710         "upload" : true,
29711         /**
29712          * @event arrange
29713          * Fire when arrange the file data
29714          * @param {Roo.bootstrap.UploadCropbox} this
29715          * @param {Object} formData
29716          */
29717         "arrange" : true
29718     });
29719     
29720     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29721 };
29722
29723 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29724     
29725     emptyText : 'Click to upload image',
29726     rotateNotify : 'Image is too small to rotate',
29727     errorTimeout : 3000,
29728     scale : 0,
29729     baseScale : 1,
29730     rotate : 0,
29731     dragable : false,
29732     pinching : false,
29733     mouseX : 0,
29734     mouseY : 0,
29735     cropData : false,
29736     minWidth : 300,
29737     minHeight : 300,
29738     file : false,
29739     exif : {},
29740     baseRotate : 1,
29741     cropType : 'image/jpeg',
29742     buttons : false,
29743     canvasLoaded : false,
29744     isDocument : false,
29745     method : 'POST',
29746     paramName : 'imageUpload',
29747     loadMask : true,
29748     loadingText : 'Loading...',
29749     maskEl : false,
29750     
29751     getAutoCreate : function()
29752     {
29753         var cfg = {
29754             tag : 'div',
29755             cls : 'roo-upload-cropbox',
29756             cn : [
29757                 {
29758                     tag : 'input',
29759                     cls : 'roo-upload-cropbox-selector',
29760                     type : 'file'
29761                 },
29762                 {
29763                     tag : 'div',
29764                     cls : 'roo-upload-cropbox-body',
29765                     style : 'cursor:pointer',
29766                     cn : [
29767                         {
29768                             tag : 'div',
29769                             cls : 'roo-upload-cropbox-preview'
29770                         },
29771                         {
29772                             tag : 'div',
29773                             cls : 'roo-upload-cropbox-thumb'
29774                         },
29775                         {
29776                             tag : 'div',
29777                             cls : 'roo-upload-cropbox-empty-notify',
29778                             html : this.emptyText
29779                         },
29780                         {
29781                             tag : 'div',
29782                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29783                             html : this.rotateNotify
29784                         }
29785                     ]
29786                 },
29787                 {
29788                     tag : 'div',
29789                     cls : 'roo-upload-cropbox-footer',
29790                     cn : {
29791                         tag : 'div',
29792                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29793                         cn : []
29794                     }
29795                 }
29796             ]
29797         };
29798         
29799         return cfg;
29800     },
29801     
29802     onRender : function(ct, position)
29803     {
29804         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29805         
29806         if (this.buttons.length) {
29807             
29808             Roo.each(this.buttons, function(bb) {
29809                 
29810                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29811                 
29812                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29813                 
29814             }, this);
29815         }
29816         
29817         if(this.loadMask){
29818             this.maskEl = this.el;
29819         }
29820     },
29821     
29822     initEvents : function()
29823     {
29824         this.urlAPI = (window.createObjectURL && window) || 
29825                                 (window.URL && URL.revokeObjectURL && URL) || 
29826                                 (window.webkitURL && webkitURL);
29827                         
29828         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29829         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29830         
29831         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29832         this.selectorEl.hide();
29833         
29834         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29835         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29836         
29837         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29838         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29839         this.thumbEl.hide();
29840         
29841         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29842         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29843         
29844         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29845         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29846         this.errorEl.hide();
29847         
29848         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29849         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29850         this.footerEl.hide();
29851         
29852         this.setThumbBoxSize();
29853         
29854         this.bind();
29855         
29856         this.resize();
29857         
29858         this.fireEvent('initial', this);
29859     },
29860
29861     bind : function()
29862     {
29863         var _this = this;
29864         
29865         window.addEventListener("resize", function() { _this.resize(); } );
29866         
29867         this.bodyEl.on('click', this.beforeSelectFile, this);
29868         
29869         if(Roo.isTouch){
29870             this.bodyEl.on('touchstart', this.onTouchStart, this);
29871             this.bodyEl.on('touchmove', this.onTouchMove, this);
29872             this.bodyEl.on('touchend', this.onTouchEnd, this);
29873         }
29874         
29875         if(!Roo.isTouch){
29876             this.bodyEl.on('mousedown', this.onMouseDown, this);
29877             this.bodyEl.on('mousemove', this.onMouseMove, this);
29878             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29879             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29880             Roo.get(document).on('mouseup', this.onMouseUp, this);
29881         }
29882         
29883         this.selectorEl.on('change', this.onFileSelected, this);
29884     },
29885     
29886     reset : function()
29887     {    
29888         this.scale = 0;
29889         this.baseScale = 1;
29890         this.rotate = 0;
29891         this.baseRotate = 1;
29892         this.dragable = false;
29893         this.pinching = false;
29894         this.mouseX = 0;
29895         this.mouseY = 0;
29896         this.cropData = false;
29897         this.notifyEl.dom.innerHTML = this.emptyText;
29898         
29899         this.selectorEl.dom.value = '';
29900         
29901     },
29902     
29903     resize : function()
29904     {
29905         if(this.fireEvent('resize', this) != false){
29906             this.setThumbBoxPosition();
29907             this.setCanvasPosition();
29908         }
29909     },
29910     
29911     onFooterButtonClick : function(e, el, o, type)
29912     {
29913         switch (type) {
29914             case 'rotate-left' :
29915                 this.onRotateLeft(e);
29916                 break;
29917             case 'rotate-right' :
29918                 this.onRotateRight(e);
29919                 break;
29920             case 'picture' :
29921                 this.beforeSelectFile(e);
29922                 break;
29923             case 'trash' :
29924                 this.trash(e);
29925                 break;
29926             case 'crop' :
29927                 this.crop(e);
29928                 break;
29929             case 'download' :
29930                 this.download(e);
29931                 break;
29932             default :
29933                 break;
29934         }
29935         
29936         this.fireEvent('footerbuttonclick', this, type);
29937     },
29938     
29939     beforeSelectFile : function(e)
29940     {
29941         e.preventDefault();
29942         
29943         if(this.fireEvent('beforeselectfile', this) != false){
29944             this.selectorEl.dom.click();
29945         }
29946     },
29947     
29948     onFileSelected : function(e)
29949     {
29950         e.preventDefault();
29951         
29952         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29953             return;
29954         }
29955         
29956         var file = this.selectorEl.dom.files[0];
29957         
29958         if(this.fireEvent('inspect', this, file) != false){
29959             this.prepare(file);
29960         }
29961         
29962     },
29963     
29964     trash : function(e)
29965     {
29966         this.fireEvent('trash', this);
29967     },
29968     
29969     download : function(e)
29970     {
29971         this.fireEvent('download', this);
29972     },
29973     
29974     loadCanvas : function(src)
29975     {   
29976         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29977             
29978             this.reset();
29979             
29980             this.imageEl = document.createElement('img');
29981             
29982             var _this = this;
29983             
29984             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29985             
29986             this.imageEl.src = src;
29987         }
29988     },
29989     
29990     onLoadCanvas : function()
29991     {   
29992         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29993         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29994         
29995         this.bodyEl.un('click', this.beforeSelectFile, this);
29996         
29997         this.notifyEl.hide();
29998         this.thumbEl.show();
29999         this.footerEl.show();
30000         
30001         this.baseRotateLevel();
30002         
30003         if(this.isDocument){
30004             this.setThumbBoxSize();
30005         }
30006         
30007         this.setThumbBoxPosition();
30008         
30009         this.baseScaleLevel();
30010         
30011         this.draw();
30012         
30013         this.resize();
30014         
30015         this.canvasLoaded = true;
30016         
30017         if(this.loadMask){
30018             this.maskEl.unmask();
30019         }
30020         
30021     },
30022     
30023     setCanvasPosition : function()
30024     {   
30025         if(!this.canvasEl){
30026             return;
30027         }
30028         
30029         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30030         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30031         
30032         this.previewEl.setLeft(pw);
30033         this.previewEl.setTop(ph);
30034         
30035     },
30036     
30037     onMouseDown : function(e)
30038     {   
30039         e.stopEvent();
30040         
30041         this.dragable = true;
30042         this.pinching = false;
30043         
30044         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30045             this.dragable = false;
30046             return;
30047         }
30048         
30049         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30050         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30051         
30052     },
30053     
30054     onMouseMove : function(e)
30055     {   
30056         e.stopEvent();
30057         
30058         if(!this.canvasLoaded){
30059             return;
30060         }
30061         
30062         if (!this.dragable){
30063             return;
30064         }
30065         
30066         var minX = Math.ceil(this.thumbEl.getLeft(true));
30067         var minY = Math.ceil(this.thumbEl.getTop(true));
30068         
30069         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30070         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30071         
30072         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30073         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30074         
30075         x = x - this.mouseX;
30076         y = y - this.mouseY;
30077         
30078         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30079         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30080         
30081         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30082         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30083         
30084         this.previewEl.setLeft(bgX);
30085         this.previewEl.setTop(bgY);
30086         
30087         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30088         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30089     },
30090     
30091     onMouseUp : function(e)
30092     {   
30093         e.stopEvent();
30094         
30095         this.dragable = false;
30096     },
30097     
30098     onMouseWheel : function(e)
30099     {   
30100         e.stopEvent();
30101         
30102         this.startScale = this.scale;
30103         
30104         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30105         
30106         if(!this.zoomable()){
30107             this.scale = this.startScale;
30108             return;
30109         }
30110         
30111         this.draw();
30112         
30113         return;
30114     },
30115     
30116     zoomable : function()
30117     {
30118         var minScale = this.thumbEl.getWidth() / this.minWidth;
30119         
30120         if(this.minWidth < this.minHeight){
30121             minScale = this.thumbEl.getHeight() / this.minHeight;
30122         }
30123         
30124         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30125         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30126         
30127         if(
30128                 this.isDocument &&
30129                 (this.rotate == 0 || this.rotate == 180) && 
30130                 (
30131                     width > this.imageEl.OriginWidth || 
30132                     height > this.imageEl.OriginHeight ||
30133                     (width < this.minWidth && height < this.minHeight)
30134                 )
30135         ){
30136             return false;
30137         }
30138         
30139         if(
30140                 this.isDocument &&
30141                 (this.rotate == 90 || this.rotate == 270) && 
30142                 (
30143                     width > this.imageEl.OriginWidth || 
30144                     height > this.imageEl.OriginHeight ||
30145                     (width < this.minHeight && height < this.minWidth)
30146                 )
30147         ){
30148             return false;
30149         }
30150         
30151         if(
30152                 !this.isDocument &&
30153                 (this.rotate == 0 || this.rotate == 180) && 
30154                 (
30155                     width < this.minWidth || 
30156                     width > this.imageEl.OriginWidth || 
30157                     height < this.minHeight || 
30158                     height > this.imageEl.OriginHeight
30159                 )
30160         ){
30161             return false;
30162         }
30163         
30164         if(
30165                 !this.isDocument &&
30166                 (this.rotate == 90 || this.rotate == 270) && 
30167                 (
30168                     width < this.minHeight || 
30169                     width > this.imageEl.OriginWidth || 
30170                     height < this.minWidth || 
30171                     height > this.imageEl.OriginHeight
30172                 )
30173         ){
30174             return false;
30175         }
30176         
30177         return true;
30178         
30179     },
30180     
30181     onRotateLeft : function(e)
30182     {   
30183         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30184             
30185             var minScale = this.thumbEl.getWidth() / this.minWidth;
30186             
30187             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30188             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30189             
30190             this.startScale = this.scale;
30191             
30192             while (this.getScaleLevel() < minScale){
30193             
30194                 this.scale = this.scale + 1;
30195                 
30196                 if(!this.zoomable()){
30197                     break;
30198                 }
30199                 
30200                 if(
30201                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30202                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30203                 ){
30204                     continue;
30205                 }
30206                 
30207                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30208
30209                 this.draw();
30210                 
30211                 return;
30212             }
30213             
30214             this.scale = this.startScale;
30215             
30216             this.onRotateFail();
30217             
30218             return false;
30219         }
30220         
30221         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30222
30223         if(this.isDocument){
30224             this.setThumbBoxSize();
30225             this.setThumbBoxPosition();
30226             this.setCanvasPosition();
30227         }
30228         
30229         this.draw();
30230         
30231         this.fireEvent('rotate', this, 'left');
30232         
30233     },
30234     
30235     onRotateRight : function(e)
30236     {
30237         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30238             
30239             var minScale = this.thumbEl.getWidth() / this.minWidth;
30240         
30241             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30242             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30243             
30244             this.startScale = this.scale;
30245             
30246             while (this.getScaleLevel() < minScale){
30247             
30248                 this.scale = this.scale + 1;
30249                 
30250                 if(!this.zoomable()){
30251                     break;
30252                 }
30253                 
30254                 if(
30255                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30256                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30257                 ){
30258                     continue;
30259                 }
30260                 
30261                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30262
30263                 this.draw();
30264                 
30265                 return;
30266             }
30267             
30268             this.scale = this.startScale;
30269             
30270             this.onRotateFail();
30271             
30272             return false;
30273         }
30274         
30275         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30276
30277         if(this.isDocument){
30278             this.setThumbBoxSize();
30279             this.setThumbBoxPosition();
30280             this.setCanvasPosition();
30281         }
30282         
30283         this.draw();
30284         
30285         this.fireEvent('rotate', this, 'right');
30286     },
30287     
30288     onRotateFail : function()
30289     {
30290         this.errorEl.show(true);
30291         
30292         var _this = this;
30293         
30294         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30295     },
30296     
30297     draw : function()
30298     {
30299         this.previewEl.dom.innerHTML = '';
30300         
30301         var canvasEl = document.createElement("canvas");
30302         
30303         var contextEl = canvasEl.getContext("2d");
30304         
30305         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30306         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30307         var center = this.imageEl.OriginWidth / 2;
30308         
30309         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30310             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30311             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30312             center = this.imageEl.OriginHeight / 2;
30313         }
30314         
30315         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30316         
30317         contextEl.translate(center, center);
30318         contextEl.rotate(this.rotate * Math.PI / 180);
30319
30320         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30321         
30322         this.canvasEl = document.createElement("canvas");
30323         
30324         this.contextEl = this.canvasEl.getContext("2d");
30325         
30326         switch (this.rotate) {
30327             case 0 :
30328                 
30329                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30330                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30331                 
30332                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30333                 
30334                 break;
30335             case 90 : 
30336                 
30337                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30338                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30339                 
30340                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30341                     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);
30342                     break;
30343                 }
30344                 
30345                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30346                 
30347                 break;
30348             case 180 :
30349                 
30350                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30351                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30352                 
30353                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30354                     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);
30355                     break;
30356                 }
30357                 
30358                 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);
30359                 
30360                 break;
30361             case 270 :
30362                 
30363                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30364                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30365         
30366                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30367                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30368                     break;
30369                 }
30370                 
30371                 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);
30372                 
30373                 break;
30374             default : 
30375                 break;
30376         }
30377         
30378         this.previewEl.appendChild(this.canvasEl);
30379         
30380         this.setCanvasPosition();
30381     },
30382     
30383     crop : function()
30384     {
30385         if(!this.canvasLoaded){
30386             return;
30387         }
30388         
30389         var imageCanvas = document.createElement("canvas");
30390         
30391         var imageContext = imageCanvas.getContext("2d");
30392         
30393         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30394         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30395         
30396         var center = imageCanvas.width / 2;
30397         
30398         imageContext.translate(center, center);
30399         
30400         imageContext.rotate(this.rotate * Math.PI / 180);
30401         
30402         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30403         
30404         var canvas = document.createElement("canvas");
30405         
30406         var context = canvas.getContext("2d");
30407                 
30408         canvas.width = this.minWidth;
30409         canvas.height = this.minHeight;
30410
30411         switch (this.rotate) {
30412             case 0 :
30413                 
30414                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30415                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30416                 
30417                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30418                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30419                 
30420                 var targetWidth = this.minWidth - 2 * x;
30421                 var targetHeight = this.minHeight - 2 * y;
30422                 
30423                 var scale = 1;
30424                 
30425                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30426                     scale = targetWidth / width;
30427                 }
30428                 
30429                 if(x > 0 && y == 0){
30430                     scale = targetHeight / height;
30431                 }
30432                 
30433                 if(x > 0 && y > 0){
30434                     scale = targetWidth / width;
30435                     
30436                     if(width < height){
30437                         scale = targetHeight / height;
30438                     }
30439                 }
30440                 
30441                 context.scale(scale, scale);
30442                 
30443                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30444                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30445
30446                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30447                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30448
30449                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30450                 
30451                 break;
30452             case 90 : 
30453                 
30454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30456                 
30457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30459                 
30460                 var targetWidth = this.minWidth - 2 * x;
30461                 var targetHeight = this.minHeight - 2 * y;
30462                 
30463                 var scale = 1;
30464                 
30465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30466                     scale = targetWidth / width;
30467                 }
30468                 
30469                 if(x > 0 && y == 0){
30470                     scale = targetHeight / height;
30471                 }
30472                 
30473                 if(x > 0 && y > 0){
30474                     scale = targetWidth / width;
30475                     
30476                     if(width < height){
30477                         scale = targetHeight / height;
30478                     }
30479                 }
30480                 
30481                 context.scale(scale, scale);
30482                 
30483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30485
30486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30488                 
30489                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30490                 
30491                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30492                 
30493                 break;
30494             case 180 :
30495                 
30496                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30497                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30498                 
30499                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30500                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30501                 
30502                 var targetWidth = this.minWidth - 2 * x;
30503                 var targetHeight = this.minHeight - 2 * y;
30504                 
30505                 var scale = 1;
30506                 
30507                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30508                     scale = targetWidth / width;
30509                 }
30510                 
30511                 if(x > 0 && y == 0){
30512                     scale = targetHeight / height;
30513                 }
30514                 
30515                 if(x > 0 && y > 0){
30516                     scale = targetWidth / width;
30517                     
30518                     if(width < height){
30519                         scale = targetHeight / height;
30520                     }
30521                 }
30522                 
30523                 context.scale(scale, scale);
30524                 
30525                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30526                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30527
30528                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30529                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30530
30531                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30532                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30533                 
30534                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30535                 
30536                 break;
30537             case 270 :
30538                 
30539                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30540                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30541                 
30542                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30543                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30544                 
30545                 var targetWidth = this.minWidth - 2 * x;
30546                 var targetHeight = this.minHeight - 2 * y;
30547                 
30548                 var scale = 1;
30549                 
30550                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30551                     scale = targetWidth / width;
30552                 }
30553                 
30554                 if(x > 0 && y == 0){
30555                     scale = targetHeight / height;
30556                 }
30557                 
30558                 if(x > 0 && y > 0){
30559                     scale = targetWidth / width;
30560                     
30561                     if(width < height){
30562                         scale = targetHeight / height;
30563                     }
30564                 }
30565                 
30566                 context.scale(scale, scale);
30567                 
30568                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30569                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30570
30571                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30572                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30573                 
30574                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30575                 
30576                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30577                 
30578                 break;
30579             default : 
30580                 break;
30581         }
30582         
30583         this.cropData = canvas.toDataURL(this.cropType);
30584         
30585         if(this.fireEvent('crop', this, this.cropData) !== false){
30586             this.process(this.file, this.cropData);
30587         }
30588         
30589         return;
30590         
30591     },
30592     
30593     setThumbBoxSize : function()
30594     {
30595         var width, height;
30596         
30597         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30598             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30599             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30600             
30601             this.minWidth = width;
30602             this.minHeight = height;
30603             
30604             if(this.rotate == 90 || this.rotate == 270){
30605                 this.minWidth = height;
30606                 this.minHeight = width;
30607             }
30608         }
30609         
30610         height = 300;
30611         width = Math.ceil(this.minWidth * height / this.minHeight);
30612         
30613         if(this.minWidth > this.minHeight){
30614             width = 300;
30615             height = Math.ceil(this.minHeight * width / this.minWidth);
30616         }
30617         
30618         this.thumbEl.setStyle({
30619             width : width + 'px',
30620             height : height + 'px'
30621         });
30622
30623         return;
30624             
30625     },
30626     
30627     setThumbBoxPosition : function()
30628     {
30629         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30630         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30631         
30632         this.thumbEl.setLeft(x);
30633         this.thumbEl.setTop(y);
30634         
30635     },
30636     
30637     baseRotateLevel : function()
30638     {
30639         this.baseRotate = 1;
30640         
30641         if(
30642                 typeof(this.exif) != 'undefined' &&
30643                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30644                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30645         ){
30646             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30647         }
30648         
30649         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30650         
30651     },
30652     
30653     baseScaleLevel : function()
30654     {
30655         var width, height;
30656         
30657         if(this.isDocument){
30658             
30659             if(this.baseRotate == 6 || this.baseRotate == 8){
30660             
30661                 height = this.thumbEl.getHeight();
30662                 this.baseScale = height / this.imageEl.OriginWidth;
30663
30664                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30665                     width = this.thumbEl.getWidth();
30666                     this.baseScale = width / this.imageEl.OriginHeight;
30667                 }
30668
30669                 return;
30670             }
30671
30672             height = this.thumbEl.getHeight();
30673             this.baseScale = height / this.imageEl.OriginHeight;
30674
30675             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30676                 width = this.thumbEl.getWidth();
30677                 this.baseScale = width / this.imageEl.OriginWidth;
30678             }
30679
30680             return;
30681         }
30682         
30683         if(this.baseRotate == 6 || this.baseRotate == 8){
30684             
30685             width = this.thumbEl.getHeight();
30686             this.baseScale = width / this.imageEl.OriginHeight;
30687             
30688             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30689                 height = this.thumbEl.getWidth();
30690                 this.baseScale = height / this.imageEl.OriginHeight;
30691             }
30692             
30693             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30694                 height = this.thumbEl.getWidth();
30695                 this.baseScale = height / this.imageEl.OriginHeight;
30696                 
30697                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30698                     width = this.thumbEl.getHeight();
30699                     this.baseScale = width / this.imageEl.OriginWidth;
30700                 }
30701             }
30702             
30703             return;
30704         }
30705         
30706         width = this.thumbEl.getWidth();
30707         this.baseScale = width / this.imageEl.OriginWidth;
30708         
30709         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30710             height = this.thumbEl.getHeight();
30711             this.baseScale = height / this.imageEl.OriginHeight;
30712         }
30713         
30714         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30715             
30716             height = this.thumbEl.getHeight();
30717             this.baseScale = height / this.imageEl.OriginHeight;
30718             
30719             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30720                 width = this.thumbEl.getWidth();
30721                 this.baseScale = width / this.imageEl.OriginWidth;
30722             }
30723             
30724         }
30725         
30726         return;
30727     },
30728     
30729     getScaleLevel : function()
30730     {
30731         return this.baseScale * Math.pow(1.1, this.scale);
30732     },
30733     
30734     onTouchStart : function(e)
30735     {
30736         if(!this.canvasLoaded){
30737             this.beforeSelectFile(e);
30738             return;
30739         }
30740         
30741         var touches = e.browserEvent.touches;
30742         
30743         if(!touches){
30744             return;
30745         }
30746         
30747         if(touches.length == 1){
30748             this.onMouseDown(e);
30749             return;
30750         }
30751         
30752         if(touches.length != 2){
30753             return;
30754         }
30755         
30756         var coords = [];
30757         
30758         for(var i = 0, finger; finger = touches[i]; i++){
30759             coords.push(finger.pageX, finger.pageY);
30760         }
30761         
30762         var x = Math.pow(coords[0] - coords[2], 2);
30763         var y = Math.pow(coords[1] - coords[3], 2);
30764         
30765         this.startDistance = Math.sqrt(x + y);
30766         
30767         this.startScale = this.scale;
30768         
30769         this.pinching = true;
30770         this.dragable = false;
30771         
30772     },
30773     
30774     onTouchMove : function(e)
30775     {
30776         if(!this.pinching && !this.dragable){
30777             return;
30778         }
30779         
30780         var touches = e.browserEvent.touches;
30781         
30782         if(!touches){
30783             return;
30784         }
30785         
30786         if(this.dragable){
30787             this.onMouseMove(e);
30788             return;
30789         }
30790         
30791         var coords = [];
30792         
30793         for(var i = 0, finger; finger = touches[i]; i++){
30794             coords.push(finger.pageX, finger.pageY);
30795         }
30796         
30797         var x = Math.pow(coords[0] - coords[2], 2);
30798         var y = Math.pow(coords[1] - coords[3], 2);
30799         
30800         this.endDistance = Math.sqrt(x + y);
30801         
30802         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30803         
30804         if(!this.zoomable()){
30805             this.scale = this.startScale;
30806             return;
30807         }
30808         
30809         this.draw();
30810         
30811     },
30812     
30813     onTouchEnd : function(e)
30814     {
30815         this.pinching = false;
30816         this.dragable = false;
30817         
30818     },
30819     
30820     process : function(file, crop)
30821     {
30822         if(this.loadMask){
30823             this.maskEl.mask(this.loadingText);
30824         }
30825         
30826         this.xhr = new XMLHttpRequest();
30827         
30828         file.xhr = this.xhr;
30829
30830         this.xhr.open(this.method, this.url, true);
30831         
30832         var headers = {
30833             "Accept": "application/json",
30834             "Cache-Control": "no-cache",
30835             "X-Requested-With": "XMLHttpRequest"
30836         };
30837         
30838         for (var headerName in headers) {
30839             var headerValue = headers[headerName];
30840             if (headerValue) {
30841                 this.xhr.setRequestHeader(headerName, headerValue);
30842             }
30843         }
30844         
30845         var _this = this;
30846         
30847         this.xhr.onload = function()
30848         {
30849             _this.xhrOnLoad(_this.xhr);
30850         }
30851         
30852         this.xhr.onerror = function()
30853         {
30854             _this.xhrOnError(_this.xhr);
30855         }
30856         
30857         var formData = new FormData();
30858
30859         formData.append('returnHTML', 'NO');
30860         
30861         if(crop){
30862             formData.append('crop', crop);
30863         }
30864         
30865         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30866             formData.append(this.paramName, file, file.name);
30867         }
30868         
30869         if(typeof(file.filename) != 'undefined'){
30870             formData.append('filename', file.filename);
30871         }
30872         
30873         if(typeof(file.mimetype) != 'undefined'){
30874             formData.append('mimetype', file.mimetype);
30875         }
30876         
30877         if(this.fireEvent('arrange', this, formData) != false){
30878             this.xhr.send(formData);
30879         };
30880     },
30881     
30882     xhrOnLoad : function(xhr)
30883     {
30884         if(this.loadMask){
30885             this.maskEl.unmask();
30886         }
30887         
30888         if (xhr.readyState !== 4) {
30889             this.fireEvent('exception', this, xhr);
30890             return;
30891         }
30892
30893         var response = Roo.decode(xhr.responseText);
30894         
30895         if(!response.success){
30896             this.fireEvent('exception', this, xhr);
30897             return;
30898         }
30899         
30900         var response = Roo.decode(xhr.responseText);
30901         
30902         this.fireEvent('upload', this, response);
30903         
30904     },
30905     
30906     xhrOnError : function()
30907     {
30908         if(this.loadMask){
30909             this.maskEl.unmask();
30910         }
30911         
30912         Roo.log('xhr on error');
30913         
30914         var response = Roo.decode(xhr.responseText);
30915           
30916         Roo.log(response);
30917         
30918     },
30919     
30920     prepare : function(file)
30921     {   
30922         if(this.loadMask){
30923             this.maskEl.mask(this.loadingText);
30924         }
30925         
30926         this.file = false;
30927         this.exif = {};
30928         
30929         if(typeof(file) === 'string'){
30930             this.loadCanvas(file);
30931             return;
30932         }
30933         
30934         if(!file || !this.urlAPI){
30935             return;
30936         }
30937         
30938         this.file = file;
30939         this.cropType = file.type;
30940         
30941         var _this = this;
30942         
30943         if(this.fireEvent('prepare', this, this.file) != false){
30944             
30945             var reader = new FileReader();
30946             
30947             reader.onload = function (e) {
30948                 if (e.target.error) {
30949                     Roo.log(e.target.error);
30950                     return;
30951                 }
30952                 
30953                 var buffer = e.target.result,
30954                     dataView = new DataView(buffer),
30955                     offset = 2,
30956                     maxOffset = dataView.byteLength - 4,
30957                     markerBytes,
30958                     markerLength;
30959                 
30960                 if (dataView.getUint16(0) === 0xffd8) {
30961                     while (offset < maxOffset) {
30962                         markerBytes = dataView.getUint16(offset);
30963                         
30964                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30965                             markerLength = dataView.getUint16(offset + 2) + 2;
30966                             if (offset + markerLength > dataView.byteLength) {
30967                                 Roo.log('Invalid meta data: Invalid segment size.');
30968                                 break;
30969                             }
30970                             
30971                             if(markerBytes == 0xffe1){
30972                                 _this.parseExifData(
30973                                     dataView,
30974                                     offset,
30975                                     markerLength
30976                                 );
30977                             }
30978                             
30979                             offset += markerLength;
30980                             
30981                             continue;
30982                         }
30983                         
30984                         break;
30985                     }
30986                     
30987                 }
30988                 
30989                 var url = _this.urlAPI.createObjectURL(_this.file);
30990                 
30991                 _this.loadCanvas(url);
30992                 
30993                 return;
30994             }
30995             
30996             reader.readAsArrayBuffer(this.file);
30997             
30998         }
30999         
31000     },
31001     
31002     parseExifData : function(dataView, offset, length)
31003     {
31004         var tiffOffset = offset + 10,
31005             littleEndian,
31006             dirOffset;
31007     
31008         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31009             // No Exif data, might be XMP data instead
31010             return;
31011         }
31012         
31013         // Check for the ASCII code for "Exif" (0x45786966):
31014         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31015             // No Exif data, might be XMP data instead
31016             return;
31017         }
31018         if (tiffOffset + 8 > dataView.byteLength) {
31019             Roo.log('Invalid Exif data: Invalid segment size.');
31020             return;
31021         }
31022         // Check for the two null bytes:
31023         if (dataView.getUint16(offset + 8) !== 0x0000) {
31024             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31025             return;
31026         }
31027         // Check the byte alignment:
31028         switch (dataView.getUint16(tiffOffset)) {
31029         case 0x4949:
31030             littleEndian = true;
31031             break;
31032         case 0x4D4D:
31033             littleEndian = false;
31034             break;
31035         default:
31036             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31037             return;
31038         }
31039         // Check for the TIFF tag marker (0x002A):
31040         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31041             Roo.log('Invalid Exif data: Missing TIFF marker.');
31042             return;
31043         }
31044         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31045         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31046         
31047         this.parseExifTags(
31048             dataView,
31049             tiffOffset,
31050             tiffOffset + dirOffset,
31051             littleEndian
31052         );
31053     },
31054     
31055     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31056     {
31057         var tagsNumber,
31058             dirEndOffset,
31059             i;
31060         if (dirOffset + 6 > dataView.byteLength) {
31061             Roo.log('Invalid Exif data: Invalid directory offset.');
31062             return;
31063         }
31064         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31065         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31066         if (dirEndOffset + 4 > dataView.byteLength) {
31067             Roo.log('Invalid Exif data: Invalid directory size.');
31068             return;
31069         }
31070         for (i = 0; i < tagsNumber; i += 1) {
31071             this.parseExifTag(
31072                 dataView,
31073                 tiffOffset,
31074                 dirOffset + 2 + 12 * i, // tag offset
31075                 littleEndian
31076             );
31077         }
31078         // Return the offset to the next directory:
31079         return dataView.getUint32(dirEndOffset, littleEndian);
31080     },
31081     
31082     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31083     {
31084         var tag = dataView.getUint16(offset, littleEndian);
31085         
31086         this.exif[tag] = this.getExifValue(
31087             dataView,
31088             tiffOffset,
31089             offset,
31090             dataView.getUint16(offset + 2, littleEndian), // tag type
31091             dataView.getUint32(offset + 4, littleEndian), // tag length
31092             littleEndian
31093         );
31094     },
31095     
31096     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31097     {
31098         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31099             tagSize,
31100             dataOffset,
31101             values,
31102             i,
31103             str,
31104             c;
31105     
31106         if (!tagType) {
31107             Roo.log('Invalid Exif data: Invalid tag type.');
31108             return;
31109         }
31110         
31111         tagSize = tagType.size * length;
31112         // Determine if the value is contained in the dataOffset bytes,
31113         // or if the value at the dataOffset is a pointer to the actual data:
31114         dataOffset = tagSize > 4 ?
31115                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31116         if (dataOffset + tagSize > dataView.byteLength) {
31117             Roo.log('Invalid Exif data: Invalid data offset.');
31118             return;
31119         }
31120         if (length === 1) {
31121             return tagType.getValue(dataView, dataOffset, littleEndian);
31122         }
31123         values = [];
31124         for (i = 0; i < length; i += 1) {
31125             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31126         }
31127         
31128         if (tagType.ascii) {
31129             str = '';
31130             // Concatenate the chars:
31131             for (i = 0; i < values.length; i += 1) {
31132                 c = values[i];
31133                 // Ignore the terminating NULL byte(s):
31134                 if (c === '\u0000') {
31135                     break;
31136                 }
31137                 str += c;
31138             }
31139             return str;
31140         }
31141         return values;
31142     }
31143     
31144 });
31145
31146 Roo.apply(Roo.bootstrap.UploadCropbox, {
31147     tags : {
31148         'Orientation': 0x0112
31149     },
31150     
31151     Orientation: {
31152             1: 0, //'top-left',
31153 //            2: 'top-right',
31154             3: 180, //'bottom-right',
31155 //            4: 'bottom-left',
31156 //            5: 'left-top',
31157             6: 90, //'right-top',
31158 //            7: 'right-bottom',
31159             8: 270 //'left-bottom'
31160     },
31161     
31162     exifTagTypes : {
31163         // byte, 8-bit unsigned int:
31164         1: {
31165             getValue: function (dataView, dataOffset) {
31166                 return dataView.getUint8(dataOffset);
31167             },
31168             size: 1
31169         },
31170         // ascii, 8-bit byte:
31171         2: {
31172             getValue: function (dataView, dataOffset) {
31173                 return String.fromCharCode(dataView.getUint8(dataOffset));
31174             },
31175             size: 1,
31176             ascii: true
31177         },
31178         // short, 16 bit int:
31179         3: {
31180             getValue: function (dataView, dataOffset, littleEndian) {
31181                 return dataView.getUint16(dataOffset, littleEndian);
31182             },
31183             size: 2
31184         },
31185         // long, 32 bit int:
31186         4: {
31187             getValue: function (dataView, dataOffset, littleEndian) {
31188                 return dataView.getUint32(dataOffset, littleEndian);
31189             },
31190             size: 4
31191         },
31192         // rational = two long values, first is numerator, second is denominator:
31193         5: {
31194             getValue: function (dataView, dataOffset, littleEndian) {
31195                 return dataView.getUint32(dataOffset, littleEndian) /
31196                     dataView.getUint32(dataOffset + 4, littleEndian);
31197             },
31198             size: 8
31199         },
31200         // slong, 32 bit signed int:
31201         9: {
31202             getValue: function (dataView, dataOffset, littleEndian) {
31203                 return dataView.getInt32(dataOffset, littleEndian);
31204             },
31205             size: 4
31206         },
31207         // srational, two slongs, first is numerator, second is denominator:
31208         10: {
31209             getValue: function (dataView, dataOffset, littleEndian) {
31210                 return dataView.getInt32(dataOffset, littleEndian) /
31211                     dataView.getInt32(dataOffset + 4, littleEndian);
31212             },
31213             size: 8
31214         }
31215     },
31216     
31217     footer : {
31218         STANDARD : [
31219             {
31220                 tag : 'div',
31221                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31222                 action : 'rotate-left',
31223                 cn : [
31224                     {
31225                         tag : 'button',
31226                         cls : 'btn btn-default',
31227                         html : '<i class="fa fa-undo"></i>'
31228                     }
31229                 ]
31230             },
31231             {
31232                 tag : 'div',
31233                 cls : 'btn-group roo-upload-cropbox-picture',
31234                 action : 'picture',
31235                 cn : [
31236                     {
31237                         tag : 'button',
31238                         cls : 'btn btn-default',
31239                         html : '<i class="fa fa-picture-o"></i>'
31240                     }
31241                 ]
31242             },
31243             {
31244                 tag : 'div',
31245                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31246                 action : 'rotate-right',
31247                 cn : [
31248                     {
31249                         tag : 'button',
31250                         cls : 'btn btn-default',
31251                         html : '<i class="fa fa-repeat"></i>'
31252                     }
31253                 ]
31254             }
31255         ],
31256         DOCUMENT : [
31257             {
31258                 tag : 'div',
31259                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31260                 action : 'rotate-left',
31261                 cn : [
31262                     {
31263                         tag : 'button',
31264                         cls : 'btn btn-default',
31265                         html : '<i class="fa fa-undo"></i>'
31266                     }
31267                 ]
31268             },
31269             {
31270                 tag : 'div',
31271                 cls : 'btn-group roo-upload-cropbox-download',
31272                 action : 'download',
31273                 cn : [
31274                     {
31275                         tag : 'button',
31276                         cls : 'btn btn-default',
31277                         html : '<i class="fa fa-download"></i>'
31278                     }
31279                 ]
31280             },
31281             {
31282                 tag : 'div',
31283                 cls : 'btn-group roo-upload-cropbox-crop',
31284                 action : 'crop',
31285                 cn : [
31286                     {
31287                         tag : 'button',
31288                         cls : 'btn btn-default',
31289                         html : '<i class="fa fa-crop"></i>'
31290                     }
31291                 ]
31292             },
31293             {
31294                 tag : 'div',
31295                 cls : 'btn-group roo-upload-cropbox-trash',
31296                 action : 'trash',
31297                 cn : [
31298                     {
31299                         tag : 'button',
31300                         cls : 'btn btn-default',
31301                         html : '<i class="fa fa-trash"></i>'
31302                     }
31303                 ]
31304             },
31305             {
31306                 tag : 'div',
31307                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31308                 action : 'rotate-right',
31309                 cn : [
31310                     {
31311                         tag : 'button',
31312                         cls : 'btn btn-default',
31313                         html : '<i class="fa fa-repeat"></i>'
31314                     }
31315                 ]
31316             }
31317         ],
31318         ROTATOR : [
31319             {
31320                 tag : 'div',
31321                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31322                 action : 'rotate-left',
31323                 cn : [
31324                     {
31325                         tag : 'button',
31326                         cls : 'btn btn-default',
31327                         html : '<i class="fa fa-undo"></i>'
31328                     }
31329                 ]
31330             },
31331             {
31332                 tag : 'div',
31333                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31334                 action : 'rotate-right',
31335                 cn : [
31336                     {
31337                         tag : 'button',
31338                         cls : 'btn btn-default',
31339                         html : '<i class="fa fa-repeat"></i>'
31340                     }
31341                 ]
31342             }
31343         ]
31344     }
31345 });
31346
31347 /*
31348 * Licence: LGPL
31349 */
31350
31351 /**
31352  * @class Roo.bootstrap.DocumentManager
31353  * @extends Roo.bootstrap.Component
31354  * Bootstrap DocumentManager class
31355  * @cfg {String} paramName default 'imageUpload'
31356  * @cfg {String} toolTipName default 'filename'
31357  * @cfg {String} method default POST
31358  * @cfg {String} url action url
31359  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31360  * @cfg {Boolean} multiple multiple upload default true
31361  * @cfg {Number} thumbSize default 300
31362  * @cfg {String} fieldLabel
31363  * @cfg {Number} labelWidth default 4
31364  * @cfg {String} labelAlign (left|top) default left
31365  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31366 * @cfg {Number} labellg set the width of label (1-12)
31367  * @cfg {Number} labelmd set the width of label (1-12)
31368  * @cfg {Number} labelsm set the width of label (1-12)
31369  * @cfg {Number} labelxs set the width of label (1-12)
31370  * 
31371  * @constructor
31372  * Create a new DocumentManager
31373  * @param {Object} config The config object
31374  */
31375
31376 Roo.bootstrap.DocumentManager = function(config){
31377     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31378     
31379     this.files = [];
31380     this.delegates = [];
31381     
31382     this.addEvents({
31383         /**
31384          * @event initial
31385          * Fire when initial the DocumentManager
31386          * @param {Roo.bootstrap.DocumentManager} this
31387          */
31388         "initial" : true,
31389         /**
31390          * @event inspect
31391          * inspect selected file
31392          * @param {Roo.bootstrap.DocumentManager} this
31393          * @param {File} file
31394          */
31395         "inspect" : true,
31396         /**
31397          * @event exception
31398          * Fire when xhr load exception
31399          * @param {Roo.bootstrap.DocumentManager} this
31400          * @param {XMLHttpRequest} xhr
31401          */
31402         "exception" : true,
31403         /**
31404          * @event afterupload
31405          * Fire when xhr load exception
31406          * @param {Roo.bootstrap.DocumentManager} this
31407          * @param {XMLHttpRequest} xhr
31408          */
31409         "afterupload" : true,
31410         /**
31411          * @event prepare
31412          * prepare the form data
31413          * @param {Roo.bootstrap.DocumentManager} this
31414          * @param {Object} formData
31415          */
31416         "prepare" : true,
31417         /**
31418          * @event remove
31419          * Fire when remove the file
31420          * @param {Roo.bootstrap.DocumentManager} this
31421          * @param {Object} file
31422          */
31423         "remove" : true,
31424         /**
31425          * @event refresh
31426          * Fire after refresh the file
31427          * @param {Roo.bootstrap.DocumentManager} this
31428          */
31429         "refresh" : true,
31430         /**
31431          * @event click
31432          * Fire after click the image
31433          * @param {Roo.bootstrap.DocumentManager} this
31434          * @param {Object} file
31435          */
31436         "click" : true,
31437         /**
31438          * @event edit
31439          * Fire when upload a image and editable set to true
31440          * @param {Roo.bootstrap.DocumentManager} this
31441          * @param {Object} file
31442          */
31443         "edit" : true,
31444         /**
31445          * @event beforeselectfile
31446          * Fire before select file
31447          * @param {Roo.bootstrap.DocumentManager} this
31448          */
31449         "beforeselectfile" : true,
31450         /**
31451          * @event process
31452          * Fire before process file
31453          * @param {Roo.bootstrap.DocumentManager} this
31454          * @param {Object} file
31455          */
31456         "process" : true,
31457         /**
31458          * @event previewrendered
31459          * Fire when preview rendered
31460          * @param {Roo.bootstrap.DocumentManager} this
31461          * @param {Object} file
31462          */
31463         "previewrendered" : true,
31464         /**
31465          */
31466         "previewResize" : true
31467         
31468     });
31469 };
31470
31471 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31472     
31473     boxes : 0,
31474     inputName : '',
31475     thumbSize : 300,
31476     multiple : true,
31477     files : false,
31478     method : 'POST',
31479     url : '',
31480     paramName : 'imageUpload',
31481     toolTipName : 'filename',
31482     fieldLabel : '',
31483     labelWidth : 4,
31484     labelAlign : 'left',
31485     editable : true,
31486     delegates : false,
31487     xhr : false, 
31488     
31489     labellg : 0,
31490     labelmd : 0,
31491     labelsm : 0,
31492     labelxs : 0,
31493     
31494     getAutoCreate : function()
31495     {   
31496         var managerWidget = {
31497             tag : 'div',
31498             cls : 'roo-document-manager',
31499             cn : [
31500                 {
31501                     tag : 'input',
31502                     cls : 'roo-document-manager-selector',
31503                     type : 'file'
31504                 },
31505                 {
31506                     tag : 'div',
31507                     cls : 'roo-document-manager-uploader',
31508                     cn : [
31509                         {
31510                             tag : 'div',
31511                             cls : 'roo-document-manager-upload-btn',
31512                             html : '<i class="fa fa-plus"></i>'
31513                         }
31514                     ]
31515                     
31516                 }
31517             ]
31518         };
31519         
31520         var content = [
31521             {
31522                 tag : 'div',
31523                 cls : 'column col-md-12',
31524                 cn : managerWidget
31525             }
31526         ];
31527         
31528         if(this.fieldLabel.length){
31529             
31530             content = [
31531                 {
31532                     tag : 'div',
31533                     cls : 'column col-md-12',
31534                     html : this.fieldLabel
31535                 },
31536                 {
31537                     tag : 'div',
31538                     cls : 'column col-md-12',
31539                     cn : managerWidget
31540                 }
31541             ];
31542
31543             if(this.labelAlign == 'left'){
31544                 content = [
31545                     {
31546                         tag : 'div',
31547                         cls : 'column',
31548                         html : this.fieldLabel
31549                     },
31550                     {
31551                         tag : 'div',
31552                         cls : 'column',
31553                         cn : managerWidget
31554                     }
31555                 ];
31556                 
31557                 if(this.labelWidth > 12){
31558                     content[0].style = "width: " + this.labelWidth + 'px';
31559                 }
31560
31561                 if(this.labelWidth < 13 && this.labelmd == 0){
31562                     this.labelmd = this.labelWidth;
31563                 }
31564
31565                 if(this.labellg > 0){
31566                     content[0].cls += ' col-lg-' + this.labellg;
31567                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31568                 }
31569
31570                 if(this.labelmd > 0){
31571                     content[0].cls += ' col-md-' + this.labelmd;
31572                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31573                 }
31574
31575                 if(this.labelsm > 0){
31576                     content[0].cls += ' col-sm-' + this.labelsm;
31577                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31578                 }
31579
31580                 if(this.labelxs > 0){
31581                     content[0].cls += ' col-xs-' + this.labelxs;
31582                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31583                 }
31584                 
31585             }
31586         }
31587         
31588         var cfg = {
31589             tag : 'div',
31590             cls : 'row clearfix',
31591             cn : content
31592         };
31593         
31594         return cfg;
31595         
31596     },
31597     
31598     initEvents : function()
31599     {
31600         this.managerEl = this.el.select('.roo-document-manager', true).first();
31601         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31602         
31603         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31604         this.selectorEl.hide();
31605         
31606         if(this.multiple){
31607             this.selectorEl.attr('multiple', 'multiple');
31608         }
31609         
31610         this.selectorEl.on('change', this.onFileSelected, this);
31611         
31612         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31613         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31614         
31615         this.uploader.on('click', this.onUploaderClick, this);
31616         
31617         this.renderProgressDialog();
31618         
31619         var _this = this;
31620         
31621         window.addEventListener("resize", function() { _this.refresh(); } );
31622         
31623         this.fireEvent('initial', this);
31624     },
31625     
31626     renderProgressDialog : function()
31627     {
31628         var _this = this;
31629         
31630         this.progressDialog = new Roo.bootstrap.Modal({
31631             cls : 'roo-document-manager-progress-dialog',
31632             allow_close : false,
31633             animate : false,
31634             title : '',
31635             buttons : [
31636                 {
31637                     name  :'cancel',
31638                     weight : 'danger',
31639                     html : 'Cancel'
31640                 }
31641             ], 
31642             listeners : { 
31643                 btnclick : function() {
31644                     _this.uploadCancel();
31645                     this.hide();
31646                 }
31647             }
31648         });
31649          
31650         this.progressDialog.render(Roo.get(document.body));
31651          
31652         this.progress = new Roo.bootstrap.Progress({
31653             cls : 'roo-document-manager-progress',
31654             active : true,
31655             striped : true
31656         });
31657         
31658         this.progress.render(this.progressDialog.getChildContainer());
31659         
31660         this.progressBar = new Roo.bootstrap.ProgressBar({
31661             cls : 'roo-document-manager-progress-bar',
31662             aria_valuenow : 0,
31663             aria_valuemin : 0,
31664             aria_valuemax : 12,
31665             panel : 'success'
31666         });
31667         
31668         this.progressBar.render(this.progress.getChildContainer());
31669     },
31670     
31671     onUploaderClick : function(e)
31672     {
31673         e.preventDefault();
31674      
31675         if(this.fireEvent('beforeselectfile', this) != false){
31676             this.selectorEl.dom.click();
31677         }
31678         
31679     },
31680     
31681     onFileSelected : function(e)
31682     {
31683         e.preventDefault();
31684         
31685         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31686             return;
31687         }
31688         
31689         Roo.each(this.selectorEl.dom.files, function(file){
31690             if(this.fireEvent('inspect', this, file) != false){
31691                 this.files.push(file);
31692             }
31693         }, this);
31694         
31695         this.queue();
31696         
31697     },
31698     
31699     queue : function()
31700     {
31701         this.selectorEl.dom.value = '';
31702         
31703         if(!this.files || !this.files.length){
31704             return;
31705         }
31706         
31707         if(this.boxes > 0 && this.files.length > this.boxes){
31708             this.files = this.files.slice(0, this.boxes);
31709         }
31710         
31711         this.uploader.show();
31712         
31713         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31714             this.uploader.hide();
31715         }
31716         
31717         var _this = this;
31718         
31719         var files = [];
31720         
31721         var docs = [];
31722         
31723         Roo.each(this.files, function(file){
31724             
31725             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31726                 var f = this.renderPreview(file);
31727                 files.push(f);
31728                 return;
31729             }
31730             
31731             if(file.type.indexOf('image') != -1){
31732                 this.delegates.push(
31733                     (function(){
31734                         _this.process(file);
31735                     }).createDelegate(this)
31736                 );
31737         
31738                 return;
31739             }
31740             
31741             docs.push(
31742                 (function(){
31743                     _this.process(file);
31744                 }).createDelegate(this)
31745             );
31746             
31747         }, this);
31748         
31749         this.files = files;
31750         
31751         this.delegates = this.delegates.concat(docs);
31752         
31753         if(!this.delegates.length){
31754             this.refresh();
31755             return;
31756         }
31757         
31758         this.progressBar.aria_valuemax = this.delegates.length;
31759         
31760         this.arrange();
31761         
31762         return;
31763     },
31764     
31765     arrange : function()
31766     {
31767         if(!this.delegates.length){
31768             this.progressDialog.hide();
31769             this.refresh();
31770             return;
31771         }
31772         
31773         var delegate = this.delegates.shift();
31774         
31775         this.progressDialog.show();
31776         
31777         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31778         
31779         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31780         
31781         delegate();
31782     },
31783     
31784     refresh : function()
31785     {
31786         this.uploader.show();
31787         
31788         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31789             this.uploader.hide();
31790         }
31791         
31792         Roo.isTouch ? this.closable(false) : this.closable(true);
31793         
31794         this.fireEvent('refresh', this);
31795     },
31796     
31797     onRemove : function(e, el, o)
31798     {
31799         e.preventDefault();
31800         
31801         this.fireEvent('remove', this, o);
31802         
31803     },
31804     
31805     remove : function(o)
31806     {
31807         var files = [];
31808         
31809         Roo.each(this.files, function(file){
31810             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31811                 files.push(file);
31812                 return;
31813             }
31814
31815             o.target.remove();
31816
31817         }, this);
31818         
31819         this.files = files;
31820         
31821         this.refresh();
31822     },
31823     
31824     clear : function()
31825     {
31826         Roo.each(this.files, function(file){
31827             if(!file.target){
31828                 return;
31829             }
31830             
31831             file.target.remove();
31832
31833         }, this);
31834         
31835         this.files = [];
31836         
31837         this.refresh();
31838     },
31839     
31840     onClick : function(e, el, o)
31841     {
31842         e.preventDefault();
31843         
31844         this.fireEvent('click', this, o);
31845         
31846     },
31847     
31848     closable : function(closable)
31849     {
31850         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31851             
31852             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31853             
31854             if(closable){
31855                 el.show();
31856                 return;
31857             }
31858             
31859             el.hide();
31860             
31861         }, this);
31862     },
31863     
31864     xhrOnLoad : function(xhr)
31865     {
31866         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31867             el.remove();
31868         }, this);
31869         
31870         if (xhr.readyState !== 4) {
31871             this.arrange();
31872             this.fireEvent('exception', this, xhr);
31873             return;
31874         }
31875
31876         var response = Roo.decode(xhr.responseText);
31877         
31878         if(!response.success){
31879             this.arrange();
31880             this.fireEvent('exception', this, xhr);
31881             return;
31882         }
31883         
31884         var file = this.renderPreview(response.data);
31885         
31886         this.files.push(file);
31887         
31888         this.arrange();
31889         
31890         this.fireEvent('afterupload', this, xhr);
31891         
31892     },
31893     
31894     xhrOnError : function(xhr)
31895     {
31896         Roo.log('xhr on error');
31897         
31898         var response = Roo.decode(xhr.responseText);
31899           
31900         Roo.log(response);
31901         
31902         this.arrange();
31903     },
31904     
31905     process : function(file)
31906     {
31907         if(this.fireEvent('process', this, file) !== false){
31908             if(this.editable && file.type.indexOf('image') != -1){
31909                 this.fireEvent('edit', this, file);
31910                 return;
31911             }
31912
31913             this.uploadStart(file, false);
31914
31915             return;
31916         }
31917         
31918     },
31919     
31920     uploadStart : function(file, crop)
31921     {
31922         this.xhr = new XMLHttpRequest();
31923         
31924         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31925             this.arrange();
31926             return;
31927         }
31928         
31929         file.xhr = this.xhr;
31930             
31931         this.managerEl.createChild({
31932             tag : 'div',
31933             cls : 'roo-document-manager-loading',
31934             cn : [
31935                 {
31936                     tag : 'div',
31937                     tooltip : file.name,
31938                     cls : 'roo-document-manager-thumb',
31939                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31940                 }
31941             ]
31942
31943         });
31944
31945         this.xhr.open(this.method, this.url, true);
31946         
31947         var headers = {
31948             "Accept": "application/json",
31949             "Cache-Control": "no-cache",
31950             "X-Requested-With": "XMLHttpRequest"
31951         };
31952         
31953         for (var headerName in headers) {
31954             var headerValue = headers[headerName];
31955             if (headerValue) {
31956                 this.xhr.setRequestHeader(headerName, headerValue);
31957             }
31958         }
31959         
31960         var _this = this;
31961         
31962         this.xhr.onload = function()
31963         {
31964             _this.xhrOnLoad(_this.xhr);
31965         }
31966         
31967         this.xhr.onerror = function()
31968         {
31969             _this.xhrOnError(_this.xhr);
31970         }
31971         
31972         var formData = new FormData();
31973
31974         formData.append('returnHTML', 'NO');
31975         
31976         if(crop){
31977             formData.append('crop', crop);
31978         }
31979         
31980         formData.append(this.paramName, file, file.name);
31981         
31982         var options = {
31983             file : file, 
31984             manually : false
31985         };
31986         
31987         if(this.fireEvent('prepare', this, formData, options) != false){
31988             
31989             if(options.manually){
31990                 return;
31991             }
31992             
31993             this.xhr.send(formData);
31994             return;
31995         };
31996         
31997         this.uploadCancel();
31998     },
31999     
32000     uploadCancel : function()
32001     {
32002         if (this.xhr) {
32003             this.xhr.abort();
32004         }
32005         
32006         this.delegates = [];
32007         
32008         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32009             el.remove();
32010         }, this);
32011         
32012         this.arrange();
32013     },
32014     
32015     renderPreview : function(file)
32016     {
32017         if(typeof(file.target) != 'undefined' && file.target){
32018             return file;
32019         }
32020         
32021         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32022         
32023         var previewEl = this.managerEl.createChild({
32024             tag : 'div',
32025             cls : 'roo-document-manager-preview',
32026             cn : [
32027                 {
32028                     tag : 'div',
32029                     tooltip : file[this.toolTipName],
32030                     cls : 'roo-document-manager-thumb',
32031                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32032                 },
32033                 {
32034                     tag : 'button',
32035                     cls : 'close',
32036                     html : '<i class="fa fa-times-circle"></i>'
32037                 }
32038             ]
32039         });
32040
32041         var close = previewEl.select('button.close', true).first();
32042
32043         close.on('click', this.onRemove, this, file);
32044
32045         file.target = previewEl;
32046
32047         var image = previewEl.select('img', true).first();
32048         
32049         var _this = this;
32050         
32051         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32052         
32053         image.on('click', this.onClick, this, file);
32054         
32055         this.fireEvent('previewrendered', this, file);
32056         
32057         return file;
32058         
32059     },
32060     
32061     onPreviewLoad : function(file, image)
32062     {
32063         if(typeof(file.target) == 'undefined' || !file.target){
32064             return;
32065         }
32066         
32067         var width = image.dom.naturalWidth || image.dom.width;
32068         var height = image.dom.naturalHeight || image.dom.height;
32069         
32070         if(!this.previewResize) {
32071             return;
32072         }
32073         
32074         if(width > height){
32075             file.target.addClass('wide');
32076             return;
32077         }
32078         
32079         file.target.addClass('tall');
32080         return;
32081         
32082     },
32083     
32084     uploadFromSource : function(file, crop)
32085     {
32086         this.xhr = new XMLHttpRequest();
32087         
32088         this.managerEl.createChild({
32089             tag : 'div',
32090             cls : 'roo-document-manager-loading',
32091             cn : [
32092                 {
32093                     tag : 'div',
32094                     tooltip : file.name,
32095                     cls : 'roo-document-manager-thumb',
32096                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32097                 }
32098             ]
32099
32100         });
32101
32102         this.xhr.open(this.method, this.url, true);
32103         
32104         var headers = {
32105             "Accept": "application/json",
32106             "Cache-Control": "no-cache",
32107             "X-Requested-With": "XMLHttpRequest"
32108         };
32109         
32110         for (var headerName in headers) {
32111             var headerValue = headers[headerName];
32112             if (headerValue) {
32113                 this.xhr.setRequestHeader(headerName, headerValue);
32114             }
32115         }
32116         
32117         var _this = this;
32118         
32119         this.xhr.onload = function()
32120         {
32121             _this.xhrOnLoad(_this.xhr);
32122         }
32123         
32124         this.xhr.onerror = function()
32125         {
32126             _this.xhrOnError(_this.xhr);
32127         }
32128         
32129         var formData = new FormData();
32130
32131         formData.append('returnHTML', 'NO');
32132         
32133         formData.append('crop', crop);
32134         
32135         if(typeof(file.filename) != 'undefined'){
32136             formData.append('filename', file.filename);
32137         }
32138         
32139         if(typeof(file.mimetype) != 'undefined'){
32140             formData.append('mimetype', file.mimetype);
32141         }
32142         
32143         Roo.log(formData);
32144         
32145         if(this.fireEvent('prepare', this, formData) != false){
32146             this.xhr.send(formData);
32147         };
32148     }
32149 });
32150
32151 /*
32152 * Licence: LGPL
32153 */
32154
32155 /**
32156  * @class Roo.bootstrap.DocumentViewer
32157  * @extends Roo.bootstrap.Component
32158  * Bootstrap DocumentViewer class
32159  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32160  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32161  * 
32162  * @constructor
32163  * Create a new DocumentViewer
32164  * @param {Object} config The config object
32165  */
32166
32167 Roo.bootstrap.DocumentViewer = function(config){
32168     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32169     
32170     this.addEvents({
32171         /**
32172          * @event initial
32173          * Fire after initEvent
32174          * @param {Roo.bootstrap.DocumentViewer} this
32175          */
32176         "initial" : true,
32177         /**
32178          * @event click
32179          * Fire after click
32180          * @param {Roo.bootstrap.DocumentViewer} this
32181          */
32182         "click" : true,
32183         /**
32184          * @event download
32185          * Fire after download button
32186          * @param {Roo.bootstrap.DocumentViewer} this
32187          */
32188         "download" : true,
32189         /**
32190          * @event trash
32191          * Fire after trash button
32192          * @param {Roo.bootstrap.DocumentViewer} this
32193          */
32194         "trash" : true
32195         
32196     });
32197 };
32198
32199 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32200     
32201     showDownload : true,
32202     
32203     showTrash : true,
32204     
32205     getAutoCreate : function()
32206     {
32207         var cfg = {
32208             tag : 'div',
32209             cls : 'roo-document-viewer',
32210             cn : [
32211                 {
32212                     tag : 'div',
32213                     cls : 'roo-document-viewer-body',
32214                     cn : [
32215                         {
32216                             tag : 'div',
32217                             cls : 'roo-document-viewer-thumb',
32218                             cn : [
32219                                 {
32220                                     tag : 'img',
32221                                     cls : 'roo-document-viewer-image'
32222                                 }
32223                             ]
32224                         }
32225                     ]
32226                 },
32227                 {
32228                     tag : 'div',
32229                     cls : 'roo-document-viewer-footer',
32230                     cn : {
32231                         tag : 'div',
32232                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32233                         cn : [
32234                             {
32235                                 tag : 'div',
32236                                 cls : 'btn-group roo-document-viewer-download',
32237                                 cn : [
32238                                     {
32239                                         tag : 'button',
32240                                         cls : 'btn btn-default',
32241                                         html : '<i class="fa fa-download"></i>'
32242                                     }
32243                                 ]
32244                             },
32245                             {
32246                                 tag : 'div',
32247                                 cls : 'btn-group roo-document-viewer-trash',
32248                                 cn : [
32249                                     {
32250                                         tag : 'button',
32251                                         cls : 'btn btn-default',
32252                                         html : '<i class="fa fa-trash"></i>'
32253                                     }
32254                                 ]
32255                             }
32256                         ]
32257                     }
32258                 }
32259             ]
32260         };
32261         
32262         return cfg;
32263     },
32264     
32265     initEvents : function()
32266     {
32267         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32268         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32269         
32270         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32271         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32272         
32273         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32274         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32275         
32276         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32277         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32278         
32279         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32280         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32281         
32282         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32283         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32284         
32285         this.bodyEl.on('click', this.onClick, this);
32286         this.downloadBtn.on('click', this.onDownload, this);
32287         this.trashBtn.on('click', this.onTrash, this);
32288         
32289         this.downloadBtn.hide();
32290         this.trashBtn.hide();
32291         
32292         if(this.showDownload){
32293             this.downloadBtn.show();
32294         }
32295         
32296         if(this.showTrash){
32297             this.trashBtn.show();
32298         }
32299         
32300         if(!this.showDownload && !this.showTrash) {
32301             this.footerEl.hide();
32302         }
32303         
32304     },
32305     
32306     initial : function()
32307     {
32308         this.fireEvent('initial', this);
32309         
32310     },
32311     
32312     onClick : function(e)
32313     {
32314         e.preventDefault();
32315         
32316         this.fireEvent('click', this);
32317     },
32318     
32319     onDownload : function(e)
32320     {
32321         e.preventDefault();
32322         
32323         this.fireEvent('download', this);
32324     },
32325     
32326     onTrash : function(e)
32327     {
32328         e.preventDefault();
32329         
32330         this.fireEvent('trash', this);
32331     }
32332     
32333 });
32334 /*
32335  * - LGPL
32336  *
32337  * nav progress bar
32338  * 
32339  */
32340
32341 /**
32342  * @class Roo.bootstrap.NavProgressBar
32343  * @extends Roo.bootstrap.Component
32344  * Bootstrap NavProgressBar class
32345  * 
32346  * @constructor
32347  * Create a new nav progress bar
32348  * @param {Object} config The config object
32349  */
32350
32351 Roo.bootstrap.NavProgressBar = function(config){
32352     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32353
32354     this.bullets = this.bullets || [];
32355    
32356 //    Roo.bootstrap.NavProgressBar.register(this);
32357      this.addEvents({
32358         /**
32359              * @event changed
32360              * Fires when the active item changes
32361              * @param {Roo.bootstrap.NavProgressBar} this
32362              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32363              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32364          */
32365         'changed': true
32366      });
32367     
32368 };
32369
32370 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32371     
32372     bullets : [],
32373     barItems : [],
32374     
32375     getAutoCreate : function()
32376     {
32377         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32378         
32379         cfg = {
32380             tag : 'div',
32381             cls : 'roo-navigation-bar-group',
32382             cn : [
32383                 {
32384                     tag : 'div',
32385                     cls : 'roo-navigation-top-bar'
32386                 },
32387                 {
32388                     tag : 'div',
32389                     cls : 'roo-navigation-bullets-bar',
32390                     cn : [
32391                         {
32392                             tag : 'ul',
32393                             cls : 'roo-navigation-bar'
32394                         }
32395                     ]
32396                 },
32397                 
32398                 {
32399                     tag : 'div',
32400                     cls : 'roo-navigation-bottom-bar'
32401                 }
32402             ]
32403             
32404         };
32405         
32406         return cfg;
32407         
32408     },
32409     
32410     initEvents: function() 
32411     {
32412         
32413     },
32414     
32415     onRender : function(ct, position) 
32416     {
32417         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32418         
32419         if(this.bullets.length){
32420             Roo.each(this.bullets, function(b){
32421                this.addItem(b);
32422             }, this);
32423         }
32424         
32425         this.format();
32426         
32427     },
32428     
32429     addItem : function(cfg)
32430     {
32431         var item = new Roo.bootstrap.NavProgressItem(cfg);
32432         
32433         item.parentId = this.id;
32434         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32435         
32436         if(cfg.html){
32437             var top = new Roo.bootstrap.Element({
32438                 tag : 'div',
32439                 cls : 'roo-navigation-bar-text'
32440             });
32441             
32442             var bottom = new Roo.bootstrap.Element({
32443                 tag : 'div',
32444                 cls : 'roo-navigation-bar-text'
32445             });
32446             
32447             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32448             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32449             
32450             var topText = new Roo.bootstrap.Element({
32451                 tag : 'span',
32452                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32453             });
32454             
32455             var bottomText = new Roo.bootstrap.Element({
32456                 tag : 'span',
32457                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32458             });
32459             
32460             topText.onRender(top.el, null);
32461             bottomText.onRender(bottom.el, null);
32462             
32463             item.topEl = top;
32464             item.bottomEl = bottom;
32465         }
32466         
32467         this.barItems.push(item);
32468         
32469         return item;
32470     },
32471     
32472     getActive : function()
32473     {
32474         var active = false;
32475         
32476         Roo.each(this.barItems, function(v){
32477             
32478             if (!v.isActive()) {
32479                 return;
32480             }
32481             
32482             active = v;
32483             return false;
32484             
32485         });
32486         
32487         return active;
32488     },
32489     
32490     setActiveItem : function(item)
32491     {
32492         var prev = false;
32493         
32494         Roo.each(this.barItems, function(v){
32495             if (v.rid == item.rid) {
32496                 return ;
32497             }
32498             
32499             if (v.isActive()) {
32500                 v.setActive(false);
32501                 prev = v;
32502             }
32503         });
32504
32505         item.setActive(true);
32506         
32507         this.fireEvent('changed', this, item, prev);
32508     },
32509     
32510     getBarItem: function(rid)
32511     {
32512         var ret = false;
32513         
32514         Roo.each(this.barItems, function(e) {
32515             if (e.rid != rid) {
32516                 return;
32517             }
32518             
32519             ret =  e;
32520             return false;
32521         });
32522         
32523         return ret;
32524     },
32525     
32526     indexOfItem : function(item)
32527     {
32528         var index = false;
32529         
32530         Roo.each(this.barItems, function(v, i){
32531             
32532             if (v.rid != item.rid) {
32533                 return;
32534             }
32535             
32536             index = i;
32537             return false
32538         });
32539         
32540         return index;
32541     },
32542     
32543     setActiveNext : function()
32544     {
32545         var i = this.indexOfItem(this.getActive());
32546         
32547         if (i > this.barItems.length) {
32548             return;
32549         }
32550         
32551         this.setActiveItem(this.barItems[i+1]);
32552     },
32553     
32554     setActivePrev : function()
32555     {
32556         var i = this.indexOfItem(this.getActive());
32557         
32558         if (i  < 1) {
32559             return;
32560         }
32561         
32562         this.setActiveItem(this.barItems[i-1]);
32563     },
32564     
32565     format : function()
32566     {
32567         if(!this.barItems.length){
32568             return;
32569         }
32570      
32571         var width = 100 / this.barItems.length;
32572         
32573         Roo.each(this.barItems, function(i){
32574             i.el.setStyle('width', width + '%');
32575             i.topEl.el.setStyle('width', width + '%');
32576             i.bottomEl.el.setStyle('width', width + '%');
32577         }, this);
32578         
32579     }
32580     
32581 });
32582 /*
32583  * - LGPL
32584  *
32585  * Nav Progress Item
32586  * 
32587  */
32588
32589 /**
32590  * @class Roo.bootstrap.NavProgressItem
32591  * @extends Roo.bootstrap.Component
32592  * Bootstrap NavProgressItem class
32593  * @cfg {String} rid the reference id
32594  * @cfg {Boolean} active (true|false) Is item active default false
32595  * @cfg {Boolean} disabled (true|false) Is item active default false
32596  * @cfg {String} html
32597  * @cfg {String} position (top|bottom) text position default bottom
32598  * @cfg {String} icon show icon instead of number
32599  * 
32600  * @constructor
32601  * Create a new NavProgressItem
32602  * @param {Object} config The config object
32603  */
32604 Roo.bootstrap.NavProgressItem = function(config){
32605     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32606     this.addEvents({
32607         // raw events
32608         /**
32609          * @event click
32610          * The raw click event for the entire grid.
32611          * @param {Roo.bootstrap.NavProgressItem} this
32612          * @param {Roo.EventObject} e
32613          */
32614         "click" : true
32615     });
32616    
32617 };
32618
32619 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32620     
32621     rid : '',
32622     active : false,
32623     disabled : false,
32624     html : '',
32625     position : 'bottom',
32626     icon : false,
32627     
32628     getAutoCreate : function()
32629     {
32630         var iconCls = 'roo-navigation-bar-item-icon';
32631         
32632         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32633         
32634         var cfg = {
32635             tag: 'li',
32636             cls: 'roo-navigation-bar-item',
32637             cn : [
32638                 {
32639                     tag : 'i',
32640                     cls : iconCls
32641                 }
32642             ]
32643         };
32644         
32645         if(this.active){
32646             cfg.cls += ' active';
32647         }
32648         if(this.disabled){
32649             cfg.cls += ' disabled';
32650         }
32651         
32652         return cfg;
32653     },
32654     
32655     disable : function()
32656     {
32657         this.setDisabled(true);
32658     },
32659     
32660     enable : function()
32661     {
32662         this.setDisabled(false);
32663     },
32664     
32665     initEvents: function() 
32666     {
32667         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32668         
32669         this.iconEl.on('click', this.onClick, this);
32670     },
32671     
32672     onClick : function(e)
32673     {
32674         e.preventDefault();
32675         
32676         if(this.disabled){
32677             return;
32678         }
32679         
32680         if(this.fireEvent('click', this, e) === false){
32681             return;
32682         };
32683         
32684         this.parent().setActiveItem(this);
32685     },
32686     
32687     isActive: function () 
32688     {
32689         return this.active;
32690     },
32691     
32692     setActive : function(state)
32693     {
32694         if(this.active == state){
32695             return;
32696         }
32697         
32698         this.active = state;
32699         
32700         if (state) {
32701             this.el.addClass('active');
32702             return;
32703         }
32704         
32705         this.el.removeClass('active');
32706         
32707         return;
32708     },
32709     
32710     setDisabled : function(state)
32711     {
32712         if(this.disabled == state){
32713             return;
32714         }
32715         
32716         this.disabled = state;
32717         
32718         if (state) {
32719             this.el.addClass('disabled');
32720             return;
32721         }
32722         
32723         this.el.removeClass('disabled');
32724     },
32725     
32726     tooltipEl : function()
32727     {
32728         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32729     }
32730 });
32731  
32732
32733  /*
32734  * - LGPL
32735  *
32736  * FieldLabel
32737  * 
32738  */
32739
32740 /**
32741  * @class Roo.bootstrap.FieldLabel
32742  * @extends Roo.bootstrap.Component
32743  * Bootstrap FieldLabel class
32744  * @cfg {String} html contents of the element
32745  * @cfg {String} tag tag of the element default label
32746  * @cfg {String} cls class of the element
32747  * @cfg {String} target label target 
32748  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32749  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32750  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32751  * @cfg {String} iconTooltip default "This field is required"
32752  * @cfg {String} indicatorpos (left|right) default left
32753  * 
32754  * @constructor
32755  * Create a new FieldLabel
32756  * @param {Object} config The config object
32757  */
32758
32759 Roo.bootstrap.FieldLabel = function(config){
32760     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32761     
32762     this.addEvents({
32763             /**
32764              * @event invalid
32765              * Fires after the field has been marked as invalid.
32766              * @param {Roo.form.FieldLabel} this
32767              * @param {String} msg The validation message
32768              */
32769             invalid : true,
32770             /**
32771              * @event valid
32772              * Fires after the field has been validated with no errors.
32773              * @param {Roo.form.FieldLabel} this
32774              */
32775             valid : true
32776         });
32777 };
32778
32779 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32780     
32781     tag: 'label',
32782     cls: '',
32783     html: '',
32784     target: '',
32785     allowBlank : true,
32786     invalidClass : 'has-warning',
32787     validClass : 'has-success',
32788     iconTooltip : 'This field is required',
32789     indicatorpos : 'left',
32790     
32791     getAutoCreate : function(){
32792         
32793         var cls = "";
32794         if (!this.allowBlank) {
32795             cls  = "visible";
32796         }
32797         
32798         var cfg = {
32799             tag : this.tag,
32800             cls : 'roo-bootstrap-field-label ' + this.cls,
32801             for : this.target,
32802             cn : [
32803                 {
32804                     tag : 'i',
32805                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32806                     tooltip : this.iconTooltip
32807                 },
32808                 {
32809                     tag : 'span',
32810                     html : this.html
32811                 }
32812             ] 
32813         };
32814         
32815         if(this.indicatorpos == 'right'){
32816             var cfg = {
32817                 tag : this.tag,
32818                 cls : 'roo-bootstrap-field-label ' + this.cls,
32819                 for : this.target,
32820                 cn : [
32821                     {
32822                         tag : 'span',
32823                         html : this.html
32824                     },
32825                     {
32826                         tag : 'i',
32827                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32828                         tooltip : this.iconTooltip
32829                     }
32830                 ] 
32831             };
32832         }
32833         
32834         return cfg;
32835     },
32836     
32837     initEvents: function() 
32838     {
32839         Roo.bootstrap.Element.superclass.initEvents.call(this);
32840         
32841         this.indicator = this.indicatorEl();
32842         
32843         if(this.indicator){
32844             this.indicator.removeClass('visible');
32845             this.indicator.addClass('invisible');
32846         }
32847         
32848         Roo.bootstrap.FieldLabel.register(this);
32849     },
32850     
32851     indicatorEl : function()
32852     {
32853         var indicator = this.el.select('i.roo-required-indicator',true).first();
32854         
32855         if(!indicator){
32856             return false;
32857         }
32858         
32859         return indicator;
32860         
32861     },
32862     
32863     /**
32864      * Mark this field as valid
32865      */
32866     markValid : function()
32867     {
32868         if(this.indicator){
32869             this.indicator.removeClass('visible');
32870             this.indicator.addClass('invisible');
32871         }
32872         if (Roo.bootstrap.version == 3) {
32873             this.el.removeClass(this.invalidClass);
32874             this.el.addClass(this.validClass);
32875         } else {
32876             this.el.removeClass('is-invalid');
32877             this.el.addClass('is-valid');
32878         }
32879         
32880         
32881         this.fireEvent('valid', this);
32882     },
32883     
32884     /**
32885      * Mark this field as invalid
32886      * @param {String} msg The validation message
32887      */
32888     markInvalid : function(msg)
32889     {
32890         if(this.indicator){
32891             this.indicator.removeClass('invisible');
32892             this.indicator.addClass('visible');
32893         }
32894           if (Roo.bootstrap.version == 3) {
32895             this.el.removeClass(this.validClass);
32896             this.el.addClass(this.invalidClass);
32897         } else {
32898             this.el.removeClass('is-valid');
32899             this.el.addClass('is-invalid');
32900         }
32901         
32902         
32903         this.fireEvent('invalid', this, msg);
32904     }
32905     
32906    
32907 });
32908
32909 Roo.apply(Roo.bootstrap.FieldLabel, {
32910     
32911     groups: {},
32912     
32913      /**
32914     * register a FieldLabel Group
32915     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32916     */
32917     register : function(label)
32918     {
32919         if(this.groups.hasOwnProperty(label.target)){
32920             return;
32921         }
32922      
32923         this.groups[label.target] = label;
32924         
32925     },
32926     /**
32927     * fetch a FieldLabel Group based on the target
32928     * @param {string} target
32929     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32930     */
32931     get: function(target) {
32932         if (typeof(this.groups[target]) == 'undefined') {
32933             return false;
32934         }
32935         
32936         return this.groups[target] ;
32937     }
32938 });
32939
32940  
32941
32942  /*
32943  * - LGPL
32944  *
32945  * page DateSplitField.
32946  * 
32947  */
32948
32949
32950 /**
32951  * @class Roo.bootstrap.DateSplitField
32952  * @extends Roo.bootstrap.Component
32953  * Bootstrap DateSplitField class
32954  * @cfg {string} fieldLabel - the label associated
32955  * @cfg {Number} labelWidth set the width of label (0-12)
32956  * @cfg {String} labelAlign (top|left)
32957  * @cfg {Boolean} dayAllowBlank (true|false) default false
32958  * @cfg {Boolean} monthAllowBlank (true|false) default false
32959  * @cfg {Boolean} yearAllowBlank (true|false) default false
32960  * @cfg {string} dayPlaceholder 
32961  * @cfg {string} monthPlaceholder
32962  * @cfg {string} yearPlaceholder
32963  * @cfg {string} dayFormat default 'd'
32964  * @cfg {string} monthFormat default 'm'
32965  * @cfg {string} yearFormat default 'Y'
32966  * @cfg {Number} labellg set the width of label (1-12)
32967  * @cfg {Number} labelmd set the width of label (1-12)
32968  * @cfg {Number} labelsm set the width of label (1-12)
32969  * @cfg {Number} labelxs set the width of label (1-12)
32970
32971  *     
32972  * @constructor
32973  * Create a new DateSplitField
32974  * @param {Object} config The config object
32975  */
32976
32977 Roo.bootstrap.DateSplitField = function(config){
32978     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32979     
32980     this.addEvents({
32981         // raw events
32982          /**
32983          * @event years
32984          * getting the data of years
32985          * @param {Roo.bootstrap.DateSplitField} this
32986          * @param {Object} years
32987          */
32988         "years" : true,
32989         /**
32990          * @event days
32991          * getting the data of days
32992          * @param {Roo.bootstrap.DateSplitField} this
32993          * @param {Object} days
32994          */
32995         "days" : true,
32996         /**
32997          * @event invalid
32998          * Fires after the field has been marked as invalid.
32999          * @param {Roo.form.Field} this
33000          * @param {String} msg The validation message
33001          */
33002         invalid : true,
33003        /**
33004          * @event valid
33005          * Fires after the field has been validated with no errors.
33006          * @param {Roo.form.Field} this
33007          */
33008         valid : true
33009     });
33010 };
33011
33012 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33013     
33014     fieldLabel : '',
33015     labelAlign : 'top',
33016     labelWidth : 3,
33017     dayAllowBlank : false,
33018     monthAllowBlank : false,
33019     yearAllowBlank : false,
33020     dayPlaceholder : '',
33021     monthPlaceholder : '',
33022     yearPlaceholder : '',
33023     dayFormat : 'd',
33024     monthFormat : 'm',
33025     yearFormat : 'Y',
33026     isFormField : true,
33027     labellg : 0,
33028     labelmd : 0,
33029     labelsm : 0,
33030     labelxs : 0,
33031     
33032     getAutoCreate : function()
33033     {
33034         var cfg = {
33035             tag : 'div',
33036             cls : 'row roo-date-split-field-group',
33037             cn : [
33038                 {
33039                     tag : 'input',
33040                     type : 'hidden',
33041                     cls : 'form-hidden-field roo-date-split-field-group-value',
33042                     name : this.name
33043                 }
33044             ]
33045         };
33046         
33047         var labelCls = 'col-md-12';
33048         var contentCls = 'col-md-4';
33049         
33050         if(this.fieldLabel){
33051             
33052             var label = {
33053                 tag : 'div',
33054                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33055                 cn : [
33056                     {
33057                         tag : 'label',
33058                         html : this.fieldLabel
33059                     }
33060                 ]
33061             };
33062             
33063             if(this.labelAlign == 'left'){
33064             
33065                 if(this.labelWidth > 12){
33066                     label.style = "width: " + this.labelWidth + 'px';
33067                 }
33068
33069                 if(this.labelWidth < 13 && this.labelmd == 0){
33070                     this.labelmd = this.labelWidth;
33071                 }
33072
33073                 if(this.labellg > 0){
33074                     labelCls = ' col-lg-' + this.labellg;
33075                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33076                 }
33077
33078                 if(this.labelmd > 0){
33079                     labelCls = ' col-md-' + this.labelmd;
33080                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33081                 }
33082
33083                 if(this.labelsm > 0){
33084                     labelCls = ' col-sm-' + this.labelsm;
33085                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33086                 }
33087
33088                 if(this.labelxs > 0){
33089                     labelCls = ' col-xs-' + this.labelxs;
33090                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33091                 }
33092             }
33093             
33094             label.cls += ' ' + labelCls;
33095             
33096             cfg.cn.push(label);
33097         }
33098         
33099         Roo.each(['day', 'month', 'year'], function(t){
33100             cfg.cn.push({
33101                 tag : 'div',
33102                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33103             });
33104         }, this);
33105         
33106         return cfg;
33107     },
33108     
33109     inputEl: function ()
33110     {
33111         return this.el.select('.roo-date-split-field-group-value', true).first();
33112     },
33113     
33114     onRender : function(ct, position) 
33115     {
33116         var _this = this;
33117         
33118         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33119         
33120         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33121         
33122         this.dayField = new Roo.bootstrap.ComboBox({
33123             allowBlank : this.dayAllowBlank,
33124             alwaysQuery : true,
33125             displayField : 'value',
33126             editable : false,
33127             fieldLabel : '',
33128             forceSelection : true,
33129             mode : 'local',
33130             placeholder : this.dayPlaceholder,
33131             selectOnFocus : true,
33132             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33133             triggerAction : 'all',
33134             typeAhead : true,
33135             valueField : 'value',
33136             store : new Roo.data.SimpleStore({
33137                 data : (function() {    
33138                     var days = [];
33139                     _this.fireEvent('days', _this, days);
33140                     return days;
33141                 })(),
33142                 fields : [ 'value' ]
33143             }),
33144             listeners : {
33145                 select : function (_self, record, index)
33146                 {
33147                     _this.setValue(_this.getValue());
33148                 }
33149             }
33150         });
33151
33152         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33153         
33154         this.monthField = new Roo.bootstrap.MonthField({
33155             after : '<i class=\"fa fa-calendar\"></i>',
33156             allowBlank : this.monthAllowBlank,
33157             placeholder : this.monthPlaceholder,
33158             readOnly : true,
33159             listeners : {
33160                 render : function (_self)
33161                 {
33162                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33163                         e.preventDefault();
33164                         _self.focus();
33165                     });
33166                 },
33167                 select : function (_self, oldvalue, newvalue)
33168                 {
33169                     _this.setValue(_this.getValue());
33170                 }
33171             }
33172         });
33173         
33174         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33175         
33176         this.yearField = new Roo.bootstrap.ComboBox({
33177             allowBlank : this.yearAllowBlank,
33178             alwaysQuery : true,
33179             displayField : 'value',
33180             editable : false,
33181             fieldLabel : '',
33182             forceSelection : true,
33183             mode : 'local',
33184             placeholder : this.yearPlaceholder,
33185             selectOnFocus : true,
33186             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33187             triggerAction : 'all',
33188             typeAhead : true,
33189             valueField : 'value',
33190             store : new Roo.data.SimpleStore({
33191                 data : (function() {
33192                     var years = [];
33193                     _this.fireEvent('years', _this, years);
33194                     return years;
33195                 })(),
33196                 fields : [ 'value' ]
33197             }),
33198             listeners : {
33199                 select : function (_self, record, index)
33200                 {
33201                     _this.setValue(_this.getValue());
33202                 }
33203             }
33204         });
33205
33206         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33207     },
33208     
33209     setValue : function(v, format)
33210     {
33211         this.inputEl.dom.value = v;
33212         
33213         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33214         
33215         var d = Date.parseDate(v, f);
33216         
33217         if(!d){
33218             this.validate();
33219             return;
33220         }
33221         
33222         this.setDay(d.format(this.dayFormat));
33223         this.setMonth(d.format(this.monthFormat));
33224         this.setYear(d.format(this.yearFormat));
33225         
33226         this.validate();
33227         
33228         return;
33229     },
33230     
33231     setDay : function(v)
33232     {
33233         this.dayField.setValue(v);
33234         this.inputEl.dom.value = this.getValue();
33235         this.validate();
33236         return;
33237     },
33238     
33239     setMonth : function(v)
33240     {
33241         this.monthField.setValue(v, true);
33242         this.inputEl.dom.value = this.getValue();
33243         this.validate();
33244         return;
33245     },
33246     
33247     setYear : function(v)
33248     {
33249         this.yearField.setValue(v);
33250         this.inputEl.dom.value = this.getValue();
33251         this.validate();
33252         return;
33253     },
33254     
33255     getDay : function()
33256     {
33257         return this.dayField.getValue();
33258     },
33259     
33260     getMonth : function()
33261     {
33262         return this.monthField.getValue();
33263     },
33264     
33265     getYear : function()
33266     {
33267         return this.yearField.getValue();
33268     },
33269     
33270     getValue : function()
33271     {
33272         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33273         
33274         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33275         
33276         return date;
33277     },
33278     
33279     reset : function()
33280     {
33281         this.setDay('');
33282         this.setMonth('');
33283         this.setYear('');
33284         this.inputEl.dom.value = '';
33285         this.validate();
33286         return;
33287     },
33288     
33289     validate : function()
33290     {
33291         var d = this.dayField.validate();
33292         var m = this.monthField.validate();
33293         var y = this.yearField.validate();
33294         
33295         var valid = true;
33296         
33297         if(
33298                 (!this.dayAllowBlank && !d) ||
33299                 (!this.monthAllowBlank && !m) ||
33300                 (!this.yearAllowBlank && !y)
33301         ){
33302             valid = false;
33303         }
33304         
33305         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33306             return valid;
33307         }
33308         
33309         if(valid){
33310             this.markValid();
33311             return valid;
33312         }
33313         
33314         this.markInvalid();
33315         
33316         return valid;
33317     },
33318     
33319     markValid : function()
33320     {
33321         
33322         var label = this.el.select('label', true).first();
33323         var icon = this.el.select('i.fa-star', true).first();
33324
33325         if(label && icon){
33326             icon.remove();
33327         }
33328         
33329         this.fireEvent('valid', this);
33330     },
33331     
33332      /**
33333      * Mark this field as invalid
33334      * @param {String} msg The validation message
33335      */
33336     markInvalid : function(msg)
33337     {
33338         
33339         var label = this.el.select('label', true).first();
33340         var icon = this.el.select('i.fa-star', true).first();
33341
33342         if(label && !icon){
33343             this.el.select('.roo-date-split-field-label', true).createChild({
33344                 tag : 'i',
33345                 cls : 'text-danger fa fa-lg fa-star',
33346                 tooltip : 'This field is required',
33347                 style : 'margin-right:5px;'
33348             }, label, true);
33349         }
33350         
33351         this.fireEvent('invalid', this, msg);
33352     },
33353     
33354     clearInvalid : function()
33355     {
33356         var label = this.el.select('label', true).first();
33357         var icon = this.el.select('i.fa-star', true).first();
33358
33359         if(label && icon){
33360             icon.remove();
33361         }
33362         
33363         this.fireEvent('valid', this);
33364     },
33365     
33366     getName: function()
33367     {
33368         return this.name;
33369     }
33370     
33371 });
33372
33373  /**
33374  *
33375  * This is based on 
33376  * http://masonry.desandro.com
33377  *
33378  * The idea is to render all the bricks based on vertical width...
33379  *
33380  * The original code extends 'outlayer' - we might need to use that....
33381  * 
33382  */
33383
33384
33385 /**
33386  * @class Roo.bootstrap.LayoutMasonry
33387  * @extends Roo.bootstrap.Component
33388  * Bootstrap Layout Masonry class
33389  * 
33390  * @constructor
33391  * Create a new Element
33392  * @param {Object} config The config object
33393  */
33394
33395 Roo.bootstrap.LayoutMasonry = function(config){
33396     
33397     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33398     
33399     this.bricks = [];
33400     
33401     Roo.bootstrap.LayoutMasonry.register(this);
33402     
33403     this.addEvents({
33404         // raw events
33405         /**
33406          * @event layout
33407          * Fire after layout the items
33408          * @param {Roo.bootstrap.LayoutMasonry} this
33409          * @param {Roo.EventObject} e
33410          */
33411         "layout" : true
33412     });
33413     
33414 };
33415
33416 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33417     
33418     /**
33419      * @cfg {Boolean} isLayoutInstant = no animation?
33420      */   
33421     isLayoutInstant : false, // needed?
33422    
33423     /**
33424      * @cfg {Number} boxWidth  width of the columns
33425      */   
33426     boxWidth : 450,
33427     
33428       /**
33429      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33430      */   
33431     boxHeight : 0,
33432     
33433     /**
33434      * @cfg {Number} padWidth padding below box..
33435      */   
33436     padWidth : 10, 
33437     
33438     /**
33439      * @cfg {Number} gutter gutter width..
33440      */   
33441     gutter : 10,
33442     
33443      /**
33444      * @cfg {Number} maxCols maximum number of columns
33445      */   
33446     
33447     maxCols: 0,
33448     
33449     /**
33450      * @cfg {Boolean} isAutoInitial defalut true
33451      */   
33452     isAutoInitial : true, 
33453     
33454     containerWidth: 0,
33455     
33456     /**
33457      * @cfg {Boolean} isHorizontal defalut false
33458      */   
33459     isHorizontal : false, 
33460
33461     currentSize : null,
33462     
33463     tag: 'div',
33464     
33465     cls: '',
33466     
33467     bricks: null, //CompositeElement
33468     
33469     cols : 1,
33470     
33471     _isLayoutInited : false,
33472     
33473 //    isAlternative : false, // only use for vertical layout...
33474     
33475     /**
33476      * @cfg {Number} alternativePadWidth padding below box..
33477      */   
33478     alternativePadWidth : 50,
33479     
33480     selectedBrick : [],
33481     
33482     getAutoCreate : function(){
33483         
33484         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33485         
33486         var cfg = {
33487             tag: this.tag,
33488             cls: 'blog-masonary-wrapper ' + this.cls,
33489             cn : {
33490                 cls : 'mas-boxes masonary'
33491             }
33492         };
33493         
33494         return cfg;
33495     },
33496     
33497     getChildContainer: function( )
33498     {
33499         if (this.boxesEl) {
33500             return this.boxesEl;
33501         }
33502         
33503         this.boxesEl = this.el.select('.mas-boxes').first();
33504         
33505         return this.boxesEl;
33506     },
33507     
33508     
33509     initEvents : function()
33510     {
33511         var _this = this;
33512         
33513         if(this.isAutoInitial){
33514             Roo.log('hook children rendered');
33515             this.on('childrenrendered', function() {
33516                 Roo.log('children rendered');
33517                 _this.initial();
33518             } ,this);
33519         }
33520     },
33521     
33522     initial : function()
33523     {
33524         this.selectedBrick = [];
33525         
33526         this.currentSize = this.el.getBox(true);
33527         
33528         Roo.EventManager.onWindowResize(this.resize, this); 
33529
33530         if(!this.isAutoInitial){
33531             this.layout();
33532             return;
33533         }
33534         
33535         this.layout();
33536         
33537         return;
33538         //this.layout.defer(500,this);
33539         
33540     },
33541     
33542     resize : function()
33543     {
33544         var cs = this.el.getBox(true);
33545         
33546         if (
33547                 this.currentSize.width == cs.width && 
33548                 this.currentSize.x == cs.x && 
33549                 this.currentSize.height == cs.height && 
33550                 this.currentSize.y == cs.y 
33551         ) {
33552             Roo.log("no change in with or X or Y");
33553             return;
33554         }
33555         
33556         this.currentSize = cs;
33557         
33558         this.layout();
33559         
33560     },
33561     
33562     layout : function()
33563     {   
33564         this._resetLayout();
33565         
33566         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33567         
33568         this.layoutItems( isInstant );
33569       
33570         this._isLayoutInited = true;
33571         
33572         this.fireEvent('layout', this);
33573         
33574     },
33575     
33576     _resetLayout : function()
33577     {
33578         if(this.isHorizontal){
33579             this.horizontalMeasureColumns();
33580             return;
33581         }
33582         
33583         this.verticalMeasureColumns();
33584         
33585     },
33586     
33587     verticalMeasureColumns : function()
33588     {
33589         this.getContainerWidth();
33590         
33591 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33592 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33593 //            return;
33594 //        }
33595         
33596         var boxWidth = this.boxWidth + this.padWidth;
33597         
33598         if(this.containerWidth < this.boxWidth){
33599             boxWidth = this.containerWidth
33600         }
33601         
33602         var containerWidth = this.containerWidth;
33603         
33604         var cols = Math.floor(containerWidth / boxWidth);
33605         
33606         this.cols = Math.max( cols, 1 );
33607         
33608         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33609         
33610         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33611         
33612         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33613         
33614         this.colWidth = boxWidth + avail - this.padWidth;
33615         
33616         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33617         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33618     },
33619     
33620     horizontalMeasureColumns : function()
33621     {
33622         this.getContainerWidth();
33623         
33624         var boxWidth = this.boxWidth;
33625         
33626         if(this.containerWidth < boxWidth){
33627             boxWidth = this.containerWidth;
33628         }
33629         
33630         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33631         
33632         this.el.setHeight(boxWidth);
33633         
33634     },
33635     
33636     getContainerWidth : function()
33637     {
33638         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33639     },
33640     
33641     layoutItems : function( isInstant )
33642     {
33643         Roo.log(this.bricks);
33644         
33645         var items = Roo.apply([], this.bricks);
33646         
33647         if(this.isHorizontal){
33648             this._horizontalLayoutItems( items , isInstant );
33649             return;
33650         }
33651         
33652 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33653 //            this._verticalAlternativeLayoutItems( items , isInstant );
33654 //            return;
33655 //        }
33656         
33657         this._verticalLayoutItems( items , isInstant );
33658         
33659     },
33660     
33661     _verticalLayoutItems : function ( items , isInstant)
33662     {
33663         if ( !items || !items.length ) {
33664             return;
33665         }
33666         
33667         var standard = [
33668             ['xs', 'xs', 'xs', 'tall'],
33669             ['xs', 'xs', 'tall'],
33670             ['xs', 'xs', 'sm'],
33671             ['xs', 'xs', 'xs'],
33672             ['xs', 'tall'],
33673             ['xs', 'sm'],
33674             ['xs', 'xs'],
33675             ['xs'],
33676             
33677             ['sm', 'xs', 'xs'],
33678             ['sm', 'xs'],
33679             ['sm'],
33680             
33681             ['tall', 'xs', 'xs', 'xs'],
33682             ['tall', 'xs', 'xs'],
33683             ['tall', 'xs'],
33684             ['tall']
33685             
33686         ];
33687         
33688         var queue = [];
33689         
33690         var boxes = [];
33691         
33692         var box = [];
33693         
33694         Roo.each(items, function(item, k){
33695             
33696             switch (item.size) {
33697                 // these layouts take up a full box,
33698                 case 'md' :
33699                 case 'md-left' :
33700                 case 'md-right' :
33701                 case 'wide' :
33702                     
33703                     if(box.length){
33704                         boxes.push(box);
33705                         box = [];
33706                     }
33707                     
33708                     boxes.push([item]);
33709                     
33710                     break;
33711                     
33712                 case 'xs' :
33713                 case 'sm' :
33714                 case 'tall' :
33715                     
33716                     box.push(item);
33717                     
33718                     break;
33719                 default :
33720                     break;
33721                     
33722             }
33723             
33724         }, this);
33725         
33726         if(box.length){
33727             boxes.push(box);
33728             box = [];
33729         }
33730         
33731         var filterPattern = function(box, length)
33732         {
33733             if(!box.length){
33734                 return;
33735             }
33736             
33737             var match = false;
33738             
33739             var pattern = box.slice(0, length);
33740             
33741             var format = [];
33742             
33743             Roo.each(pattern, function(i){
33744                 format.push(i.size);
33745             }, this);
33746             
33747             Roo.each(standard, function(s){
33748                 
33749                 if(String(s) != String(format)){
33750                     return;
33751                 }
33752                 
33753                 match = true;
33754                 return false;
33755                 
33756             }, this);
33757             
33758             if(!match && length == 1){
33759                 return;
33760             }
33761             
33762             if(!match){
33763                 filterPattern(box, length - 1);
33764                 return;
33765             }
33766                 
33767             queue.push(pattern);
33768
33769             box = box.slice(length, box.length);
33770
33771             filterPattern(box, 4);
33772
33773             return;
33774             
33775         }
33776         
33777         Roo.each(boxes, function(box, k){
33778             
33779             if(!box.length){
33780                 return;
33781             }
33782             
33783             if(box.length == 1){
33784                 queue.push(box);
33785                 return;
33786             }
33787             
33788             filterPattern(box, 4);
33789             
33790         }, this);
33791         
33792         this._processVerticalLayoutQueue( queue, isInstant );
33793         
33794     },
33795     
33796 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33797 //    {
33798 //        if ( !items || !items.length ) {
33799 //            return;
33800 //        }
33801 //
33802 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33803 //        
33804 //    },
33805     
33806     _horizontalLayoutItems : function ( items , isInstant)
33807     {
33808         if ( !items || !items.length || items.length < 3) {
33809             return;
33810         }
33811         
33812         items.reverse();
33813         
33814         var eItems = items.slice(0, 3);
33815         
33816         items = items.slice(3, items.length);
33817         
33818         var standard = [
33819             ['xs', 'xs', 'xs', 'wide'],
33820             ['xs', 'xs', 'wide'],
33821             ['xs', 'xs', 'sm'],
33822             ['xs', 'xs', 'xs'],
33823             ['xs', 'wide'],
33824             ['xs', 'sm'],
33825             ['xs', 'xs'],
33826             ['xs'],
33827             
33828             ['sm', 'xs', 'xs'],
33829             ['sm', 'xs'],
33830             ['sm'],
33831             
33832             ['wide', 'xs', 'xs', 'xs'],
33833             ['wide', 'xs', 'xs'],
33834             ['wide', 'xs'],
33835             ['wide'],
33836             
33837             ['wide-thin']
33838         ];
33839         
33840         var queue = [];
33841         
33842         var boxes = [];
33843         
33844         var box = [];
33845         
33846         Roo.each(items, function(item, k){
33847             
33848             switch (item.size) {
33849                 case 'md' :
33850                 case 'md-left' :
33851                 case 'md-right' :
33852                 case 'tall' :
33853                     
33854                     if(box.length){
33855                         boxes.push(box);
33856                         box = [];
33857                     }
33858                     
33859                     boxes.push([item]);
33860                     
33861                     break;
33862                     
33863                 case 'xs' :
33864                 case 'sm' :
33865                 case 'wide' :
33866                 case 'wide-thin' :
33867                     
33868                     box.push(item);
33869                     
33870                     break;
33871                 default :
33872                     break;
33873                     
33874             }
33875             
33876         }, this);
33877         
33878         if(box.length){
33879             boxes.push(box);
33880             box = [];
33881         }
33882         
33883         var filterPattern = function(box, length)
33884         {
33885             if(!box.length){
33886                 return;
33887             }
33888             
33889             var match = false;
33890             
33891             var pattern = box.slice(0, length);
33892             
33893             var format = [];
33894             
33895             Roo.each(pattern, function(i){
33896                 format.push(i.size);
33897             }, this);
33898             
33899             Roo.each(standard, function(s){
33900                 
33901                 if(String(s) != String(format)){
33902                     return;
33903                 }
33904                 
33905                 match = true;
33906                 return false;
33907                 
33908             }, this);
33909             
33910             if(!match && length == 1){
33911                 return;
33912             }
33913             
33914             if(!match){
33915                 filterPattern(box, length - 1);
33916                 return;
33917             }
33918                 
33919             queue.push(pattern);
33920
33921             box = box.slice(length, box.length);
33922
33923             filterPattern(box, 4);
33924
33925             return;
33926             
33927         }
33928         
33929         Roo.each(boxes, function(box, k){
33930             
33931             if(!box.length){
33932                 return;
33933             }
33934             
33935             if(box.length == 1){
33936                 queue.push(box);
33937                 return;
33938             }
33939             
33940             filterPattern(box, 4);
33941             
33942         }, this);
33943         
33944         
33945         var prune = [];
33946         
33947         var pos = this.el.getBox(true);
33948         
33949         var minX = pos.x;
33950         
33951         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33952         
33953         var hit_end = false;
33954         
33955         Roo.each(queue, function(box){
33956             
33957             if(hit_end){
33958                 
33959                 Roo.each(box, function(b){
33960                 
33961                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33962                     b.el.hide();
33963
33964                 }, this);
33965
33966                 return;
33967             }
33968             
33969             var mx = 0;
33970             
33971             Roo.each(box, function(b){
33972                 
33973                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33974                 b.el.show();
33975
33976                 mx = Math.max(mx, b.x);
33977                 
33978             }, this);
33979             
33980             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33981             
33982             if(maxX < minX){
33983                 
33984                 Roo.each(box, function(b){
33985                 
33986                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33987                     b.el.hide();
33988                     
33989                 }, this);
33990                 
33991                 hit_end = true;
33992                 
33993                 return;
33994             }
33995             
33996             prune.push(box);
33997             
33998         }, this);
33999         
34000         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34001     },
34002     
34003     /** Sets position of item in DOM
34004     * @param {Element} item
34005     * @param {Number} x - horizontal position
34006     * @param {Number} y - vertical position
34007     * @param {Boolean} isInstant - disables transitions
34008     */
34009     _processVerticalLayoutQueue : function( queue, isInstant )
34010     {
34011         var pos = this.el.getBox(true);
34012         var x = pos.x;
34013         var y = pos.y;
34014         var maxY = [];
34015         
34016         for (var i = 0; i < this.cols; i++){
34017             maxY[i] = pos.y;
34018         }
34019         
34020         Roo.each(queue, function(box, k){
34021             
34022             var col = k % this.cols;
34023             
34024             Roo.each(box, function(b,kk){
34025                 
34026                 b.el.position('absolute');
34027                 
34028                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34029                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34030                 
34031                 if(b.size == 'md-left' || b.size == 'md-right'){
34032                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34033                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34034                 }
34035                 
34036                 b.el.setWidth(width);
34037                 b.el.setHeight(height);
34038                 // iframe?
34039                 b.el.select('iframe',true).setSize(width,height);
34040                 
34041             }, this);
34042             
34043             for (var i = 0; i < this.cols; i++){
34044                 
34045                 if(maxY[i] < maxY[col]){
34046                     col = i;
34047                     continue;
34048                 }
34049                 
34050                 col = Math.min(col, i);
34051                 
34052             }
34053             
34054             x = pos.x + col * (this.colWidth + this.padWidth);
34055             
34056             y = maxY[col];
34057             
34058             var positions = [];
34059             
34060             switch (box.length){
34061                 case 1 :
34062                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34063                     break;
34064                 case 2 :
34065                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34066                     break;
34067                 case 3 :
34068                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34069                     break;
34070                 case 4 :
34071                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34072                     break;
34073                 default :
34074                     break;
34075             }
34076             
34077             Roo.each(box, function(b,kk){
34078                 
34079                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34080                 
34081                 var sz = b.el.getSize();
34082                 
34083                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34084                 
34085             }, this);
34086             
34087         }, this);
34088         
34089         var mY = 0;
34090         
34091         for (var i = 0; i < this.cols; i++){
34092             mY = Math.max(mY, maxY[i]);
34093         }
34094         
34095         this.el.setHeight(mY - pos.y);
34096         
34097     },
34098     
34099 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34100 //    {
34101 //        var pos = this.el.getBox(true);
34102 //        var x = pos.x;
34103 //        var y = pos.y;
34104 //        var maxX = pos.right;
34105 //        
34106 //        var maxHeight = 0;
34107 //        
34108 //        Roo.each(items, function(item, k){
34109 //            
34110 //            var c = k % 2;
34111 //            
34112 //            item.el.position('absolute');
34113 //                
34114 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34115 //
34116 //            item.el.setWidth(width);
34117 //
34118 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34119 //
34120 //            item.el.setHeight(height);
34121 //            
34122 //            if(c == 0){
34123 //                item.el.setXY([x, y], isInstant ? false : true);
34124 //            } else {
34125 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34126 //            }
34127 //            
34128 //            y = y + height + this.alternativePadWidth;
34129 //            
34130 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34131 //            
34132 //        }, this);
34133 //        
34134 //        this.el.setHeight(maxHeight);
34135 //        
34136 //    },
34137     
34138     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34139     {
34140         var pos = this.el.getBox(true);
34141         
34142         var minX = pos.x;
34143         var minY = pos.y;
34144         
34145         var maxX = pos.right;
34146         
34147         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34148         
34149         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34150         
34151         Roo.each(queue, function(box, k){
34152             
34153             Roo.each(box, function(b, kk){
34154                 
34155                 b.el.position('absolute');
34156                 
34157                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34158                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34159                 
34160                 if(b.size == 'md-left' || b.size == 'md-right'){
34161                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34162                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34163                 }
34164                 
34165                 b.el.setWidth(width);
34166                 b.el.setHeight(height);
34167                 
34168             }, this);
34169             
34170             if(!box.length){
34171                 return;
34172             }
34173             
34174             var positions = [];
34175             
34176             switch (box.length){
34177                 case 1 :
34178                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34179                     break;
34180                 case 2 :
34181                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34182                     break;
34183                 case 3 :
34184                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34185                     break;
34186                 case 4 :
34187                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34188                     break;
34189                 default :
34190                     break;
34191             }
34192             
34193             Roo.each(box, function(b,kk){
34194                 
34195                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34196                 
34197                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34198                 
34199             }, this);
34200             
34201         }, this);
34202         
34203     },
34204     
34205     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34206     {
34207         Roo.each(eItems, function(b,k){
34208             
34209             b.size = (k == 0) ? 'sm' : 'xs';
34210             b.x = (k == 0) ? 2 : 1;
34211             b.y = (k == 0) ? 2 : 1;
34212             
34213             b.el.position('absolute');
34214             
34215             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34216                 
34217             b.el.setWidth(width);
34218             
34219             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34220             
34221             b.el.setHeight(height);
34222             
34223         }, this);
34224
34225         var positions = [];
34226         
34227         positions.push({
34228             x : maxX - this.unitWidth * 2 - this.gutter,
34229             y : minY
34230         });
34231         
34232         positions.push({
34233             x : maxX - this.unitWidth,
34234             y : minY + (this.unitWidth + this.gutter) * 2
34235         });
34236         
34237         positions.push({
34238             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34239             y : minY
34240         });
34241         
34242         Roo.each(eItems, function(b,k){
34243             
34244             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34245
34246         }, this);
34247         
34248     },
34249     
34250     getVerticalOneBoxColPositions : function(x, y, box)
34251     {
34252         var pos = [];
34253         
34254         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34255         
34256         if(box[0].size == 'md-left'){
34257             rand = 0;
34258         }
34259         
34260         if(box[0].size == 'md-right'){
34261             rand = 1;
34262         }
34263         
34264         pos.push({
34265             x : x + (this.unitWidth + this.gutter) * rand,
34266             y : y
34267         });
34268         
34269         return pos;
34270     },
34271     
34272     getVerticalTwoBoxColPositions : function(x, y, box)
34273     {
34274         var pos = [];
34275         
34276         if(box[0].size == 'xs'){
34277             
34278             pos.push({
34279                 x : x,
34280                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34281             });
34282
34283             pos.push({
34284                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34285                 y : y
34286             });
34287             
34288             return pos;
34289             
34290         }
34291         
34292         pos.push({
34293             x : x,
34294             y : y
34295         });
34296
34297         pos.push({
34298             x : x + (this.unitWidth + this.gutter) * 2,
34299             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34300         });
34301         
34302         return pos;
34303         
34304     },
34305     
34306     getVerticalThreeBoxColPositions : function(x, y, box)
34307     {
34308         var pos = [];
34309         
34310         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34311             
34312             pos.push({
34313                 x : x,
34314                 y : y
34315             });
34316
34317             pos.push({
34318                 x : x + (this.unitWidth + this.gutter) * 1,
34319                 y : y
34320             });
34321             
34322             pos.push({
34323                 x : x + (this.unitWidth + this.gutter) * 2,
34324                 y : y
34325             });
34326             
34327             return pos;
34328             
34329         }
34330         
34331         if(box[0].size == 'xs' && box[1].size == 'xs'){
34332             
34333             pos.push({
34334                 x : x,
34335                 y : y
34336             });
34337
34338             pos.push({
34339                 x : x,
34340                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34341             });
34342             
34343             pos.push({
34344                 x : x + (this.unitWidth + this.gutter) * 1,
34345                 y : y
34346             });
34347             
34348             return pos;
34349             
34350         }
34351         
34352         pos.push({
34353             x : x,
34354             y : y
34355         });
34356
34357         pos.push({
34358             x : x + (this.unitWidth + this.gutter) * 2,
34359             y : y
34360         });
34361
34362         pos.push({
34363             x : x + (this.unitWidth + this.gutter) * 2,
34364             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34365         });
34366             
34367         return pos;
34368         
34369     },
34370     
34371     getVerticalFourBoxColPositions : function(x, y, box)
34372     {
34373         var pos = [];
34374         
34375         if(box[0].size == 'xs'){
34376             
34377             pos.push({
34378                 x : x,
34379                 y : y
34380             });
34381
34382             pos.push({
34383                 x : x,
34384                 y : y + (this.unitHeight + this.gutter) * 1
34385             });
34386             
34387             pos.push({
34388                 x : x,
34389                 y : y + (this.unitHeight + this.gutter) * 2
34390             });
34391             
34392             pos.push({
34393                 x : x + (this.unitWidth + this.gutter) * 1,
34394                 y : y
34395             });
34396             
34397             return pos;
34398             
34399         }
34400         
34401         pos.push({
34402             x : x,
34403             y : y
34404         });
34405
34406         pos.push({
34407             x : x + (this.unitWidth + this.gutter) * 2,
34408             y : y
34409         });
34410
34411         pos.push({
34412             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34413             y : y + (this.unitHeight + this.gutter) * 1
34414         });
34415
34416         pos.push({
34417             x : x + (this.unitWidth + this.gutter) * 2,
34418             y : y + (this.unitWidth + this.gutter) * 2
34419         });
34420
34421         return pos;
34422         
34423     },
34424     
34425     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34426     {
34427         var pos = [];
34428         
34429         if(box[0].size == 'md-left'){
34430             pos.push({
34431                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34432                 y : minY
34433             });
34434             
34435             return pos;
34436         }
34437         
34438         if(box[0].size == 'md-right'){
34439             pos.push({
34440                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34441                 y : minY + (this.unitWidth + this.gutter) * 1
34442             });
34443             
34444             return pos;
34445         }
34446         
34447         var rand = Math.floor(Math.random() * (4 - box[0].y));
34448         
34449         pos.push({
34450             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34451             y : minY + (this.unitWidth + this.gutter) * rand
34452         });
34453         
34454         return pos;
34455         
34456     },
34457     
34458     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34459     {
34460         var pos = [];
34461         
34462         if(box[0].size == 'xs'){
34463             
34464             pos.push({
34465                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34466                 y : minY
34467             });
34468
34469             pos.push({
34470                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34471                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34472             });
34473             
34474             return pos;
34475             
34476         }
34477         
34478         pos.push({
34479             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34480             y : minY
34481         });
34482
34483         pos.push({
34484             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34485             y : minY + (this.unitWidth + this.gutter) * 2
34486         });
34487         
34488         return pos;
34489         
34490     },
34491     
34492     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34493     {
34494         var pos = [];
34495         
34496         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34497             
34498             pos.push({
34499                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34500                 y : minY
34501             });
34502
34503             pos.push({
34504                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34505                 y : minY + (this.unitWidth + this.gutter) * 1
34506             });
34507             
34508             pos.push({
34509                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34510                 y : minY + (this.unitWidth + this.gutter) * 2
34511             });
34512             
34513             return pos;
34514             
34515         }
34516         
34517         if(box[0].size == 'xs' && box[1].size == 'xs'){
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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34526                 y : minY
34527             });
34528             
34529             pos.push({
34530                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34531                 y : minY + (this.unitWidth + this.gutter) * 1
34532             });
34533             
34534             return pos;
34535             
34536         }
34537         
34538         pos.push({
34539             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34540             y : minY
34541         });
34542
34543         pos.push({
34544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34545             y : minY + (this.unitWidth + this.gutter) * 2
34546         });
34547
34548         pos.push({
34549             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34550             y : minY + (this.unitWidth + this.gutter) * 2
34551         });
34552             
34553         return pos;
34554         
34555     },
34556     
34557     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34558     {
34559         var pos = [];
34560         
34561         if(box[0].size == 'xs'){
34562             
34563             pos.push({
34564                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34565                 y : minY
34566             });
34567
34568             pos.push({
34569                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34570                 y : minY
34571             });
34572             
34573             pos.push({
34574                 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),
34575                 y : minY
34576             });
34577             
34578             pos.push({
34579                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34580                 y : minY + (this.unitWidth + this.gutter) * 1
34581             });
34582             
34583             return pos;
34584             
34585         }
34586         
34587         pos.push({
34588             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34589             y : minY
34590         });
34591         
34592         pos.push({
34593             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34594             y : minY + (this.unitWidth + this.gutter) * 2
34595         });
34596         
34597         pos.push({
34598             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34599             y : minY + (this.unitWidth + this.gutter) * 2
34600         });
34601         
34602         pos.push({
34603             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),
34604             y : minY + (this.unitWidth + this.gutter) * 2
34605         });
34606
34607         return pos;
34608         
34609     },
34610     
34611     /**
34612     * remove a Masonry Brick
34613     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34614     */
34615     removeBrick : function(brick_id)
34616     {
34617         if (!brick_id) {
34618             return;
34619         }
34620         
34621         for (var i = 0; i<this.bricks.length; i++) {
34622             if (this.bricks[i].id == brick_id) {
34623                 this.bricks.splice(i,1);
34624                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34625                 this.initial();
34626             }
34627         }
34628     },
34629     
34630     /**
34631     * adds a Masonry Brick
34632     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34633     */
34634     addBrick : function(cfg)
34635     {
34636         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34637         //this.register(cn);
34638         cn.parentId = this.id;
34639         cn.render(this.el);
34640         return cn;
34641     },
34642     
34643     /**
34644     * register a Masonry Brick
34645     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34646     */
34647     
34648     register : function(brick)
34649     {
34650         this.bricks.push(brick);
34651         brick.masonryId = this.id;
34652     },
34653     
34654     /**
34655     * clear all the Masonry Brick
34656     */
34657     clearAll : function()
34658     {
34659         this.bricks = [];
34660         //this.getChildContainer().dom.innerHTML = "";
34661         this.el.dom.innerHTML = '';
34662     },
34663     
34664     getSelected : function()
34665     {
34666         if (!this.selectedBrick) {
34667             return false;
34668         }
34669         
34670         return this.selectedBrick;
34671     }
34672 });
34673
34674 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34675     
34676     groups: {},
34677      /**
34678     * register a Masonry Layout
34679     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34680     */
34681     
34682     register : function(layout)
34683     {
34684         this.groups[layout.id] = layout;
34685     },
34686     /**
34687     * fetch a  Masonry Layout based on the masonry layout ID
34688     * @param {string} the masonry layout to add
34689     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34690     */
34691     
34692     get: function(layout_id) {
34693         if (typeof(this.groups[layout_id]) == 'undefined') {
34694             return false;
34695         }
34696         return this.groups[layout_id] ;
34697     }
34698     
34699     
34700     
34701 });
34702
34703  
34704
34705  /**
34706  *
34707  * This is based on 
34708  * http://masonry.desandro.com
34709  *
34710  * The idea is to render all the bricks based on vertical width...
34711  *
34712  * The original code extends 'outlayer' - we might need to use that....
34713  * 
34714  */
34715
34716
34717 /**
34718  * @class Roo.bootstrap.LayoutMasonryAuto
34719  * @extends Roo.bootstrap.Component
34720  * Bootstrap Layout Masonry class
34721  * 
34722  * @constructor
34723  * Create a new Element
34724  * @param {Object} config The config object
34725  */
34726
34727 Roo.bootstrap.LayoutMasonryAuto = function(config){
34728     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34729 };
34730
34731 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34732     
34733       /**
34734      * @cfg {Boolean} isFitWidth  - resize the width..
34735      */   
34736     isFitWidth : false,  // options..
34737     /**
34738      * @cfg {Boolean} isOriginLeft = left align?
34739      */   
34740     isOriginLeft : true,
34741     /**
34742      * @cfg {Boolean} isOriginTop = top align?
34743      */   
34744     isOriginTop : false,
34745     /**
34746      * @cfg {Boolean} isLayoutInstant = no animation?
34747      */   
34748     isLayoutInstant : false, // needed?
34749     /**
34750      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34751      */   
34752     isResizingContainer : true,
34753     /**
34754      * @cfg {Number} columnWidth  width of the columns 
34755      */   
34756     
34757     columnWidth : 0,
34758     
34759     /**
34760      * @cfg {Number} maxCols maximum number of columns
34761      */   
34762     
34763     maxCols: 0,
34764     /**
34765      * @cfg {Number} padHeight padding below box..
34766      */   
34767     
34768     padHeight : 10, 
34769     
34770     /**
34771      * @cfg {Boolean} isAutoInitial defalut true
34772      */   
34773     
34774     isAutoInitial : true, 
34775     
34776     // private?
34777     gutter : 0,
34778     
34779     containerWidth: 0,
34780     initialColumnWidth : 0,
34781     currentSize : null,
34782     
34783     colYs : null, // array.
34784     maxY : 0,
34785     padWidth: 10,
34786     
34787     
34788     tag: 'div',
34789     cls: '',
34790     bricks: null, //CompositeElement
34791     cols : 0, // array?
34792     // element : null, // wrapped now this.el
34793     _isLayoutInited : null, 
34794     
34795     
34796     getAutoCreate : function(){
34797         
34798         var cfg = {
34799             tag: this.tag,
34800             cls: 'blog-masonary-wrapper ' + this.cls,
34801             cn : {
34802                 cls : 'mas-boxes masonary'
34803             }
34804         };
34805         
34806         return cfg;
34807     },
34808     
34809     getChildContainer: function( )
34810     {
34811         if (this.boxesEl) {
34812             return this.boxesEl;
34813         }
34814         
34815         this.boxesEl = this.el.select('.mas-boxes').first();
34816         
34817         return this.boxesEl;
34818     },
34819     
34820     
34821     initEvents : function()
34822     {
34823         var _this = this;
34824         
34825         if(this.isAutoInitial){
34826             Roo.log('hook children rendered');
34827             this.on('childrenrendered', function() {
34828                 Roo.log('children rendered');
34829                 _this.initial();
34830             } ,this);
34831         }
34832         
34833     },
34834     
34835     initial : function()
34836     {
34837         this.reloadItems();
34838
34839         this.currentSize = this.el.getBox(true);
34840
34841         /// was window resize... - let's see if this works..
34842         Roo.EventManager.onWindowResize(this.resize, this); 
34843
34844         if(!this.isAutoInitial){
34845             this.layout();
34846             return;
34847         }
34848         
34849         this.layout.defer(500,this);
34850     },
34851     
34852     reloadItems: function()
34853     {
34854         this.bricks = this.el.select('.masonry-brick', true);
34855         
34856         this.bricks.each(function(b) {
34857             //Roo.log(b.getSize());
34858             if (!b.attr('originalwidth')) {
34859                 b.attr('originalwidth',  b.getSize().width);
34860             }
34861             
34862         });
34863         
34864         Roo.log(this.bricks.elements.length);
34865     },
34866     
34867     resize : function()
34868     {
34869         Roo.log('resize');
34870         var cs = this.el.getBox(true);
34871         
34872         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34873             Roo.log("no change in with or X");
34874             return;
34875         }
34876         this.currentSize = cs;
34877         this.layout();
34878     },
34879     
34880     layout : function()
34881     {
34882          Roo.log('layout');
34883         this._resetLayout();
34884         //this._manageStamps();
34885       
34886         // don't animate first layout
34887         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34888         this.layoutItems( isInstant );
34889       
34890         // flag for initalized
34891         this._isLayoutInited = true;
34892     },
34893     
34894     layoutItems : function( isInstant )
34895     {
34896         //var items = this._getItemsForLayout( this.items );
34897         // original code supports filtering layout items.. we just ignore it..
34898         
34899         this._layoutItems( this.bricks , isInstant );
34900       
34901         this._postLayout();
34902     },
34903     _layoutItems : function ( items , isInstant)
34904     {
34905        //this.fireEvent( 'layout', this, items );
34906     
34907
34908         if ( !items || !items.elements.length ) {
34909           // no items, emit event with empty array
34910             return;
34911         }
34912
34913         var queue = [];
34914         items.each(function(item) {
34915             Roo.log("layout item");
34916             Roo.log(item);
34917             // get x/y object from method
34918             var position = this._getItemLayoutPosition( item );
34919             // enqueue
34920             position.item = item;
34921             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34922             queue.push( position );
34923         }, this);
34924       
34925         this._processLayoutQueue( queue );
34926     },
34927     /** Sets position of item in DOM
34928     * @param {Element} item
34929     * @param {Number} x - horizontal position
34930     * @param {Number} y - vertical position
34931     * @param {Boolean} isInstant - disables transitions
34932     */
34933     _processLayoutQueue : function( queue )
34934     {
34935         for ( var i=0, len = queue.length; i < len; i++ ) {
34936             var obj = queue[i];
34937             obj.item.position('absolute');
34938             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34939         }
34940     },
34941       
34942     
34943     /**
34944     * Any logic you want to do after each layout,
34945     * i.e. size the container
34946     */
34947     _postLayout : function()
34948     {
34949         this.resizeContainer();
34950     },
34951     
34952     resizeContainer : function()
34953     {
34954         if ( !this.isResizingContainer ) {
34955             return;
34956         }
34957         var size = this._getContainerSize();
34958         if ( size ) {
34959             this.el.setSize(size.width,size.height);
34960             this.boxesEl.setSize(size.width,size.height);
34961         }
34962     },
34963     
34964     
34965     
34966     _resetLayout : function()
34967     {
34968         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34969         this.colWidth = this.el.getWidth();
34970         //this.gutter = this.el.getWidth(); 
34971         
34972         this.measureColumns();
34973
34974         // reset column Y
34975         var i = this.cols;
34976         this.colYs = [];
34977         while (i--) {
34978             this.colYs.push( 0 );
34979         }
34980     
34981         this.maxY = 0;
34982     },
34983
34984     measureColumns : function()
34985     {
34986         this.getContainerWidth();
34987       // if columnWidth is 0, default to outerWidth of first item
34988         if ( !this.columnWidth ) {
34989             var firstItem = this.bricks.first();
34990             Roo.log(firstItem);
34991             this.columnWidth  = this.containerWidth;
34992             if (firstItem && firstItem.attr('originalwidth') ) {
34993                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34994             }
34995             // columnWidth fall back to item of first element
34996             Roo.log("set column width?");
34997                         this.initialColumnWidth = this.columnWidth  ;
34998
34999             // if first elem has no width, default to size of container
35000             
35001         }
35002         
35003         
35004         if (this.initialColumnWidth) {
35005             this.columnWidth = this.initialColumnWidth;
35006         }
35007         
35008         
35009             
35010         // column width is fixed at the top - however if container width get's smaller we should
35011         // reduce it...
35012         
35013         // this bit calcs how man columns..
35014             
35015         var columnWidth = this.columnWidth += this.gutter;
35016       
35017         // calculate columns
35018         var containerWidth = this.containerWidth + this.gutter;
35019         
35020         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35021         // fix rounding errors, typically with gutters
35022         var excess = columnWidth - containerWidth % columnWidth;
35023         
35024         
35025         // if overshoot is less than a pixel, round up, otherwise floor it
35026         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35027         cols = Math[ mathMethod ]( cols );
35028         this.cols = Math.max( cols, 1 );
35029         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35030         
35031          // padding positioning..
35032         var totalColWidth = this.cols * this.columnWidth;
35033         var padavail = this.containerWidth - totalColWidth;
35034         // so for 2 columns - we need 3 'pads'
35035         
35036         var padNeeded = (1+this.cols) * this.padWidth;
35037         
35038         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35039         
35040         this.columnWidth += padExtra
35041         //this.padWidth = Math.floor(padavail /  ( this.cols));
35042         
35043         // adjust colum width so that padding is fixed??
35044         
35045         // we have 3 columns ... total = width * 3
35046         // we have X left over... that should be used by 
35047         
35048         //if (this.expandC) {
35049             
35050         //}
35051         
35052         
35053         
35054     },
35055     
35056     getContainerWidth : function()
35057     {
35058        /* // container is parent if fit width
35059         var container = this.isFitWidth ? this.element.parentNode : this.element;
35060         // check that this.size and size are there
35061         // IE8 triggers resize on body size change, so they might not be
35062         
35063         var size = getSize( container );  //FIXME
35064         this.containerWidth = size && size.innerWidth; //FIXME
35065         */
35066          
35067         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35068         
35069     },
35070     
35071     _getItemLayoutPosition : function( item )  // what is item?
35072     {
35073         // we resize the item to our columnWidth..
35074       
35075         item.setWidth(this.columnWidth);
35076         item.autoBoxAdjust  = false;
35077         
35078         var sz = item.getSize();
35079  
35080         // how many columns does this brick span
35081         var remainder = this.containerWidth % this.columnWidth;
35082         
35083         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35084         // round if off by 1 pixel, otherwise use ceil
35085         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35086         colSpan = Math.min( colSpan, this.cols );
35087         
35088         // normally this should be '1' as we dont' currently allow multi width columns..
35089         
35090         var colGroup = this._getColGroup( colSpan );
35091         // get the minimum Y value from the columns
35092         var minimumY = Math.min.apply( Math, colGroup );
35093         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35094         
35095         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35096          
35097         // position the brick
35098         var position = {
35099             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35100             y: this.currentSize.y + minimumY + this.padHeight
35101         };
35102         
35103         Roo.log(position);
35104         // apply setHeight to necessary columns
35105         var setHeight = minimumY + sz.height + this.padHeight;
35106         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35107         
35108         var setSpan = this.cols + 1 - colGroup.length;
35109         for ( var i = 0; i < setSpan; i++ ) {
35110           this.colYs[ shortColIndex + i ] = setHeight ;
35111         }
35112       
35113         return position;
35114     },
35115     
35116     /**
35117      * @param {Number} colSpan - number of columns the element spans
35118      * @returns {Array} colGroup
35119      */
35120     _getColGroup : function( colSpan )
35121     {
35122         if ( colSpan < 2 ) {
35123           // if brick spans only one column, use all the column Ys
35124           return this.colYs;
35125         }
35126       
35127         var colGroup = [];
35128         // how many different places could this brick fit horizontally
35129         var groupCount = this.cols + 1 - colSpan;
35130         // for each group potential horizontal position
35131         for ( var i = 0; i < groupCount; i++ ) {
35132           // make an array of colY values for that one group
35133           var groupColYs = this.colYs.slice( i, i + colSpan );
35134           // and get the max value of the array
35135           colGroup[i] = Math.max.apply( Math, groupColYs );
35136         }
35137         return colGroup;
35138     },
35139     /*
35140     _manageStamp : function( stamp )
35141     {
35142         var stampSize =  stamp.getSize();
35143         var offset = stamp.getBox();
35144         // get the columns that this stamp affects
35145         var firstX = this.isOriginLeft ? offset.x : offset.right;
35146         var lastX = firstX + stampSize.width;
35147         var firstCol = Math.floor( firstX / this.columnWidth );
35148         firstCol = Math.max( 0, firstCol );
35149         
35150         var lastCol = Math.floor( lastX / this.columnWidth );
35151         // lastCol should not go over if multiple of columnWidth #425
35152         lastCol -= lastX % this.columnWidth ? 0 : 1;
35153         lastCol = Math.min( this.cols - 1, lastCol );
35154         
35155         // set colYs to bottom of the stamp
35156         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35157             stampSize.height;
35158             
35159         for ( var i = firstCol; i <= lastCol; i++ ) {
35160           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35161         }
35162     },
35163     */
35164     
35165     _getContainerSize : function()
35166     {
35167         this.maxY = Math.max.apply( Math, this.colYs );
35168         var size = {
35169             height: this.maxY
35170         };
35171       
35172         if ( this.isFitWidth ) {
35173             size.width = this._getContainerFitWidth();
35174         }
35175       
35176         return size;
35177     },
35178     
35179     _getContainerFitWidth : function()
35180     {
35181         var unusedCols = 0;
35182         // count unused columns
35183         var i = this.cols;
35184         while ( --i ) {
35185           if ( this.colYs[i] !== 0 ) {
35186             break;
35187           }
35188           unusedCols++;
35189         }
35190         // fit container to columns that have been used
35191         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35192     },
35193     
35194     needsResizeLayout : function()
35195     {
35196         var previousWidth = this.containerWidth;
35197         this.getContainerWidth();
35198         return previousWidth !== this.containerWidth;
35199     }
35200  
35201 });
35202
35203  
35204
35205  /*
35206  * - LGPL
35207  *
35208  * element
35209  * 
35210  */
35211
35212 /**
35213  * @class Roo.bootstrap.MasonryBrick
35214  * @extends Roo.bootstrap.Component
35215  * Bootstrap MasonryBrick class
35216  * 
35217  * @constructor
35218  * Create a new MasonryBrick
35219  * @param {Object} config The config object
35220  */
35221
35222 Roo.bootstrap.MasonryBrick = function(config){
35223     
35224     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35225     
35226     Roo.bootstrap.MasonryBrick.register(this);
35227     
35228     this.addEvents({
35229         // raw events
35230         /**
35231          * @event click
35232          * When a MasonryBrick is clcik
35233          * @param {Roo.bootstrap.MasonryBrick} this
35234          * @param {Roo.EventObject} e
35235          */
35236         "click" : true
35237     });
35238 };
35239
35240 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35241     
35242     /**
35243      * @cfg {String} title
35244      */   
35245     title : '',
35246     /**
35247      * @cfg {String} html
35248      */   
35249     html : '',
35250     /**
35251      * @cfg {String} bgimage
35252      */   
35253     bgimage : '',
35254     /**
35255      * @cfg {String} videourl
35256      */   
35257     videourl : '',
35258     /**
35259      * @cfg {String} cls
35260      */   
35261     cls : '',
35262     /**
35263      * @cfg {String} href
35264      */   
35265     href : '',
35266     /**
35267      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35268      */   
35269     size : 'xs',
35270     
35271     /**
35272      * @cfg {String} placetitle (center|bottom)
35273      */   
35274     placetitle : '',
35275     
35276     /**
35277      * @cfg {Boolean} isFitContainer defalut true
35278      */   
35279     isFitContainer : true, 
35280     
35281     /**
35282      * @cfg {Boolean} preventDefault defalut false
35283      */   
35284     preventDefault : false, 
35285     
35286     /**
35287      * @cfg {Boolean} inverse defalut false
35288      */   
35289     maskInverse : false, 
35290     
35291     getAutoCreate : function()
35292     {
35293         if(!this.isFitContainer){
35294             return this.getSplitAutoCreate();
35295         }
35296         
35297         var cls = 'masonry-brick masonry-brick-full';
35298         
35299         if(this.href.length){
35300             cls += ' masonry-brick-link';
35301         }
35302         
35303         if(this.bgimage.length){
35304             cls += ' masonry-brick-image';
35305         }
35306         
35307         if(this.maskInverse){
35308             cls += ' mask-inverse';
35309         }
35310         
35311         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35312             cls += ' enable-mask';
35313         }
35314         
35315         if(this.size){
35316             cls += ' masonry-' + this.size + '-brick';
35317         }
35318         
35319         if(this.placetitle.length){
35320             
35321             switch (this.placetitle) {
35322                 case 'center' :
35323                     cls += ' masonry-center-title';
35324                     break;
35325                 case 'bottom' :
35326                     cls += ' masonry-bottom-title';
35327                     break;
35328                 default:
35329                     break;
35330             }
35331             
35332         } else {
35333             if(!this.html.length && !this.bgimage.length){
35334                 cls += ' masonry-center-title';
35335             }
35336
35337             if(!this.html.length && this.bgimage.length){
35338                 cls += ' masonry-bottom-title';
35339             }
35340         }
35341         
35342         if(this.cls){
35343             cls += ' ' + this.cls;
35344         }
35345         
35346         var cfg = {
35347             tag: (this.href.length) ? 'a' : 'div',
35348             cls: cls,
35349             cn: [
35350                 {
35351                     tag: 'div',
35352                     cls: 'masonry-brick-mask'
35353                 },
35354                 {
35355                     tag: 'div',
35356                     cls: 'masonry-brick-paragraph',
35357                     cn: []
35358                 }
35359             ]
35360         };
35361         
35362         if(this.href.length){
35363             cfg.href = this.href;
35364         }
35365         
35366         var cn = cfg.cn[1].cn;
35367         
35368         if(this.title.length){
35369             cn.push({
35370                 tag: 'h4',
35371                 cls: 'masonry-brick-title',
35372                 html: this.title
35373             });
35374         }
35375         
35376         if(this.html.length){
35377             cn.push({
35378                 tag: 'p',
35379                 cls: 'masonry-brick-text',
35380                 html: this.html
35381             });
35382         }
35383         
35384         if (!this.title.length && !this.html.length) {
35385             cfg.cn[1].cls += ' hide';
35386         }
35387         
35388         if(this.bgimage.length){
35389             cfg.cn.push({
35390                 tag: 'img',
35391                 cls: 'masonry-brick-image-view',
35392                 src: this.bgimage
35393             });
35394         }
35395         
35396         if(this.videourl.length){
35397             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35398             // youtube support only?
35399             cfg.cn.push({
35400                 tag: 'iframe',
35401                 cls: 'masonry-brick-image-view',
35402                 src: vurl,
35403                 frameborder : 0,
35404                 allowfullscreen : true
35405             });
35406         }
35407         
35408         return cfg;
35409         
35410     },
35411     
35412     getSplitAutoCreate : function()
35413     {
35414         var cls = 'masonry-brick masonry-brick-split';
35415         
35416         if(this.href.length){
35417             cls += ' masonry-brick-link';
35418         }
35419         
35420         if(this.bgimage.length){
35421             cls += ' masonry-brick-image';
35422         }
35423         
35424         if(this.size){
35425             cls += ' masonry-' + this.size + '-brick';
35426         }
35427         
35428         switch (this.placetitle) {
35429             case 'center' :
35430                 cls += ' masonry-center-title';
35431                 break;
35432             case 'bottom' :
35433                 cls += ' masonry-bottom-title';
35434                 break;
35435             default:
35436                 if(!this.bgimage.length){
35437                     cls += ' masonry-center-title';
35438                 }
35439
35440                 if(this.bgimage.length){
35441                     cls += ' masonry-bottom-title';
35442                 }
35443                 break;
35444         }
35445         
35446         if(this.cls){
35447             cls += ' ' + this.cls;
35448         }
35449         
35450         var cfg = {
35451             tag: (this.href.length) ? 'a' : 'div',
35452             cls: cls,
35453             cn: [
35454                 {
35455                     tag: 'div',
35456                     cls: 'masonry-brick-split-head',
35457                     cn: [
35458                         {
35459                             tag: 'div',
35460                             cls: 'masonry-brick-paragraph',
35461                             cn: []
35462                         }
35463                     ]
35464                 },
35465                 {
35466                     tag: 'div',
35467                     cls: 'masonry-brick-split-body',
35468                     cn: []
35469                 }
35470             ]
35471         };
35472         
35473         if(this.href.length){
35474             cfg.href = this.href;
35475         }
35476         
35477         if(this.title.length){
35478             cfg.cn[0].cn[0].cn.push({
35479                 tag: 'h4',
35480                 cls: 'masonry-brick-title',
35481                 html: this.title
35482             });
35483         }
35484         
35485         if(this.html.length){
35486             cfg.cn[1].cn.push({
35487                 tag: 'p',
35488                 cls: 'masonry-brick-text',
35489                 html: this.html
35490             });
35491         }
35492
35493         if(this.bgimage.length){
35494             cfg.cn[0].cn.push({
35495                 tag: 'img',
35496                 cls: 'masonry-brick-image-view',
35497                 src: this.bgimage
35498             });
35499         }
35500         
35501         if(this.videourl.length){
35502             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35503             // youtube support only?
35504             cfg.cn[0].cn.cn.push({
35505                 tag: 'iframe',
35506                 cls: 'masonry-brick-image-view',
35507                 src: vurl,
35508                 frameborder : 0,
35509                 allowfullscreen : true
35510             });
35511         }
35512         
35513         return cfg;
35514     },
35515     
35516     initEvents: function() 
35517     {
35518         switch (this.size) {
35519             case 'xs' :
35520                 this.x = 1;
35521                 this.y = 1;
35522                 break;
35523             case 'sm' :
35524                 this.x = 2;
35525                 this.y = 2;
35526                 break;
35527             case 'md' :
35528             case 'md-left' :
35529             case 'md-right' :
35530                 this.x = 3;
35531                 this.y = 3;
35532                 break;
35533             case 'tall' :
35534                 this.x = 2;
35535                 this.y = 3;
35536                 break;
35537             case 'wide' :
35538                 this.x = 3;
35539                 this.y = 2;
35540                 break;
35541             case 'wide-thin' :
35542                 this.x = 3;
35543                 this.y = 1;
35544                 break;
35545                         
35546             default :
35547                 break;
35548         }
35549         
35550         if(Roo.isTouch){
35551             this.el.on('touchstart', this.onTouchStart, this);
35552             this.el.on('touchmove', this.onTouchMove, this);
35553             this.el.on('touchend', this.onTouchEnd, this);
35554             this.el.on('contextmenu', this.onContextMenu, this);
35555         } else {
35556             this.el.on('mouseenter'  ,this.enter, this);
35557             this.el.on('mouseleave', this.leave, this);
35558             this.el.on('click', this.onClick, this);
35559         }
35560         
35561         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35562             this.parent().bricks.push(this);   
35563         }
35564         
35565     },
35566     
35567     onClick: function(e, el)
35568     {
35569         var time = this.endTimer - this.startTimer;
35570         // Roo.log(e.preventDefault());
35571         if(Roo.isTouch){
35572             if(time > 1000){
35573                 e.preventDefault();
35574                 return;
35575             }
35576         }
35577         
35578         if(!this.preventDefault){
35579             return;
35580         }
35581         
35582         e.preventDefault();
35583         
35584         if (this.activeClass != '') {
35585             this.selectBrick();
35586         }
35587         
35588         this.fireEvent('click', this, e);
35589     },
35590     
35591     enter: function(e, el)
35592     {
35593         e.preventDefault();
35594         
35595         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35596             return;
35597         }
35598         
35599         if(this.bgimage.length && this.html.length){
35600             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35601         }
35602     },
35603     
35604     leave: function(e, el)
35605     {
35606         e.preventDefault();
35607         
35608         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35609             return;
35610         }
35611         
35612         if(this.bgimage.length && this.html.length){
35613             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35614         }
35615     },
35616     
35617     onTouchStart: function(e, el)
35618     {
35619 //        e.preventDefault();
35620         
35621         this.touchmoved = false;
35622         
35623         if(!this.isFitContainer){
35624             return;
35625         }
35626         
35627         if(!this.bgimage.length || !this.html.length){
35628             return;
35629         }
35630         
35631         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35632         
35633         this.timer = new Date().getTime();
35634         
35635     },
35636     
35637     onTouchMove: function(e, el)
35638     {
35639         this.touchmoved = true;
35640     },
35641     
35642     onContextMenu : function(e,el)
35643     {
35644         e.preventDefault();
35645         e.stopPropagation();
35646         return false;
35647     },
35648     
35649     onTouchEnd: function(e, el)
35650     {
35651 //        e.preventDefault();
35652         
35653         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35654         
35655             this.leave(e,el);
35656             
35657             return;
35658         }
35659         
35660         if(!this.bgimage.length || !this.html.length){
35661             
35662             if(this.href.length){
35663                 window.location.href = this.href;
35664             }
35665             
35666             return;
35667         }
35668         
35669         if(!this.isFitContainer){
35670             return;
35671         }
35672         
35673         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35674         
35675         window.location.href = this.href;
35676     },
35677     
35678     //selection on single brick only
35679     selectBrick : function() {
35680         
35681         if (!this.parentId) {
35682             return;
35683         }
35684         
35685         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35686         var index = m.selectedBrick.indexOf(this.id);
35687         
35688         if ( index > -1) {
35689             m.selectedBrick.splice(index,1);
35690             this.el.removeClass(this.activeClass);
35691             return;
35692         }
35693         
35694         for(var i = 0; i < m.selectedBrick.length; i++) {
35695             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35696             b.el.removeClass(b.activeClass);
35697         }
35698         
35699         m.selectedBrick = [];
35700         
35701         m.selectedBrick.push(this.id);
35702         this.el.addClass(this.activeClass);
35703         return;
35704     },
35705     
35706     isSelected : function(){
35707         return this.el.hasClass(this.activeClass);
35708         
35709     }
35710 });
35711
35712 Roo.apply(Roo.bootstrap.MasonryBrick, {
35713     
35714     //groups: {},
35715     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35716      /**
35717     * register a Masonry Brick
35718     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35719     */
35720     
35721     register : function(brick)
35722     {
35723         //this.groups[brick.id] = brick;
35724         this.groups.add(brick.id, brick);
35725     },
35726     /**
35727     * fetch a  masonry brick based on the masonry brick ID
35728     * @param {string} the masonry brick to add
35729     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35730     */
35731     
35732     get: function(brick_id) 
35733     {
35734         // if (typeof(this.groups[brick_id]) == 'undefined') {
35735         //     return false;
35736         // }
35737         // return this.groups[brick_id] ;
35738         
35739         if(this.groups.key(brick_id)) {
35740             return this.groups.key(brick_id);
35741         }
35742         
35743         return false;
35744     }
35745     
35746     
35747     
35748 });
35749
35750  /*
35751  * - LGPL
35752  *
35753  * element
35754  * 
35755  */
35756
35757 /**
35758  * @class Roo.bootstrap.Brick
35759  * @extends Roo.bootstrap.Component
35760  * Bootstrap Brick class
35761  * 
35762  * @constructor
35763  * Create a new Brick
35764  * @param {Object} config The config object
35765  */
35766
35767 Roo.bootstrap.Brick = function(config){
35768     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35769     
35770     this.addEvents({
35771         // raw events
35772         /**
35773          * @event click
35774          * When a Brick is click
35775          * @param {Roo.bootstrap.Brick} this
35776          * @param {Roo.EventObject} e
35777          */
35778         "click" : true
35779     });
35780 };
35781
35782 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35783     
35784     /**
35785      * @cfg {String} title
35786      */   
35787     title : '',
35788     /**
35789      * @cfg {String} html
35790      */   
35791     html : '',
35792     /**
35793      * @cfg {String} bgimage
35794      */   
35795     bgimage : '',
35796     /**
35797      * @cfg {String} cls
35798      */   
35799     cls : '',
35800     /**
35801      * @cfg {String} href
35802      */   
35803     href : '',
35804     /**
35805      * @cfg {String} video
35806      */   
35807     video : '',
35808     /**
35809      * @cfg {Boolean} square
35810      */   
35811     square : true,
35812     
35813     getAutoCreate : function()
35814     {
35815         var cls = 'roo-brick';
35816         
35817         if(this.href.length){
35818             cls += ' roo-brick-link';
35819         }
35820         
35821         if(this.bgimage.length){
35822             cls += ' roo-brick-image';
35823         }
35824         
35825         if(!this.html.length && !this.bgimage.length){
35826             cls += ' roo-brick-center-title';
35827         }
35828         
35829         if(!this.html.length && this.bgimage.length){
35830             cls += ' roo-brick-bottom-title';
35831         }
35832         
35833         if(this.cls){
35834             cls += ' ' + this.cls;
35835         }
35836         
35837         var cfg = {
35838             tag: (this.href.length) ? 'a' : 'div',
35839             cls: cls,
35840             cn: [
35841                 {
35842                     tag: 'div',
35843                     cls: 'roo-brick-paragraph',
35844                     cn: []
35845                 }
35846             ]
35847         };
35848         
35849         if(this.href.length){
35850             cfg.href = this.href;
35851         }
35852         
35853         var cn = cfg.cn[0].cn;
35854         
35855         if(this.title.length){
35856             cn.push({
35857                 tag: 'h4',
35858                 cls: 'roo-brick-title',
35859                 html: this.title
35860             });
35861         }
35862         
35863         if(this.html.length){
35864             cn.push({
35865                 tag: 'p',
35866                 cls: 'roo-brick-text',
35867                 html: this.html
35868             });
35869         } else {
35870             cn.cls += ' hide';
35871         }
35872         
35873         if(this.bgimage.length){
35874             cfg.cn.push({
35875                 tag: 'img',
35876                 cls: 'roo-brick-image-view',
35877                 src: this.bgimage
35878             });
35879         }
35880         
35881         return cfg;
35882     },
35883     
35884     initEvents: function() 
35885     {
35886         if(this.title.length || this.html.length){
35887             this.el.on('mouseenter'  ,this.enter, this);
35888             this.el.on('mouseleave', this.leave, this);
35889         }
35890         
35891         Roo.EventManager.onWindowResize(this.resize, this); 
35892         
35893         if(this.bgimage.length){
35894             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35895             this.imageEl.on('load', this.onImageLoad, this);
35896             return;
35897         }
35898         
35899         this.resize();
35900     },
35901     
35902     onImageLoad : function()
35903     {
35904         this.resize();
35905     },
35906     
35907     resize : function()
35908     {
35909         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35910         
35911         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35912         
35913         if(this.bgimage.length){
35914             var image = this.el.select('.roo-brick-image-view', true).first();
35915             
35916             image.setWidth(paragraph.getWidth());
35917             
35918             if(this.square){
35919                 image.setHeight(paragraph.getWidth());
35920             }
35921             
35922             this.el.setHeight(image.getHeight());
35923             paragraph.setHeight(image.getHeight());
35924             
35925         }
35926         
35927     },
35928     
35929     enter: function(e, el)
35930     {
35931         e.preventDefault();
35932         
35933         if(this.bgimage.length){
35934             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35935             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35936         }
35937     },
35938     
35939     leave: function(e, el)
35940     {
35941         e.preventDefault();
35942         
35943         if(this.bgimage.length){
35944             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35945             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35946         }
35947     }
35948     
35949 });
35950
35951  
35952
35953  /*
35954  * - LGPL
35955  *
35956  * Number field 
35957  */
35958
35959 /**
35960  * @class Roo.bootstrap.NumberField
35961  * @extends Roo.bootstrap.Input
35962  * Bootstrap NumberField class
35963  * 
35964  * 
35965  * 
35966  * 
35967  * @constructor
35968  * Create a new NumberField
35969  * @param {Object} config The config object
35970  */
35971
35972 Roo.bootstrap.NumberField = function(config){
35973     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35974 };
35975
35976 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35977     
35978     /**
35979      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35980      */
35981     allowDecimals : true,
35982     /**
35983      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35984      */
35985     decimalSeparator : ".",
35986     /**
35987      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35988      */
35989     decimalPrecision : 2,
35990     /**
35991      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35992      */
35993     allowNegative : true,
35994     
35995     /**
35996      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35997      */
35998     allowZero: true,
35999     /**
36000      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36001      */
36002     minValue : Number.NEGATIVE_INFINITY,
36003     /**
36004      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36005      */
36006     maxValue : Number.MAX_VALUE,
36007     /**
36008      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36009      */
36010     minText : "The minimum value for this field is {0}",
36011     /**
36012      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36013      */
36014     maxText : "The maximum value for this field is {0}",
36015     /**
36016      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36017      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36018      */
36019     nanText : "{0} is not a valid number",
36020     /**
36021      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36022      */
36023     thousandsDelimiter : false,
36024     /**
36025      * @cfg {String} valueAlign alignment of value
36026      */
36027     valueAlign : "left",
36028
36029     getAutoCreate : function()
36030     {
36031         var hiddenInput = {
36032             tag: 'input',
36033             type: 'hidden',
36034             id: Roo.id(),
36035             cls: 'hidden-number-input'
36036         };
36037         
36038         if (this.name) {
36039             hiddenInput.name = this.name;
36040         }
36041         
36042         this.name = '';
36043         
36044         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36045         
36046         this.name = hiddenInput.name;
36047         
36048         if(cfg.cn.length > 0) {
36049             cfg.cn.push(hiddenInput);
36050         }
36051         
36052         return cfg;
36053     },
36054
36055     // private
36056     initEvents : function()
36057     {   
36058         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36059         
36060         var allowed = "0123456789";
36061         
36062         if(this.allowDecimals){
36063             allowed += this.decimalSeparator;
36064         }
36065         
36066         if(this.allowNegative){
36067             allowed += "-";
36068         }
36069         
36070         if(this.thousandsDelimiter) {
36071             allowed += ",";
36072         }
36073         
36074         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36075         
36076         var keyPress = function(e){
36077             
36078             var k = e.getKey();
36079             
36080             var c = e.getCharCode();
36081             
36082             if(
36083                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36084                     allowed.indexOf(String.fromCharCode(c)) === -1
36085             ){
36086                 e.stopEvent();
36087                 return;
36088             }
36089             
36090             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36091                 return;
36092             }
36093             
36094             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36095                 e.stopEvent();
36096             }
36097         };
36098         
36099         this.el.on("keypress", keyPress, this);
36100     },
36101     
36102     validateValue : function(value)
36103     {
36104         
36105         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36106             return false;
36107         }
36108         
36109         var num = this.parseValue(value);
36110         
36111         if(isNaN(num)){
36112             this.markInvalid(String.format(this.nanText, value));
36113             return false;
36114         }
36115         
36116         if(num < this.minValue){
36117             this.markInvalid(String.format(this.minText, this.minValue));
36118             return false;
36119         }
36120         
36121         if(num > this.maxValue){
36122             this.markInvalid(String.format(this.maxText, this.maxValue));
36123             return false;
36124         }
36125         
36126         return true;
36127     },
36128
36129     getValue : function()
36130     {
36131         var v = this.hiddenEl().getValue();
36132         
36133         return this.fixPrecision(this.parseValue(v));
36134     },
36135
36136     parseValue : function(value)
36137     {
36138         if(this.thousandsDelimiter) {
36139             value += "";
36140             r = new RegExp(",", "g");
36141             value = value.replace(r, "");
36142         }
36143         
36144         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36145         return isNaN(value) ? '' : value;
36146     },
36147
36148     fixPrecision : function(value)
36149     {
36150         if(this.thousandsDelimiter) {
36151             value += "";
36152             r = new RegExp(",", "g");
36153             value = value.replace(r, "");
36154         }
36155         
36156         var nan = isNaN(value);
36157         
36158         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36159             return nan ? '' : value;
36160         }
36161         return parseFloat(value).toFixed(this.decimalPrecision);
36162     },
36163
36164     setValue : function(v)
36165     {
36166         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36167         
36168         this.value = v;
36169         
36170         if(this.rendered){
36171             
36172             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36173             
36174             this.inputEl().dom.value = (v == '') ? '' :
36175                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36176             
36177             if(!this.allowZero && v === '0') {
36178                 this.hiddenEl().dom.value = '';
36179                 this.inputEl().dom.value = '';
36180             }
36181             
36182             this.validate();
36183         }
36184     },
36185
36186     decimalPrecisionFcn : function(v)
36187     {
36188         return Math.floor(v);
36189     },
36190
36191     beforeBlur : function()
36192     {
36193         var v = this.parseValue(this.getRawValue());
36194         
36195         if(v || v === 0 || v === ''){
36196             this.setValue(v);
36197         }
36198     },
36199     
36200     hiddenEl : function()
36201     {
36202         return this.el.select('input.hidden-number-input',true).first();
36203     }
36204     
36205 });
36206
36207  
36208
36209 /*
36210 * Licence: LGPL
36211 */
36212
36213 /**
36214  * @class Roo.bootstrap.DocumentSlider
36215  * @extends Roo.bootstrap.Component
36216  * Bootstrap DocumentSlider class
36217  * 
36218  * @constructor
36219  * Create a new DocumentViewer
36220  * @param {Object} config The config object
36221  */
36222
36223 Roo.bootstrap.DocumentSlider = function(config){
36224     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36225     
36226     this.files = [];
36227     
36228     this.addEvents({
36229         /**
36230          * @event initial
36231          * Fire after initEvent
36232          * @param {Roo.bootstrap.DocumentSlider} this
36233          */
36234         "initial" : true,
36235         /**
36236          * @event update
36237          * Fire after update
36238          * @param {Roo.bootstrap.DocumentSlider} this
36239          */
36240         "update" : true,
36241         /**
36242          * @event click
36243          * Fire after click
36244          * @param {Roo.bootstrap.DocumentSlider} this
36245          */
36246         "click" : true
36247     });
36248 };
36249
36250 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36251     
36252     files : false,
36253     
36254     indicator : 0,
36255     
36256     getAutoCreate : function()
36257     {
36258         var cfg = {
36259             tag : 'div',
36260             cls : 'roo-document-slider',
36261             cn : [
36262                 {
36263                     tag : 'div',
36264                     cls : 'roo-document-slider-header',
36265                     cn : [
36266                         {
36267                             tag : 'div',
36268                             cls : 'roo-document-slider-header-title'
36269                         }
36270                     ]
36271                 },
36272                 {
36273                     tag : 'div',
36274                     cls : 'roo-document-slider-body',
36275                     cn : [
36276                         {
36277                             tag : 'div',
36278                             cls : 'roo-document-slider-prev',
36279                             cn : [
36280                                 {
36281                                     tag : 'i',
36282                                     cls : 'fa fa-chevron-left'
36283                                 }
36284                             ]
36285                         },
36286                         {
36287                             tag : 'div',
36288                             cls : 'roo-document-slider-thumb',
36289                             cn : [
36290                                 {
36291                                     tag : 'img',
36292                                     cls : 'roo-document-slider-image'
36293                                 }
36294                             ]
36295                         },
36296                         {
36297                             tag : 'div',
36298                             cls : 'roo-document-slider-next',
36299                             cn : [
36300                                 {
36301                                     tag : 'i',
36302                                     cls : 'fa fa-chevron-right'
36303                                 }
36304                             ]
36305                         }
36306                     ]
36307                 }
36308             ]
36309         };
36310         
36311         return cfg;
36312     },
36313     
36314     initEvents : function()
36315     {
36316         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36317         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36318         
36319         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36320         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36321         
36322         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36323         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36324         
36325         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36326         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36327         
36328         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36329         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36330         
36331         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36332         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36333         
36334         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36335         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36336         
36337         this.thumbEl.on('click', this.onClick, this);
36338         
36339         this.prevIndicator.on('click', this.prev, this);
36340         
36341         this.nextIndicator.on('click', this.next, this);
36342         
36343     },
36344     
36345     initial : function()
36346     {
36347         if(this.files.length){
36348             this.indicator = 1;
36349             this.update()
36350         }
36351         
36352         this.fireEvent('initial', this);
36353     },
36354     
36355     update : function()
36356     {
36357         this.imageEl.attr('src', this.files[this.indicator - 1]);
36358         
36359         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36360         
36361         this.prevIndicator.show();
36362         
36363         if(this.indicator == 1){
36364             this.prevIndicator.hide();
36365         }
36366         
36367         this.nextIndicator.show();
36368         
36369         if(this.indicator == this.files.length){
36370             this.nextIndicator.hide();
36371         }
36372         
36373         this.thumbEl.scrollTo('top');
36374         
36375         this.fireEvent('update', this);
36376     },
36377     
36378     onClick : function(e)
36379     {
36380         e.preventDefault();
36381         
36382         this.fireEvent('click', this);
36383     },
36384     
36385     prev : function(e)
36386     {
36387         e.preventDefault();
36388         
36389         this.indicator = Math.max(1, this.indicator - 1);
36390         
36391         this.update();
36392     },
36393     
36394     next : function(e)
36395     {
36396         e.preventDefault();
36397         
36398         this.indicator = Math.min(this.files.length, this.indicator + 1);
36399         
36400         this.update();
36401     }
36402 });
36403 /*
36404  * - LGPL
36405  *
36406  * RadioSet
36407  *
36408  *
36409  */
36410
36411 /**
36412  * @class Roo.bootstrap.RadioSet
36413  * @extends Roo.bootstrap.Input
36414  * Bootstrap RadioSet class
36415  * @cfg {String} indicatorpos (left|right) default left
36416  * @cfg {Boolean} inline (true|false) inline the element (default true)
36417  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36418  * @constructor
36419  * Create a new RadioSet
36420  * @param {Object} config The config object
36421  */
36422
36423 Roo.bootstrap.RadioSet = function(config){
36424     
36425     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36426     
36427     this.radioes = [];
36428     
36429     Roo.bootstrap.RadioSet.register(this);
36430     
36431     this.addEvents({
36432         /**
36433         * @event check
36434         * Fires when the element is checked or unchecked.
36435         * @param {Roo.bootstrap.RadioSet} this This radio
36436         * @param {Roo.bootstrap.Radio} item The checked item
36437         */
36438        check : true,
36439        /**
36440         * @event click
36441         * Fires when the element is click.
36442         * @param {Roo.bootstrap.RadioSet} this This radio set
36443         * @param {Roo.bootstrap.Radio} item The checked item
36444         * @param {Roo.EventObject} e The event object
36445         */
36446        click : true
36447     });
36448     
36449 };
36450
36451 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36452
36453     radioes : false,
36454     
36455     inline : true,
36456     
36457     weight : '',
36458     
36459     indicatorpos : 'left',
36460     
36461     getAutoCreate : function()
36462     {
36463         var label = {
36464             tag : 'label',
36465             cls : 'roo-radio-set-label',
36466             cn : [
36467                 {
36468                     tag : 'span',
36469                     html : this.fieldLabel
36470                 }
36471             ]
36472         };
36473         if (Roo.bootstrap.version == 3) {
36474             
36475             
36476             if(this.indicatorpos == 'left'){
36477                 label.cn.unshift({
36478                     tag : 'i',
36479                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36480                     tooltip : 'This field is required'
36481                 });
36482             } else {
36483                 label.cn.push({
36484                     tag : 'i',
36485                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36486                     tooltip : 'This field is required'
36487                 });
36488             }
36489         }
36490         var items = {
36491             tag : 'div',
36492             cls : 'roo-radio-set-items'
36493         };
36494         
36495         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36496         
36497         if (align === 'left' && this.fieldLabel.length) {
36498             
36499             items = {
36500                 cls : "roo-radio-set-right", 
36501                 cn: [
36502                     items
36503                 ]
36504             };
36505             
36506             if(this.labelWidth > 12){
36507                 label.style = "width: " + this.labelWidth + 'px';
36508             }
36509             
36510             if(this.labelWidth < 13 && this.labelmd == 0){
36511                 this.labelmd = this.labelWidth;
36512             }
36513             
36514             if(this.labellg > 0){
36515                 label.cls += ' col-lg-' + this.labellg;
36516                 items.cls += ' col-lg-' + (12 - this.labellg);
36517             }
36518             
36519             if(this.labelmd > 0){
36520                 label.cls += ' col-md-' + this.labelmd;
36521                 items.cls += ' col-md-' + (12 - this.labelmd);
36522             }
36523             
36524             if(this.labelsm > 0){
36525                 label.cls += ' col-sm-' + this.labelsm;
36526                 items.cls += ' col-sm-' + (12 - this.labelsm);
36527             }
36528             
36529             if(this.labelxs > 0){
36530                 label.cls += ' col-xs-' + this.labelxs;
36531                 items.cls += ' col-xs-' + (12 - this.labelxs);
36532             }
36533         }
36534         
36535         var cfg = {
36536             tag : 'div',
36537             cls : 'roo-radio-set',
36538             cn : [
36539                 {
36540                     tag : 'input',
36541                     cls : 'roo-radio-set-input',
36542                     type : 'hidden',
36543                     name : this.name,
36544                     value : this.value ? this.value :  ''
36545                 },
36546                 label,
36547                 items
36548             ]
36549         };
36550         
36551         if(this.weight.length){
36552             cfg.cls += ' roo-radio-' + this.weight;
36553         }
36554         
36555         if(this.inline) {
36556             cfg.cls += ' roo-radio-set-inline';
36557         }
36558         
36559         var settings=this;
36560         ['xs','sm','md','lg'].map(function(size){
36561             if (settings[size]) {
36562                 cfg.cls += ' col-' + size + '-' + settings[size];
36563             }
36564         });
36565         
36566         return cfg;
36567         
36568     },
36569
36570     initEvents : function()
36571     {
36572         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36573         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36574         
36575         if(!this.fieldLabel.length){
36576             this.labelEl.hide();
36577         }
36578         
36579         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36580         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36581         
36582         this.indicator = this.indicatorEl();
36583         
36584         if(this.indicator){
36585             this.indicator.addClass('invisible');
36586         }
36587         
36588         this.originalValue = this.getValue();
36589         
36590     },
36591     
36592     inputEl: function ()
36593     {
36594         return this.el.select('.roo-radio-set-input', true).first();
36595     },
36596     
36597     getChildContainer : function()
36598     {
36599         return this.itemsEl;
36600     },
36601     
36602     register : function(item)
36603     {
36604         this.radioes.push(item);
36605         
36606     },
36607     
36608     validate : function()
36609     {   
36610         if(this.getVisibilityEl().hasClass('hidden')){
36611             return true;
36612         }
36613         
36614         var valid = false;
36615         
36616         Roo.each(this.radioes, function(i){
36617             if(!i.checked){
36618                 return;
36619             }
36620             
36621             valid = true;
36622             return false;
36623         });
36624         
36625         if(this.allowBlank) {
36626             return true;
36627         }
36628         
36629         if(this.disabled || valid){
36630             this.markValid();
36631             return true;
36632         }
36633         
36634         this.markInvalid();
36635         return false;
36636         
36637     },
36638     
36639     markValid : function()
36640     {
36641         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36642             this.indicatorEl().removeClass('visible');
36643             this.indicatorEl().addClass('invisible');
36644         }
36645         
36646         
36647         if (Roo.bootstrap.version == 3) {
36648             this.el.removeClass([this.invalidClass, this.validClass]);
36649             this.el.addClass(this.validClass);
36650         } else {
36651             this.el.removeClass(['is-invalid','is-valid']);
36652             this.el.addClass(['is-valid']);
36653         }
36654         this.fireEvent('valid', this);
36655     },
36656     
36657     markInvalid : function(msg)
36658     {
36659         if(this.allowBlank || this.disabled){
36660             return;
36661         }
36662         
36663         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36664             this.indicatorEl().removeClass('invisible');
36665             this.indicatorEl().addClass('visible');
36666         }
36667         if (Roo.bootstrap.version == 3) {
36668             this.el.removeClass([this.invalidClass, this.validClass]);
36669             this.el.addClass(this.invalidClass);
36670         } else {
36671             this.el.removeClass(['is-invalid','is-valid']);
36672             this.el.addClass(['is-invalid']);
36673         }
36674         
36675         this.fireEvent('invalid', this, msg);
36676         
36677     },
36678     
36679     setValue : function(v, suppressEvent)
36680     {   
36681         if(this.value === v){
36682             return;
36683         }
36684         
36685         this.value = v;
36686         
36687         if(this.rendered){
36688             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36689         }
36690         
36691         Roo.each(this.radioes, function(i){
36692             i.checked = false;
36693             i.el.removeClass('checked');
36694         });
36695         
36696         Roo.each(this.radioes, function(i){
36697             
36698             if(i.value === v || i.value.toString() === v.toString()){
36699                 i.checked = true;
36700                 i.el.addClass('checked');
36701                 
36702                 if(suppressEvent !== true){
36703                     this.fireEvent('check', this, i);
36704                 }
36705                 
36706                 return false;
36707             }
36708             
36709         }, this);
36710         
36711         this.validate();
36712     },
36713     
36714     clearInvalid : function(){
36715         
36716         if(!this.el || this.preventMark){
36717             return;
36718         }
36719         
36720         this.el.removeClass([this.invalidClass]);
36721         
36722         this.fireEvent('valid', this);
36723     }
36724     
36725 });
36726
36727 Roo.apply(Roo.bootstrap.RadioSet, {
36728     
36729     groups: {},
36730     
36731     register : function(set)
36732     {
36733         this.groups[set.name] = set;
36734     },
36735     
36736     get: function(name) 
36737     {
36738         if (typeof(this.groups[name]) == 'undefined') {
36739             return false;
36740         }
36741         
36742         return this.groups[name] ;
36743     }
36744     
36745 });
36746 /*
36747  * Based on:
36748  * Ext JS Library 1.1.1
36749  * Copyright(c) 2006-2007, Ext JS, LLC.
36750  *
36751  * Originally Released Under LGPL - original licence link has changed is not relivant.
36752  *
36753  * Fork - LGPL
36754  * <script type="text/javascript">
36755  */
36756
36757
36758 /**
36759  * @class Roo.bootstrap.SplitBar
36760  * @extends Roo.util.Observable
36761  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36762  * <br><br>
36763  * Usage:
36764  * <pre><code>
36765 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36766                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36767 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36768 split.minSize = 100;
36769 split.maxSize = 600;
36770 split.animate = true;
36771 split.on('moved', splitterMoved);
36772 </code></pre>
36773  * @constructor
36774  * Create a new SplitBar
36775  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36776  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36777  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36778  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36779                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36780                         position of the SplitBar).
36781  */
36782 Roo.bootstrap.SplitBar = function(cfg){
36783     
36784     /** @private */
36785     
36786     //{
36787     //  dragElement : elm
36788     //  resizingElement: el,
36789         // optional..
36790     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36791     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36792         // existingProxy ???
36793     //}
36794     
36795     this.el = Roo.get(cfg.dragElement, true);
36796     this.el.dom.unselectable = "on";
36797     /** @private */
36798     this.resizingEl = Roo.get(cfg.resizingElement, true);
36799
36800     /**
36801      * @private
36802      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36803      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36804      * @type Number
36805      */
36806     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36807     
36808     /**
36809      * The minimum size of the resizing element. (Defaults to 0)
36810      * @type Number
36811      */
36812     this.minSize = 0;
36813     
36814     /**
36815      * The maximum size of the resizing element. (Defaults to 2000)
36816      * @type Number
36817      */
36818     this.maxSize = 2000;
36819     
36820     /**
36821      * Whether to animate the transition to the new size
36822      * @type Boolean
36823      */
36824     this.animate = false;
36825     
36826     /**
36827      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36828      * @type Boolean
36829      */
36830     this.useShim = false;
36831     
36832     /** @private */
36833     this.shim = null;
36834     
36835     if(!cfg.existingProxy){
36836         /** @private */
36837         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36838     }else{
36839         this.proxy = Roo.get(cfg.existingProxy).dom;
36840     }
36841     /** @private */
36842     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36843     
36844     /** @private */
36845     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36846     
36847     /** @private */
36848     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36849     
36850     /** @private */
36851     this.dragSpecs = {};
36852     
36853     /**
36854      * @private The adapter to use to positon and resize elements
36855      */
36856     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36857     this.adapter.init(this);
36858     
36859     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36860         /** @private */
36861         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36862         this.el.addClass("roo-splitbar-h");
36863     }else{
36864         /** @private */
36865         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36866         this.el.addClass("roo-splitbar-v");
36867     }
36868     
36869     this.addEvents({
36870         /**
36871          * @event resize
36872          * Fires when the splitter is moved (alias for {@link #event-moved})
36873          * @param {Roo.bootstrap.SplitBar} this
36874          * @param {Number} newSize the new width or height
36875          */
36876         "resize" : true,
36877         /**
36878          * @event moved
36879          * Fires when the splitter is moved
36880          * @param {Roo.bootstrap.SplitBar} this
36881          * @param {Number} newSize the new width or height
36882          */
36883         "moved" : true,
36884         /**
36885          * @event beforeresize
36886          * Fires before the splitter is dragged
36887          * @param {Roo.bootstrap.SplitBar} this
36888          */
36889         "beforeresize" : true,
36890
36891         "beforeapply" : true
36892     });
36893
36894     Roo.util.Observable.call(this);
36895 };
36896
36897 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36898     onStartProxyDrag : function(x, y){
36899         this.fireEvent("beforeresize", this);
36900         if(!this.overlay){
36901             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36902             o.unselectable();
36903             o.enableDisplayMode("block");
36904             // all splitbars share the same overlay
36905             Roo.bootstrap.SplitBar.prototype.overlay = o;
36906         }
36907         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36908         this.overlay.show();
36909         Roo.get(this.proxy).setDisplayed("block");
36910         var size = this.adapter.getElementSize(this);
36911         this.activeMinSize = this.getMinimumSize();;
36912         this.activeMaxSize = this.getMaximumSize();;
36913         var c1 = size - this.activeMinSize;
36914         var c2 = Math.max(this.activeMaxSize - size, 0);
36915         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36916             this.dd.resetConstraints();
36917             this.dd.setXConstraint(
36918                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36919                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36920             );
36921             this.dd.setYConstraint(0, 0);
36922         }else{
36923             this.dd.resetConstraints();
36924             this.dd.setXConstraint(0, 0);
36925             this.dd.setYConstraint(
36926                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36927                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36928             );
36929          }
36930         this.dragSpecs.startSize = size;
36931         this.dragSpecs.startPoint = [x, y];
36932         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36933     },
36934     
36935     /** 
36936      * @private Called after the drag operation by the DDProxy
36937      */
36938     onEndProxyDrag : function(e){
36939         Roo.get(this.proxy).setDisplayed(false);
36940         var endPoint = Roo.lib.Event.getXY(e);
36941         if(this.overlay){
36942             this.overlay.hide();
36943         }
36944         var newSize;
36945         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36946             newSize = this.dragSpecs.startSize + 
36947                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36948                     endPoint[0] - this.dragSpecs.startPoint[0] :
36949                     this.dragSpecs.startPoint[0] - endPoint[0]
36950                 );
36951         }else{
36952             newSize = this.dragSpecs.startSize + 
36953                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36954                     endPoint[1] - this.dragSpecs.startPoint[1] :
36955                     this.dragSpecs.startPoint[1] - endPoint[1]
36956                 );
36957         }
36958         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36959         if(newSize != this.dragSpecs.startSize){
36960             if(this.fireEvent('beforeapply', this, newSize) !== false){
36961                 this.adapter.setElementSize(this, newSize);
36962                 this.fireEvent("moved", this, newSize);
36963                 this.fireEvent("resize", this, newSize);
36964             }
36965         }
36966     },
36967     
36968     /**
36969      * Get the adapter this SplitBar uses
36970      * @return The adapter object
36971      */
36972     getAdapter : function(){
36973         return this.adapter;
36974     },
36975     
36976     /**
36977      * Set the adapter this SplitBar uses
36978      * @param {Object} adapter A SplitBar adapter object
36979      */
36980     setAdapter : function(adapter){
36981         this.adapter = adapter;
36982         this.adapter.init(this);
36983     },
36984     
36985     /**
36986      * Gets the minimum size for the resizing element
36987      * @return {Number} The minimum size
36988      */
36989     getMinimumSize : function(){
36990         return this.minSize;
36991     },
36992     
36993     /**
36994      * Sets the minimum size for the resizing element
36995      * @param {Number} minSize The minimum size
36996      */
36997     setMinimumSize : function(minSize){
36998         this.minSize = minSize;
36999     },
37000     
37001     /**
37002      * Gets the maximum size for the resizing element
37003      * @return {Number} The maximum size
37004      */
37005     getMaximumSize : function(){
37006         return this.maxSize;
37007     },
37008     
37009     /**
37010      * Sets the maximum size for the resizing element
37011      * @param {Number} maxSize The maximum size
37012      */
37013     setMaximumSize : function(maxSize){
37014         this.maxSize = maxSize;
37015     },
37016     
37017     /**
37018      * Sets the initialize size for the resizing element
37019      * @param {Number} size The initial size
37020      */
37021     setCurrentSize : function(size){
37022         var oldAnimate = this.animate;
37023         this.animate = false;
37024         this.adapter.setElementSize(this, size);
37025         this.animate = oldAnimate;
37026     },
37027     
37028     /**
37029      * Destroy this splitbar. 
37030      * @param {Boolean} removeEl True to remove the element
37031      */
37032     destroy : function(removeEl){
37033         if(this.shim){
37034             this.shim.remove();
37035         }
37036         this.dd.unreg();
37037         this.proxy.parentNode.removeChild(this.proxy);
37038         if(removeEl){
37039             this.el.remove();
37040         }
37041     }
37042 });
37043
37044 /**
37045  * @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.
37046  */
37047 Roo.bootstrap.SplitBar.createProxy = function(dir){
37048     var proxy = new Roo.Element(document.createElement("div"));
37049     proxy.unselectable();
37050     var cls = 'roo-splitbar-proxy';
37051     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37052     document.body.appendChild(proxy.dom);
37053     return proxy.dom;
37054 };
37055
37056 /** 
37057  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37058  * Default Adapter. It assumes the splitter and resizing element are not positioned
37059  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37060  */
37061 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37062 };
37063
37064 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37065     // do nothing for now
37066     init : function(s){
37067     
37068     },
37069     /**
37070      * Called before drag operations to get the current size of the resizing element. 
37071      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37072      */
37073      getElementSize : function(s){
37074         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37075             return s.resizingEl.getWidth();
37076         }else{
37077             return s.resizingEl.getHeight();
37078         }
37079     },
37080     
37081     /**
37082      * Called after drag operations to set the size of the resizing element.
37083      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37084      * @param {Number} newSize The new size to set
37085      * @param {Function} onComplete A function to be invoked when resizing is complete
37086      */
37087     setElementSize : function(s, newSize, onComplete){
37088         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37089             if(!s.animate){
37090                 s.resizingEl.setWidth(newSize);
37091                 if(onComplete){
37092                     onComplete(s, newSize);
37093                 }
37094             }else{
37095                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37096             }
37097         }else{
37098             
37099             if(!s.animate){
37100                 s.resizingEl.setHeight(newSize);
37101                 if(onComplete){
37102                     onComplete(s, newSize);
37103                 }
37104             }else{
37105                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37106             }
37107         }
37108     }
37109 };
37110
37111 /** 
37112  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37113  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37114  * Adapter that  moves the splitter element to align with the resized sizing element. 
37115  * Used with an absolute positioned SplitBar.
37116  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37117  * document.body, make sure you assign an id to the body element.
37118  */
37119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37120     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37121     this.container = Roo.get(container);
37122 };
37123
37124 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37125     init : function(s){
37126         this.basic.init(s);
37127     },
37128     
37129     getElementSize : function(s){
37130         return this.basic.getElementSize(s);
37131     },
37132     
37133     setElementSize : function(s, newSize, onComplete){
37134         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37135     },
37136     
37137     moveSplitter : function(s){
37138         var yes = Roo.bootstrap.SplitBar;
37139         switch(s.placement){
37140             case yes.LEFT:
37141                 s.el.setX(s.resizingEl.getRight());
37142                 break;
37143             case yes.RIGHT:
37144                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37145                 break;
37146             case yes.TOP:
37147                 s.el.setY(s.resizingEl.getBottom());
37148                 break;
37149             case yes.BOTTOM:
37150                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37151                 break;
37152         }
37153     }
37154 };
37155
37156 /**
37157  * Orientation constant - Create a vertical SplitBar
37158  * @static
37159  * @type Number
37160  */
37161 Roo.bootstrap.SplitBar.VERTICAL = 1;
37162
37163 /**
37164  * Orientation constant - Create a horizontal SplitBar
37165  * @static
37166  * @type Number
37167  */
37168 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37169
37170 /**
37171  * Placement constant - The resizing element is to the left of the splitter element
37172  * @static
37173  * @type Number
37174  */
37175 Roo.bootstrap.SplitBar.LEFT = 1;
37176
37177 /**
37178  * Placement constant - The resizing element is to the right of the splitter element
37179  * @static
37180  * @type Number
37181  */
37182 Roo.bootstrap.SplitBar.RIGHT = 2;
37183
37184 /**
37185  * Placement constant - The resizing element is positioned above the splitter element
37186  * @static
37187  * @type Number
37188  */
37189 Roo.bootstrap.SplitBar.TOP = 3;
37190
37191 /**
37192  * Placement constant - The resizing element is positioned under splitter element
37193  * @static
37194  * @type Number
37195  */
37196 Roo.bootstrap.SplitBar.BOTTOM = 4;
37197 Roo.namespace("Roo.bootstrap.layout");/*
37198  * Based on:
37199  * Ext JS Library 1.1.1
37200  * Copyright(c) 2006-2007, Ext JS, LLC.
37201  *
37202  * Originally Released Under LGPL - original licence link has changed is not relivant.
37203  *
37204  * Fork - LGPL
37205  * <script type="text/javascript">
37206  */
37207
37208 /**
37209  * @class Roo.bootstrap.layout.Manager
37210  * @extends Roo.bootstrap.Component
37211  * Base class for layout managers.
37212  */
37213 Roo.bootstrap.layout.Manager = function(config)
37214 {
37215     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37216
37217
37218
37219
37220
37221     /** false to disable window resize monitoring @type Boolean */
37222     this.monitorWindowResize = true;
37223     this.regions = {};
37224     this.addEvents({
37225         /**
37226          * @event layout
37227          * Fires when a layout is performed.
37228          * @param {Roo.LayoutManager} this
37229          */
37230         "layout" : true,
37231         /**
37232          * @event regionresized
37233          * Fires when the user resizes a region.
37234          * @param {Roo.LayoutRegion} region The resized region
37235          * @param {Number} newSize The new size (width for east/west, height for north/south)
37236          */
37237         "regionresized" : true,
37238         /**
37239          * @event regioncollapsed
37240          * Fires when a region is collapsed.
37241          * @param {Roo.LayoutRegion} region The collapsed region
37242          */
37243         "regioncollapsed" : true,
37244         /**
37245          * @event regionexpanded
37246          * Fires when a region is expanded.
37247          * @param {Roo.LayoutRegion} region The expanded region
37248          */
37249         "regionexpanded" : true
37250     });
37251     this.updating = false;
37252
37253     if (config.el) {
37254         this.el = Roo.get(config.el);
37255         this.initEvents();
37256     }
37257
37258 };
37259
37260 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37261
37262
37263     regions : null,
37264
37265     monitorWindowResize : true,
37266
37267
37268     updating : false,
37269
37270
37271     onRender : function(ct, position)
37272     {
37273         if(!this.el){
37274             this.el = Roo.get(ct);
37275             this.initEvents();
37276         }
37277         //this.fireEvent('render',this);
37278     },
37279
37280
37281     initEvents: function()
37282     {
37283
37284
37285         // ie scrollbar fix
37286         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37287             document.body.scroll = "no";
37288         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37289             this.el.position('relative');
37290         }
37291         this.id = this.el.id;
37292         this.el.addClass("roo-layout-container");
37293         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37294         if(this.el.dom != document.body ) {
37295             this.el.on('resize', this.layout,this);
37296             this.el.on('show', this.layout,this);
37297         }
37298
37299     },
37300
37301     /**
37302      * Returns true if this layout is currently being updated
37303      * @return {Boolean}
37304      */
37305     isUpdating : function(){
37306         return this.updating;
37307     },
37308
37309     /**
37310      * Suspend the LayoutManager from doing auto-layouts while
37311      * making multiple add or remove calls
37312      */
37313     beginUpdate : function(){
37314         this.updating = true;
37315     },
37316
37317     /**
37318      * Restore auto-layouts and optionally disable the manager from performing a layout
37319      * @param {Boolean} noLayout true to disable a layout update
37320      */
37321     endUpdate : function(noLayout){
37322         this.updating = false;
37323         if(!noLayout){
37324             this.layout();
37325         }
37326     },
37327
37328     layout: function(){
37329         // abstract...
37330     },
37331
37332     onRegionResized : function(region, newSize){
37333         this.fireEvent("regionresized", region, newSize);
37334         this.layout();
37335     },
37336
37337     onRegionCollapsed : function(region){
37338         this.fireEvent("regioncollapsed", region);
37339     },
37340
37341     onRegionExpanded : function(region){
37342         this.fireEvent("regionexpanded", region);
37343     },
37344
37345     /**
37346      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37347      * performs box-model adjustments.
37348      * @return {Object} The size as an object {width: (the width), height: (the height)}
37349      */
37350     getViewSize : function()
37351     {
37352         var size;
37353         if(this.el.dom != document.body){
37354             size = this.el.getSize();
37355         }else{
37356             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37357         }
37358         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37359         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37360         return size;
37361     },
37362
37363     /**
37364      * Returns the Element this layout is bound to.
37365      * @return {Roo.Element}
37366      */
37367     getEl : function(){
37368         return this.el;
37369     },
37370
37371     /**
37372      * Returns the specified region.
37373      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37374      * @return {Roo.LayoutRegion}
37375      */
37376     getRegion : function(target){
37377         return this.regions[target.toLowerCase()];
37378     },
37379
37380     onWindowResize : function(){
37381         if(this.monitorWindowResize){
37382             this.layout();
37383         }
37384     }
37385 });
37386 /*
37387  * Based on:
37388  * Ext JS Library 1.1.1
37389  * Copyright(c) 2006-2007, Ext JS, LLC.
37390  *
37391  * Originally Released Under LGPL - original licence link has changed is not relivant.
37392  *
37393  * Fork - LGPL
37394  * <script type="text/javascript">
37395  */
37396 /**
37397  * @class Roo.bootstrap.layout.Border
37398  * @extends Roo.bootstrap.layout.Manager
37399  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37400  * please see: examples/bootstrap/nested.html<br><br>
37401  
37402 <b>The container the layout is rendered into can be either the body element or any other element.
37403 If it is not the body element, the container needs to either be an absolute positioned element,
37404 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37405 the container size if it is not the body element.</b>
37406
37407 * @constructor
37408 * Create a new Border
37409 * @param {Object} config Configuration options
37410  */
37411 Roo.bootstrap.layout.Border = function(config){
37412     config = config || {};
37413     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37414     
37415     
37416     
37417     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37418         if(config[region]){
37419             config[region].region = region;
37420             this.addRegion(config[region]);
37421         }
37422     },this);
37423     
37424 };
37425
37426 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37427
37428 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37429     
37430     parent : false, // this might point to a 'nest' or a ???
37431     
37432     /**
37433      * Creates and adds a new region if it doesn't already exist.
37434      * @param {String} target The target region key (north, south, east, west or center).
37435      * @param {Object} config The regions config object
37436      * @return {BorderLayoutRegion} The new region
37437      */
37438     addRegion : function(config)
37439     {
37440         if(!this.regions[config.region]){
37441             var r = this.factory(config);
37442             this.bindRegion(r);
37443         }
37444         return this.regions[config.region];
37445     },
37446
37447     // private (kinda)
37448     bindRegion : function(r){
37449         this.regions[r.config.region] = r;
37450         
37451         r.on("visibilitychange",    this.layout, this);
37452         r.on("paneladded",          this.layout, this);
37453         r.on("panelremoved",        this.layout, this);
37454         r.on("invalidated",         this.layout, this);
37455         r.on("resized",             this.onRegionResized, this);
37456         r.on("collapsed",           this.onRegionCollapsed, this);
37457         r.on("expanded",            this.onRegionExpanded, this);
37458     },
37459
37460     /**
37461      * Performs a layout update.
37462      */
37463     layout : function()
37464     {
37465         if(this.updating) {
37466             return;
37467         }
37468         
37469         // render all the rebions if they have not been done alreayd?
37470         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37471             if(this.regions[region] && !this.regions[region].bodyEl){
37472                 this.regions[region].onRender(this.el)
37473             }
37474         },this);
37475         
37476         var size = this.getViewSize();
37477         var w = size.width;
37478         var h = size.height;
37479         var centerW = w;
37480         var centerH = h;
37481         var centerY = 0;
37482         var centerX = 0;
37483         //var x = 0, y = 0;
37484
37485         var rs = this.regions;
37486         var north = rs["north"];
37487         var south = rs["south"]; 
37488         var west = rs["west"];
37489         var east = rs["east"];
37490         var center = rs["center"];
37491         //if(this.hideOnLayout){ // not supported anymore
37492             //c.el.setStyle("display", "none");
37493         //}
37494         if(north && north.isVisible()){
37495             var b = north.getBox();
37496             var m = north.getMargins();
37497             b.width = w - (m.left+m.right);
37498             b.x = m.left;
37499             b.y = m.top;
37500             centerY = b.height + b.y + m.bottom;
37501             centerH -= centerY;
37502             north.updateBox(this.safeBox(b));
37503         }
37504         if(south && south.isVisible()){
37505             var b = south.getBox();
37506             var m = south.getMargins();
37507             b.width = w - (m.left+m.right);
37508             b.x = m.left;
37509             var totalHeight = (b.height + m.top + m.bottom);
37510             b.y = h - totalHeight + m.top;
37511             centerH -= totalHeight;
37512             south.updateBox(this.safeBox(b));
37513         }
37514         if(west && west.isVisible()){
37515             var b = west.getBox();
37516             var m = west.getMargins();
37517             b.height = centerH - (m.top+m.bottom);
37518             b.x = m.left;
37519             b.y = centerY + m.top;
37520             var totalWidth = (b.width + m.left + m.right);
37521             centerX += totalWidth;
37522             centerW -= totalWidth;
37523             west.updateBox(this.safeBox(b));
37524         }
37525         if(east && east.isVisible()){
37526             var b = east.getBox();
37527             var m = east.getMargins();
37528             b.height = centerH - (m.top+m.bottom);
37529             var totalWidth = (b.width + m.left + m.right);
37530             b.x = w - totalWidth + m.left;
37531             b.y = centerY + m.top;
37532             centerW -= totalWidth;
37533             east.updateBox(this.safeBox(b));
37534         }
37535         if(center){
37536             var m = center.getMargins();
37537             var centerBox = {
37538                 x: centerX + m.left,
37539                 y: centerY + m.top,
37540                 width: centerW - (m.left+m.right),
37541                 height: centerH - (m.top+m.bottom)
37542             };
37543             //if(this.hideOnLayout){
37544                 //center.el.setStyle("display", "block");
37545             //}
37546             center.updateBox(this.safeBox(centerBox));
37547         }
37548         this.el.repaint();
37549         this.fireEvent("layout", this);
37550     },
37551
37552     // private
37553     safeBox : function(box){
37554         box.width = Math.max(0, box.width);
37555         box.height = Math.max(0, box.height);
37556         return box;
37557     },
37558
37559     /**
37560      * Adds a ContentPanel (or subclass) to this layout.
37561      * @param {String} target The target region key (north, south, east, west or center).
37562      * @param {Roo.ContentPanel} panel The panel to add
37563      * @return {Roo.ContentPanel} The added panel
37564      */
37565     add : function(target, panel){
37566          
37567         target = target.toLowerCase();
37568         return this.regions[target].add(panel);
37569     },
37570
37571     /**
37572      * Remove a ContentPanel (or subclass) to this layout.
37573      * @param {String} target The target region key (north, south, east, west or center).
37574      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37575      * @return {Roo.ContentPanel} The removed panel
37576      */
37577     remove : function(target, panel){
37578         target = target.toLowerCase();
37579         return this.regions[target].remove(panel);
37580     },
37581
37582     /**
37583      * Searches all regions for a panel with the specified id
37584      * @param {String} panelId
37585      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37586      */
37587     findPanel : function(panelId){
37588         var rs = this.regions;
37589         for(var target in rs){
37590             if(typeof rs[target] != "function"){
37591                 var p = rs[target].getPanel(panelId);
37592                 if(p){
37593                     return p;
37594                 }
37595             }
37596         }
37597         return null;
37598     },
37599
37600     /**
37601      * Searches all regions for a panel with the specified id and activates (shows) it.
37602      * @param {String/ContentPanel} panelId The panels id or the panel itself
37603      * @return {Roo.ContentPanel} The shown panel or null
37604      */
37605     showPanel : function(panelId) {
37606       var rs = this.regions;
37607       for(var target in rs){
37608          var r = rs[target];
37609          if(typeof r != "function"){
37610             if(r.hasPanel(panelId)){
37611                return r.showPanel(panelId);
37612             }
37613          }
37614       }
37615       return null;
37616    },
37617
37618    /**
37619      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37620      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37621      */
37622    /*
37623     restoreState : function(provider){
37624         if(!provider){
37625             provider = Roo.state.Manager;
37626         }
37627         var sm = new Roo.LayoutStateManager();
37628         sm.init(this, provider);
37629     },
37630 */
37631  
37632  
37633     /**
37634      * Adds a xtype elements to the layout.
37635      * <pre><code>
37636
37637 layout.addxtype({
37638        xtype : 'ContentPanel',
37639        region: 'west',
37640        items: [ .... ]
37641    }
37642 );
37643
37644 layout.addxtype({
37645         xtype : 'NestedLayoutPanel',
37646         region: 'west',
37647         layout: {
37648            center: { },
37649            west: { }   
37650         },
37651         items : [ ... list of content panels or nested layout panels.. ]
37652    }
37653 );
37654 </code></pre>
37655      * @param {Object} cfg Xtype definition of item to add.
37656      */
37657     addxtype : function(cfg)
37658     {
37659         // basically accepts a pannel...
37660         // can accept a layout region..!?!?
37661         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37662         
37663         
37664         // theory?  children can only be panels??
37665         
37666         //if (!cfg.xtype.match(/Panel$/)) {
37667         //    return false;
37668         //}
37669         var ret = false;
37670         
37671         if (typeof(cfg.region) == 'undefined') {
37672             Roo.log("Failed to add Panel, region was not set");
37673             Roo.log(cfg);
37674             return false;
37675         }
37676         var region = cfg.region;
37677         delete cfg.region;
37678         
37679           
37680         var xitems = [];
37681         if (cfg.items) {
37682             xitems = cfg.items;
37683             delete cfg.items;
37684         }
37685         var nb = false;
37686         
37687         if ( region == 'center') {
37688             Roo.log("Center: " + cfg.title);
37689         }
37690         
37691         
37692         switch(cfg.xtype) 
37693         {
37694             case 'Content':  // ContentPanel (el, cfg)
37695             case 'Scroll':  // ContentPanel (el, cfg)
37696             case 'View': 
37697                 cfg.autoCreate = cfg.autoCreate || true;
37698                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37699                 //} else {
37700                 //    var el = this.el.createChild();
37701                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37702                 //}
37703                 
37704                 this.add(region, ret);
37705                 break;
37706             
37707             /*
37708             case 'TreePanel': // our new panel!
37709                 cfg.el = this.el.createChild();
37710                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37711                 this.add(region, ret);
37712                 break;
37713             */
37714             
37715             case 'Nest': 
37716                 // create a new Layout (which is  a Border Layout...
37717                 
37718                 var clayout = cfg.layout;
37719                 clayout.el  = this.el.createChild();
37720                 clayout.items   = clayout.items  || [];
37721                 
37722                 delete cfg.layout;
37723                 
37724                 // replace this exitems with the clayout ones..
37725                 xitems = clayout.items;
37726                  
37727                 // force background off if it's in center...
37728                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37729                     cfg.background = false;
37730                 }
37731                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37732                 
37733                 
37734                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37735                 //console.log('adding nested layout panel '  + cfg.toSource());
37736                 this.add(region, ret);
37737                 nb = {}; /// find first...
37738                 break;
37739             
37740             case 'Grid':
37741                 
37742                 // needs grid and region
37743                 
37744                 //var el = this.getRegion(region).el.createChild();
37745                 /*
37746                  *var el = this.el.createChild();
37747                 // create the grid first...
37748                 cfg.grid.container = el;
37749                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37750                 */
37751                 
37752                 if (region == 'center' && this.active ) {
37753                     cfg.background = false;
37754                 }
37755                 
37756                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37757                 
37758                 this.add(region, ret);
37759                 /*
37760                 if (cfg.background) {
37761                     // render grid on panel activation (if panel background)
37762                     ret.on('activate', function(gp) {
37763                         if (!gp.grid.rendered) {
37764                     //        gp.grid.render(el);
37765                         }
37766                     });
37767                 } else {
37768                   //  cfg.grid.render(el);
37769                 }
37770                 */
37771                 break;
37772            
37773            
37774             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37775                 // it was the old xcomponent building that caused this before.
37776                 // espeically if border is the top element in the tree.
37777                 ret = this;
37778                 break; 
37779                 
37780                     
37781                 
37782                 
37783                 
37784             default:
37785                 /*
37786                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37787                     
37788                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37789                     this.add(region, ret);
37790                 } else {
37791                 */
37792                     Roo.log(cfg);
37793                     throw "Can not add '" + cfg.xtype + "' to Border";
37794                     return null;
37795              
37796                                 
37797              
37798         }
37799         this.beginUpdate();
37800         // add children..
37801         var region = '';
37802         var abn = {};
37803         Roo.each(xitems, function(i)  {
37804             region = nb && i.region ? i.region : false;
37805             
37806             var add = ret.addxtype(i);
37807            
37808             if (region) {
37809                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37810                 if (!i.background) {
37811                     abn[region] = nb[region] ;
37812                 }
37813             }
37814             
37815         });
37816         this.endUpdate();
37817
37818         // make the last non-background panel active..
37819         //if (nb) { Roo.log(abn); }
37820         if (nb) {
37821             
37822             for(var r in abn) {
37823                 region = this.getRegion(r);
37824                 if (region) {
37825                     // tried using nb[r], but it does not work..
37826                      
37827                     region.showPanel(abn[r]);
37828                    
37829                 }
37830             }
37831         }
37832         return ret;
37833         
37834     },
37835     
37836     
37837 // private
37838     factory : function(cfg)
37839     {
37840         
37841         var validRegions = Roo.bootstrap.layout.Border.regions;
37842
37843         var target = cfg.region;
37844         cfg.mgr = this;
37845         
37846         var r = Roo.bootstrap.layout;
37847         Roo.log(target);
37848         switch(target){
37849             case "north":
37850                 return new r.North(cfg);
37851             case "south":
37852                 return new r.South(cfg);
37853             case "east":
37854                 return new r.East(cfg);
37855             case "west":
37856                 return new r.West(cfg);
37857             case "center":
37858                 return new r.Center(cfg);
37859         }
37860         throw 'Layout region "'+target+'" not supported.';
37861     }
37862     
37863     
37864 });
37865  /*
37866  * Based on:
37867  * Ext JS Library 1.1.1
37868  * Copyright(c) 2006-2007, Ext JS, LLC.
37869  *
37870  * Originally Released Under LGPL - original licence link has changed is not relivant.
37871  *
37872  * Fork - LGPL
37873  * <script type="text/javascript">
37874  */
37875  
37876 /**
37877  * @class Roo.bootstrap.layout.Basic
37878  * @extends Roo.util.Observable
37879  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37880  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37881  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37882  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37883  * @cfg {string}   region  the region that it inhabits..
37884  * @cfg {bool}   skipConfig skip config?
37885  * 
37886
37887  */
37888 Roo.bootstrap.layout.Basic = function(config){
37889     
37890     this.mgr = config.mgr;
37891     
37892     this.position = config.region;
37893     
37894     var skipConfig = config.skipConfig;
37895     
37896     this.events = {
37897         /**
37898          * @scope Roo.BasicLayoutRegion
37899          */
37900         
37901         /**
37902          * @event beforeremove
37903          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37904          * @param {Roo.LayoutRegion} this
37905          * @param {Roo.ContentPanel} panel The panel
37906          * @param {Object} e The cancel event object
37907          */
37908         "beforeremove" : true,
37909         /**
37910          * @event invalidated
37911          * Fires when the layout for this region is changed.
37912          * @param {Roo.LayoutRegion} this
37913          */
37914         "invalidated" : true,
37915         /**
37916          * @event visibilitychange
37917          * Fires when this region is shown or hidden 
37918          * @param {Roo.LayoutRegion} this
37919          * @param {Boolean} visibility true or false
37920          */
37921         "visibilitychange" : true,
37922         /**
37923          * @event paneladded
37924          * Fires when a panel is added. 
37925          * @param {Roo.LayoutRegion} this
37926          * @param {Roo.ContentPanel} panel The panel
37927          */
37928         "paneladded" : true,
37929         /**
37930          * @event panelremoved
37931          * Fires when a panel is removed. 
37932          * @param {Roo.LayoutRegion} this
37933          * @param {Roo.ContentPanel} panel The panel
37934          */
37935         "panelremoved" : true,
37936         /**
37937          * @event beforecollapse
37938          * Fires when this region before collapse.
37939          * @param {Roo.LayoutRegion} this
37940          */
37941         "beforecollapse" : true,
37942         /**
37943          * @event collapsed
37944          * Fires when this region is collapsed.
37945          * @param {Roo.LayoutRegion} this
37946          */
37947         "collapsed" : true,
37948         /**
37949          * @event expanded
37950          * Fires when this region is expanded.
37951          * @param {Roo.LayoutRegion} this
37952          */
37953         "expanded" : true,
37954         /**
37955          * @event slideshow
37956          * Fires when this region is slid into view.
37957          * @param {Roo.LayoutRegion} this
37958          */
37959         "slideshow" : true,
37960         /**
37961          * @event slidehide
37962          * Fires when this region slides out of view. 
37963          * @param {Roo.LayoutRegion} this
37964          */
37965         "slidehide" : true,
37966         /**
37967          * @event panelactivated
37968          * Fires when a panel is activated. 
37969          * @param {Roo.LayoutRegion} this
37970          * @param {Roo.ContentPanel} panel The activated panel
37971          */
37972         "panelactivated" : true,
37973         /**
37974          * @event resized
37975          * Fires when the user resizes this region. 
37976          * @param {Roo.LayoutRegion} this
37977          * @param {Number} newSize The new size (width for east/west, height for north/south)
37978          */
37979         "resized" : true
37980     };
37981     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37982     this.panels = new Roo.util.MixedCollection();
37983     this.panels.getKey = this.getPanelId.createDelegate(this);
37984     this.box = null;
37985     this.activePanel = null;
37986     // ensure listeners are added...
37987     
37988     if (config.listeners || config.events) {
37989         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37990             listeners : config.listeners || {},
37991             events : config.events || {}
37992         });
37993     }
37994     
37995     if(skipConfig !== true){
37996         this.applyConfig(config);
37997     }
37998 };
37999
38000 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38001 {
38002     getPanelId : function(p){
38003         return p.getId();
38004     },
38005     
38006     applyConfig : function(config){
38007         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38008         this.config = config;
38009         
38010     },
38011     
38012     /**
38013      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38014      * the width, for horizontal (north, south) the height.
38015      * @param {Number} newSize The new width or height
38016      */
38017     resizeTo : function(newSize){
38018         var el = this.el ? this.el :
38019                  (this.activePanel ? this.activePanel.getEl() : null);
38020         if(el){
38021             switch(this.position){
38022                 case "east":
38023                 case "west":
38024                     el.setWidth(newSize);
38025                     this.fireEvent("resized", this, newSize);
38026                 break;
38027                 case "north":
38028                 case "south":
38029                     el.setHeight(newSize);
38030                     this.fireEvent("resized", this, newSize);
38031                 break;                
38032             }
38033         }
38034     },
38035     
38036     getBox : function(){
38037         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38038     },
38039     
38040     getMargins : function(){
38041         return this.margins;
38042     },
38043     
38044     updateBox : function(box){
38045         this.box = box;
38046         var el = this.activePanel.getEl();
38047         el.dom.style.left = box.x + "px";
38048         el.dom.style.top = box.y + "px";
38049         this.activePanel.setSize(box.width, box.height);
38050     },
38051     
38052     /**
38053      * Returns the container element for this region.
38054      * @return {Roo.Element}
38055      */
38056     getEl : function(){
38057         return this.activePanel;
38058     },
38059     
38060     /**
38061      * Returns true if this region is currently visible.
38062      * @return {Boolean}
38063      */
38064     isVisible : function(){
38065         return this.activePanel ? true : false;
38066     },
38067     
38068     setActivePanel : function(panel){
38069         panel = this.getPanel(panel);
38070         if(this.activePanel && this.activePanel != panel){
38071             this.activePanel.setActiveState(false);
38072             this.activePanel.getEl().setLeftTop(-10000,-10000);
38073         }
38074         this.activePanel = panel;
38075         panel.setActiveState(true);
38076         if(this.box){
38077             panel.setSize(this.box.width, this.box.height);
38078         }
38079         this.fireEvent("panelactivated", this, panel);
38080         this.fireEvent("invalidated");
38081     },
38082     
38083     /**
38084      * Show the specified panel.
38085      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38086      * @return {Roo.ContentPanel} The shown panel or null
38087      */
38088     showPanel : function(panel){
38089         panel = this.getPanel(panel);
38090         if(panel){
38091             this.setActivePanel(panel);
38092         }
38093         return panel;
38094     },
38095     
38096     /**
38097      * Get the active panel for this region.
38098      * @return {Roo.ContentPanel} The active panel or null
38099      */
38100     getActivePanel : function(){
38101         return this.activePanel;
38102     },
38103     
38104     /**
38105      * Add the passed ContentPanel(s)
38106      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38107      * @return {Roo.ContentPanel} The panel added (if only one was added)
38108      */
38109     add : function(panel){
38110         if(arguments.length > 1){
38111             for(var i = 0, len = arguments.length; i < len; i++) {
38112                 this.add(arguments[i]);
38113             }
38114             return null;
38115         }
38116         if(this.hasPanel(panel)){
38117             this.showPanel(panel);
38118             return panel;
38119         }
38120         var el = panel.getEl();
38121         if(el.dom.parentNode != this.mgr.el.dom){
38122             this.mgr.el.dom.appendChild(el.dom);
38123         }
38124         if(panel.setRegion){
38125             panel.setRegion(this);
38126         }
38127         this.panels.add(panel);
38128         el.setStyle("position", "absolute");
38129         if(!panel.background){
38130             this.setActivePanel(panel);
38131             if(this.config.initialSize && this.panels.getCount()==1){
38132                 this.resizeTo(this.config.initialSize);
38133             }
38134         }
38135         this.fireEvent("paneladded", this, panel);
38136         return panel;
38137     },
38138     
38139     /**
38140      * Returns true if the panel is in this region.
38141      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38142      * @return {Boolean}
38143      */
38144     hasPanel : function(panel){
38145         if(typeof panel == "object"){ // must be panel obj
38146             panel = panel.getId();
38147         }
38148         return this.getPanel(panel) ? true : false;
38149     },
38150     
38151     /**
38152      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38153      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38154      * @param {Boolean} preservePanel Overrides the config preservePanel option
38155      * @return {Roo.ContentPanel} The panel that was removed
38156      */
38157     remove : function(panel, preservePanel){
38158         panel = this.getPanel(panel);
38159         if(!panel){
38160             return null;
38161         }
38162         var e = {};
38163         this.fireEvent("beforeremove", this, panel, e);
38164         if(e.cancel === true){
38165             return null;
38166         }
38167         var panelId = panel.getId();
38168         this.panels.removeKey(panelId);
38169         return panel;
38170     },
38171     
38172     /**
38173      * Returns the panel specified or null if it's not in this region.
38174      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38175      * @return {Roo.ContentPanel}
38176      */
38177     getPanel : function(id){
38178         if(typeof id == "object"){ // must be panel obj
38179             return id;
38180         }
38181         return this.panels.get(id);
38182     },
38183     
38184     /**
38185      * Returns this regions position (north/south/east/west/center).
38186      * @return {String} 
38187      */
38188     getPosition: function(){
38189         return this.position;    
38190     }
38191 });/*
38192  * Based on:
38193  * Ext JS Library 1.1.1
38194  * Copyright(c) 2006-2007, Ext JS, LLC.
38195  *
38196  * Originally Released Under LGPL - original licence link has changed is not relivant.
38197  *
38198  * Fork - LGPL
38199  * <script type="text/javascript">
38200  */
38201  
38202 /**
38203  * @class Roo.bootstrap.layout.Region
38204  * @extends Roo.bootstrap.layout.Basic
38205  * This class represents a region in a layout manager.
38206  
38207  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38208  * @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})
38209  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38210  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38211  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38212  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38213  * @cfg {String}    title           The title for the region (overrides panel titles)
38214  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38215  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38216  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38217  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38218  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38219  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38220  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38221  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38222  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38223  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38224
38225  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38226  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38227  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38228  * @cfg {Number}    width           For East/West panels
38229  * @cfg {Number}    height          For North/South panels
38230  * @cfg {Boolean}   split           To show the splitter
38231  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38232  * 
38233  * @cfg {string}   cls             Extra CSS classes to add to region
38234  * 
38235  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38236  * @cfg {string}   region  the region that it inhabits..
38237  *
38238
38239  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38240  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38241
38242  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38243  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38244  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38245  */
38246 Roo.bootstrap.layout.Region = function(config)
38247 {
38248     this.applyConfig(config);
38249
38250     var mgr = config.mgr;
38251     var pos = config.region;
38252     config.skipConfig = true;
38253     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38254     
38255     if (mgr.el) {
38256         this.onRender(mgr.el);   
38257     }
38258      
38259     this.visible = true;
38260     this.collapsed = false;
38261     this.unrendered_panels = [];
38262 };
38263
38264 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38265
38266     position: '', // set by wrapper (eg. north/south etc..)
38267     unrendered_panels : null,  // unrendered panels.
38268     
38269     tabPosition : false,
38270     
38271     mgr: false, // points to 'Border'
38272     
38273     
38274     createBody : function(){
38275         /** This region's body element 
38276         * @type Roo.Element */
38277         this.bodyEl = this.el.createChild({
38278                 tag: "div",
38279                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38280         });
38281     },
38282
38283     onRender: function(ctr, pos)
38284     {
38285         var dh = Roo.DomHelper;
38286         /** This region's container element 
38287         * @type Roo.Element */
38288         this.el = dh.append(ctr.dom, {
38289                 tag: "div",
38290                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38291             }, true);
38292         /** This region's title element 
38293         * @type Roo.Element */
38294     
38295         this.titleEl = dh.append(this.el.dom,  {
38296                 tag: "div",
38297                 unselectable: "on",
38298                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38299                 children:[
38300                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38301                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38302                 ]
38303             }, true);
38304         
38305         this.titleEl.enableDisplayMode();
38306         /** This region's title text element 
38307         * @type HTMLElement */
38308         this.titleTextEl = this.titleEl.dom.firstChild;
38309         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38310         /*
38311         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38312         this.closeBtn.enableDisplayMode();
38313         this.closeBtn.on("click", this.closeClicked, this);
38314         this.closeBtn.hide();
38315     */
38316         this.createBody(this.config);
38317         if(this.config.hideWhenEmpty){
38318             this.hide();
38319             this.on("paneladded", this.validateVisibility, this);
38320             this.on("panelremoved", this.validateVisibility, this);
38321         }
38322         if(this.autoScroll){
38323             this.bodyEl.setStyle("overflow", "auto");
38324         }else{
38325             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38326         }
38327         //if(c.titlebar !== false){
38328             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38329                 this.titleEl.hide();
38330             }else{
38331                 this.titleEl.show();
38332                 if(this.config.title){
38333                     this.titleTextEl.innerHTML = this.config.title;
38334                 }
38335             }
38336         //}
38337         if(this.config.collapsed){
38338             this.collapse(true);
38339         }
38340         if(this.config.hidden){
38341             this.hide();
38342         }
38343         
38344         if (this.unrendered_panels && this.unrendered_panels.length) {
38345             for (var i =0;i< this.unrendered_panels.length; i++) {
38346                 this.add(this.unrendered_panels[i]);
38347             }
38348             this.unrendered_panels = null;
38349             
38350         }
38351         
38352     },
38353     
38354     applyConfig : function(c)
38355     {
38356         /*
38357          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38358             var dh = Roo.DomHelper;
38359             if(c.titlebar !== false){
38360                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38361                 this.collapseBtn.on("click", this.collapse, this);
38362                 this.collapseBtn.enableDisplayMode();
38363                 /*
38364                 if(c.showPin === true || this.showPin){
38365                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38366                     this.stickBtn.enableDisplayMode();
38367                     this.stickBtn.on("click", this.expand, this);
38368                     this.stickBtn.hide();
38369                 }
38370                 
38371             }
38372             */
38373             /** This region's collapsed element
38374             * @type Roo.Element */
38375             /*
38376              *
38377             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38378                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38379             ]}, true);
38380             
38381             if(c.floatable !== false){
38382                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38383                this.collapsedEl.on("click", this.collapseClick, this);
38384             }
38385
38386             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38387                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38388                    id: "message", unselectable: "on", style:{"float":"left"}});
38389                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38390              }
38391             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38392             this.expandBtn.on("click", this.expand, this);
38393             
38394         }
38395         
38396         if(this.collapseBtn){
38397             this.collapseBtn.setVisible(c.collapsible == true);
38398         }
38399         
38400         this.cmargins = c.cmargins || this.cmargins ||
38401                          (this.position == "west" || this.position == "east" ?
38402                              {top: 0, left: 2, right:2, bottom: 0} :
38403                              {top: 2, left: 0, right:0, bottom: 2});
38404         */
38405         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38406         
38407         
38408         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38409         
38410         this.autoScroll = c.autoScroll || false;
38411         
38412         
38413        
38414         
38415         this.duration = c.duration || .30;
38416         this.slideDuration = c.slideDuration || .45;
38417         this.config = c;
38418        
38419     },
38420     /**
38421      * Returns true if this region is currently visible.
38422      * @return {Boolean}
38423      */
38424     isVisible : function(){
38425         return this.visible;
38426     },
38427
38428     /**
38429      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38430      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38431      */
38432     //setCollapsedTitle : function(title){
38433     //    title = title || "&#160;";
38434      //   if(this.collapsedTitleTextEl){
38435       //      this.collapsedTitleTextEl.innerHTML = title;
38436        // }
38437     //},
38438
38439     getBox : function(){
38440         var b;
38441       //  if(!this.collapsed){
38442             b = this.el.getBox(false, true);
38443        // }else{
38444           //  b = this.collapsedEl.getBox(false, true);
38445         //}
38446         return b;
38447     },
38448
38449     getMargins : function(){
38450         return this.margins;
38451         //return this.collapsed ? this.cmargins : this.margins;
38452     },
38453 /*
38454     highlight : function(){
38455         this.el.addClass("x-layout-panel-dragover");
38456     },
38457
38458     unhighlight : function(){
38459         this.el.removeClass("x-layout-panel-dragover");
38460     },
38461 */
38462     updateBox : function(box)
38463     {
38464         if (!this.bodyEl) {
38465             return; // not rendered yet..
38466         }
38467         
38468         this.box = box;
38469         if(!this.collapsed){
38470             this.el.dom.style.left = box.x + "px";
38471             this.el.dom.style.top = box.y + "px";
38472             this.updateBody(box.width, box.height);
38473         }else{
38474             this.collapsedEl.dom.style.left = box.x + "px";
38475             this.collapsedEl.dom.style.top = box.y + "px";
38476             this.collapsedEl.setSize(box.width, box.height);
38477         }
38478         if(this.tabs){
38479             this.tabs.autoSizeTabs();
38480         }
38481     },
38482
38483     updateBody : function(w, h)
38484     {
38485         if(w !== null){
38486             this.el.setWidth(w);
38487             w -= this.el.getBorderWidth("rl");
38488             if(this.config.adjustments){
38489                 w += this.config.adjustments[0];
38490             }
38491         }
38492         if(h !== null && h > 0){
38493             this.el.setHeight(h);
38494             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38495             h -= this.el.getBorderWidth("tb");
38496             if(this.config.adjustments){
38497                 h += this.config.adjustments[1];
38498             }
38499             this.bodyEl.setHeight(h);
38500             if(this.tabs){
38501                 h = this.tabs.syncHeight(h);
38502             }
38503         }
38504         if(this.panelSize){
38505             w = w !== null ? w : this.panelSize.width;
38506             h = h !== null ? h : this.panelSize.height;
38507         }
38508         if(this.activePanel){
38509             var el = this.activePanel.getEl();
38510             w = w !== null ? w : el.getWidth();
38511             h = h !== null ? h : el.getHeight();
38512             this.panelSize = {width: w, height: h};
38513             this.activePanel.setSize(w, h);
38514         }
38515         if(Roo.isIE && this.tabs){
38516             this.tabs.el.repaint();
38517         }
38518     },
38519
38520     /**
38521      * Returns the container element for this region.
38522      * @return {Roo.Element}
38523      */
38524     getEl : function(){
38525         return this.el;
38526     },
38527
38528     /**
38529      * Hides this region.
38530      */
38531     hide : function(){
38532         //if(!this.collapsed){
38533             this.el.dom.style.left = "-2000px";
38534             this.el.hide();
38535         //}else{
38536          //   this.collapsedEl.dom.style.left = "-2000px";
38537          //   this.collapsedEl.hide();
38538        // }
38539         this.visible = false;
38540         this.fireEvent("visibilitychange", this, false);
38541     },
38542
38543     /**
38544      * Shows this region if it was previously hidden.
38545      */
38546     show : function(){
38547         //if(!this.collapsed){
38548             this.el.show();
38549         //}else{
38550         //    this.collapsedEl.show();
38551        // }
38552         this.visible = true;
38553         this.fireEvent("visibilitychange", this, true);
38554     },
38555 /*
38556     closeClicked : function(){
38557         if(this.activePanel){
38558             this.remove(this.activePanel);
38559         }
38560     },
38561
38562     collapseClick : function(e){
38563         if(this.isSlid){
38564            e.stopPropagation();
38565            this.slideIn();
38566         }else{
38567            e.stopPropagation();
38568            this.slideOut();
38569         }
38570     },
38571 */
38572     /**
38573      * Collapses this region.
38574      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38575      */
38576     /*
38577     collapse : function(skipAnim, skipCheck = false){
38578         if(this.collapsed) {
38579             return;
38580         }
38581         
38582         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38583             
38584             this.collapsed = true;
38585             if(this.split){
38586                 this.split.el.hide();
38587             }
38588             if(this.config.animate && skipAnim !== true){
38589                 this.fireEvent("invalidated", this);
38590                 this.animateCollapse();
38591             }else{
38592                 this.el.setLocation(-20000,-20000);
38593                 this.el.hide();
38594                 this.collapsedEl.show();
38595                 this.fireEvent("collapsed", this);
38596                 this.fireEvent("invalidated", this);
38597             }
38598         }
38599         
38600     },
38601 */
38602     animateCollapse : function(){
38603         // overridden
38604     },
38605
38606     /**
38607      * Expands this region if it was previously collapsed.
38608      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38609      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38610      */
38611     /*
38612     expand : function(e, skipAnim){
38613         if(e) {
38614             e.stopPropagation();
38615         }
38616         if(!this.collapsed || this.el.hasActiveFx()) {
38617             return;
38618         }
38619         if(this.isSlid){
38620             this.afterSlideIn();
38621             skipAnim = true;
38622         }
38623         this.collapsed = false;
38624         if(this.config.animate && skipAnim !== true){
38625             this.animateExpand();
38626         }else{
38627             this.el.show();
38628             if(this.split){
38629                 this.split.el.show();
38630             }
38631             this.collapsedEl.setLocation(-2000,-2000);
38632             this.collapsedEl.hide();
38633             this.fireEvent("invalidated", this);
38634             this.fireEvent("expanded", this);
38635         }
38636     },
38637 */
38638     animateExpand : function(){
38639         // overridden
38640     },
38641
38642     initTabs : function()
38643     {
38644         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38645         
38646         var ts = new Roo.bootstrap.panel.Tabs({
38647             el: this.bodyEl.dom,
38648             region : this,
38649             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38650             disableTooltips: this.config.disableTabTips,
38651             toolbar : this.config.toolbar
38652         });
38653         
38654         if(this.config.hideTabs){
38655             ts.stripWrap.setDisplayed(false);
38656         }
38657         this.tabs = ts;
38658         ts.resizeTabs = this.config.resizeTabs === true;
38659         ts.minTabWidth = this.config.minTabWidth || 40;
38660         ts.maxTabWidth = this.config.maxTabWidth || 250;
38661         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38662         ts.monitorResize = false;
38663         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38664         ts.bodyEl.addClass('roo-layout-tabs-body');
38665         this.panels.each(this.initPanelAsTab, this);
38666     },
38667
38668     initPanelAsTab : function(panel){
38669         var ti = this.tabs.addTab(
38670             panel.getEl().id,
38671             panel.getTitle(),
38672             null,
38673             this.config.closeOnTab && panel.isClosable(),
38674             panel.tpl
38675         );
38676         if(panel.tabTip !== undefined){
38677             ti.setTooltip(panel.tabTip);
38678         }
38679         ti.on("activate", function(){
38680               this.setActivePanel(panel);
38681         }, this);
38682         
38683         if(this.config.closeOnTab){
38684             ti.on("beforeclose", function(t, e){
38685                 e.cancel = true;
38686                 this.remove(panel);
38687             }, this);
38688         }
38689         
38690         panel.tabItem = ti;
38691         
38692         return ti;
38693     },
38694
38695     updatePanelTitle : function(panel, title)
38696     {
38697         if(this.activePanel == panel){
38698             this.updateTitle(title);
38699         }
38700         if(this.tabs){
38701             var ti = this.tabs.getTab(panel.getEl().id);
38702             ti.setText(title);
38703             if(panel.tabTip !== undefined){
38704                 ti.setTooltip(panel.tabTip);
38705             }
38706         }
38707     },
38708
38709     updateTitle : function(title){
38710         if(this.titleTextEl && !this.config.title){
38711             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38712         }
38713     },
38714
38715     setActivePanel : function(panel)
38716     {
38717         panel = this.getPanel(panel);
38718         if(this.activePanel && this.activePanel != panel){
38719             if(this.activePanel.setActiveState(false) === false){
38720                 return;
38721             }
38722         }
38723         this.activePanel = panel;
38724         panel.setActiveState(true);
38725         if(this.panelSize){
38726             panel.setSize(this.panelSize.width, this.panelSize.height);
38727         }
38728         if(this.closeBtn){
38729             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38730         }
38731         this.updateTitle(panel.getTitle());
38732         if(this.tabs){
38733             this.fireEvent("invalidated", this);
38734         }
38735         this.fireEvent("panelactivated", this, panel);
38736     },
38737
38738     /**
38739      * Shows the specified panel.
38740      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38741      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38742      */
38743     showPanel : function(panel)
38744     {
38745         panel = this.getPanel(panel);
38746         if(panel){
38747             if(this.tabs){
38748                 var tab = this.tabs.getTab(panel.getEl().id);
38749                 if(tab.isHidden()){
38750                     this.tabs.unhideTab(tab.id);
38751                 }
38752                 tab.activate();
38753             }else{
38754                 this.setActivePanel(panel);
38755             }
38756         }
38757         return panel;
38758     },
38759
38760     /**
38761      * Get the active panel for this region.
38762      * @return {Roo.ContentPanel} The active panel or null
38763      */
38764     getActivePanel : function(){
38765         return this.activePanel;
38766     },
38767
38768     validateVisibility : function(){
38769         if(this.panels.getCount() < 1){
38770             this.updateTitle("&#160;");
38771             this.closeBtn.hide();
38772             this.hide();
38773         }else{
38774             if(!this.isVisible()){
38775                 this.show();
38776             }
38777         }
38778     },
38779
38780     /**
38781      * Adds the passed ContentPanel(s) to this region.
38782      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38783      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38784      */
38785     add : function(panel)
38786     {
38787         if(arguments.length > 1){
38788             for(var i = 0, len = arguments.length; i < len; i++) {
38789                 this.add(arguments[i]);
38790             }
38791             return null;
38792         }
38793         
38794         // if we have not been rendered yet, then we can not really do much of this..
38795         if (!this.bodyEl) {
38796             this.unrendered_panels.push(panel);
38797             return panel;
38798         }
38799         
38800         
38801         
38802         
38803         if(this.hasPanel(panel)){
38804             this.showPanel(panel);
38805             return panel;
38806         }
38807         panel.setRegion(this);
38808         this.panels.add(panel);
38809        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38810             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38811             // and hide them... ???
38812             this.bodyEl.dom.appendChild(panel.getEl().dom);
38813             if(panel.background !== true){
38814                 this.setActivePanel(panel);
38815             }
38816             this.fireEvent("paneladded", this, panel);
38817             return panel;
38818         }
38819         */
38820         if(!this.tabs){
38821             this.initTabs();
38822         }else{
38823             this.initPanelAsTab(panel);
38824         }
38825         
38826         
38827         if(panel.background !== true){
38828             this.tabs.activate(panel.getEl().id);
38829         }
38830         this.fireEvent("paneladded", this, panel);
38831         return panel;
38832     },
38833
38834     /**
38835      * Hides the tab for the specified panel.
38836      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38837      */
38838     hidePanel : function(panel){
38839         if(this.tabs && (panel = this.getPanel(panel))){
38840             this.tabs.hideTab(panel.getEl().id);
38841         }
38842     },
38843
38844     /**
38845      * Unhides the tab for a previously hidden panel.
38846      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38847      */
38848     unhidePanel : function(panel){
38849         if(this.tabs && (panel = this.getPanel(panel))){
38850             this.tabs.unhideTab(panel.getEl().id);
38851         }
38852     },
38853
38854     clearPanels : function(){
38855         while(this.panels.getCount() > 0){
38856              this.remove(this.panels.first());
38857         }
38858     },
38859
38860     /**
38861      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38862      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38863      * @param {Boolean} preservePanel Overrides the config preservePanel option
38864      * @return {Roo.ContentPanel} The panel that was removed
38865      */
38866     remove : function(panel, preservePanel)
38867     {
38868         panel = this.getPanel(panel);
38869         if(!panel){
38870             return null;
38871         }
38872         var e = {};
38873         this.fireEvent("beforeremove", this, panel, e);
38874         if(e.cancel === true){
38875             return null;
38876         }
38877         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38878         var panelId = panel.getId();
38879         this.panels.removeKey(panelId);
38880         if(preservePanel){
38881             document.body.appendChild(panel.getEl().dom);
38882         }
38883         if(this.tabs){
38884             this.tabs.removeTab(panel.getEl().id);
38885         }else if (!preservePanel){
38886             this.bodyEl.dom.removeChild(panel.getEl().dom);
38887         }
38888         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38889             var p = this.panels.first();
38890             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38891             tempEl.appendChild(p.getEl().dom);
38892             this.bodyEl.update("");
38893             this.bodyEl.dom.appendChild(p.getEl().dom);
38894             tempEl = null;
38895             this.updateTitle(p.getTitle());
38896             this.tabs = null;
38897             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38898             this.setActivePanel(p);
38899         }
38900         panel.setRegion(null);
38901         if(this.activePanel == panel){
38902             this.activePanel = null;
38903         }
38904         if(this.config.autoDestroy !== false && preservePanel !== true){
38905             try{panel.destroy();}catch(e){}
38906         }
38907         this.fireEvent("panelremoved", this, panel);
38908         return panel;
38909     },
38910
38911     /**
38912      * Returns the TabPanel component used by this region
38913      * @return {Roo.TabPanel}
38914      */
38915     getTabs : function(){
38916         return this.tabs;
38917     },
38918
38919     createTool : function(parentEl, className){
38920         var btn = Roo.DomHelper.append(parentEl, {
38921             tag: "div",
38922             cls: "x-layout-tools-button",
38923             children: [ {
38924                 tag: "div",
38925                 cls: "roo-layout-tools-button-inner " + className,
38926                 html: "&#160;"
38927             }]
38928         }, true);
38929         btn.addClassOnOver("roo-layout-tools-button-over");
38930         return btn;
38931     }
38932 });/*
38933  * Based on:
38934  * Ext JS Library 1.1.1
38935  * Copyright(c) 2006-2007, Ext JS, LLC.
38936  *
38937  * Originally Released Under LGPL - original licence link has changed is not relivant.
38938  *
38939  * Fork - LGPL
38940  * <script type="text/javascript">
38941  */
38942  
38943
38944
38945 /**
38946  * @class Roo.SplitLayoutRegion
38947  * @extends Roo.LayoutRegion
38948  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38949  */
38950 Roo.bootstrap.layout.Split = function(config){
38951     this.cursor = config.cursor;
38952     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38953 };
38954
38955 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38956 {
38957     splitTip : "Drag to resize.",
38958     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38959     useSplitTips : false,
38960
38961     applyConfig : function(config){
38962         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38963     },
38964     
38965     onRender : function(ctr,pos) {
38966         
38967         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38968         if(!this.config.split){
38969             return;
38970         }
38971         if(!this.split){
38972             
38973             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38974                             tag: "div",
38975                             id: this.el.id + "-split",
38976                             cls: "roo-layout-split roo-layout-split-"+this.position,
38977                             html: "&#160;"
38978             });
38979             /** The SplitBar for this region 
38980             * @type Roo.SplitBar */
38981             // does not exist yet...
38982             Roo.log([this.position, this.orientation]);
38983             
38984             this.split = new Roo.bootstrap.SplitBar({
38985                 dragElement : splitEl,
38986                 resizingElement: this.el,
38987                 orientation : this.orientation
38988             });
38989             
38990             this.split.on("moved", this.onSplitMove, this);
38991             this.split.useShim = this.config.useShim === true;
38992             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38993             if(this.useSplitTips){
38994                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38995             }
38996             //if(config.collapsible){
38997             //    this.split.el.on("dblclick", this.collapse,  this);
38998             //}
38999         }
39000         if(typeof this.config.minSize != "undefined"){
39001             this.split.minSize = this.config.minSize;
39002         }
39003         if(typeof this.config.maxSize != "undefined"){
39004             this.split.maxSize = this.config.maxSize;
39005         }
39006         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39007             this.hideSplitter();
39008         }
39009         
39010     },
39011
39012     getHMaxSize : function(){
39013          var cmax = this.config.maxSize || 10000;
39014          var center = this.mgr.getRegion("center");
39015          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39016     },
39017
39018     getVMaxSize : function(){
39019          var cmax = this.config.maxSize || 10000;
39020          var center = this.mgr.getRegion("center");
39021          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39022     },
39023
39024     onSplitMove : function(split, newSize){
39025         this.fireEvent("resized", this, newSize);
39026     },
39027     
39028     /** 
39029      * Returns the {@link Roo.SplitBar} for this region.
39030      * @return {Roo.SplitBar}
39031      */
39032     getSplitBar : function(){
39033         return this.split;
39034     },
39035     
39036     hide : function(){
39037         this.hideSplitter();
39038         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39039     },
39040
39041     hideSplitter : function(){
39042         if(this.split){
39043             this.split.el.setLocation(-2000,-2000);
39044             this.split.el.hide();
39045         }
39046     },
39047
39048     show : function(){
39049         if(this.split){
39050             this.split.el.show();
39051         }
39052         Roo.bootstrap.layout.Split.superclass.show.call(this);
39053     },
39054     
39055     beforeSlide: function(){
39056         if(Roo.isGecko){// firefox overflow auto bug workaround
39057             this.bodyEl.clip();
39058             if(this.tabs) {
39059                 this.tabs.bodyEl.clip();
39060             }
39061             if(this.activePanel){
39062                 this.activePanel.getEl().clip();
39063                 
39064                 if(this.activePanel.beforeSlide){
39065                     this.activePanel.beforeSlide();
39066                 }
39067             }
39068         }
39069     },
39070     
39071     afterSlide : function(){
39072         if(Roo.isGecko){// firefox overflow auto bug workaround
39073             this.bodyEl.unclip();
39074             if(this.tabs) {
39075                 this.tabs.bodyEl.unclip();
39076             }
39077             if(this.activePanel){
39078                 this.activePanel.getEl().unclip();
39079                 if(this.activePanel.afterSlide){
39080                     this.activePanel.afterSlide();
39081                 }
39082             }
39083         }
39084     },
39085
39086     initAutoHide : function(){
39087         if(this.autoHide !== false){
39088             if(!this.autoHideHd){
39089                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39090                 this.autoHideHd = {
39091                     "mouseout": function(e){
39092                         if(!e.within(this.el, true)){
39093                             st.delay(500);
39094                         }
39095                     },
39096                     "mouseover" : function(e){
39097                         st.cancel();
39098                     },
39099                     scope : this
39100                 };
39101             }
39102             this.el.on(this.autoHideHd);
39103         }
39104     },
39105
39106     clearAutoHide : function(){
39107         if(this.autoHide !== false){
39108             this.el.un("mouseout", this.autoHideHd.mouseout);
39109             this.el.un("mouseover", this.autoHideHd.mouseover);
39110         }
39111     },
39112
39113     clearMonitor : function(){
39114         Roo.get(document).un("click", this.slideInIf, this);
39115     },
39116
39117     // these names are backwards but not changed for compat
39118     slideOut : function(){
39119         if(this.isSlid || this.el.hasActiveFx()){
39120             return;
39121         }
39122         this.isSlid = true;
39123         if(this.collapseBtn){
39124             this.collapseBtn.hide();
39125         }
39126         this.closeBtnState = this.closeBtn.getStyle('display');
39127         this.closeBtn.hide();
39128         if(this.stickBtn){
39129             this.stickBtn.show();
39130         }
39131         this.el.show();
39132         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39133         this.beforeSlide();
39134         this.el.setStyle("z-index", 10001);
39135         this.el.slideIn(this.getSlideAnchor(), {
39136             callback: function(){
39137                 this.afterSlide();
39138                 this.initAutoHide();
39139                 Roo.get(document).on("click", this.slideInIf, this);
39140                 this.fireEvent("slideshow", this);
39141             },
39142             scope: this,
39143             block: true
39144         });
39145     },
39146
39147     afterSlideIn : function(){
39148         this.clearAutoHide();
39149         this.isSlid = false;
39150         this.clearMonitor();
39151         this.el.setStyle("z-index", "");
39152         if(this.collapseBtn){
39153             this.collapseBtn.show();
39154         }
39155         this.closeBtn.setStyle('display', this.closeBtnState);
39156         if(this.stickBtn){
39157             this.stickBtn.hide();
39158         }
39159         this.fireEvent("slidehide", this);
39160     },
39161
39162     slideIn : function(cb){
39163         if(!this.isSlid || this.el.hasActiveFx()){
39164             Roo.callback(cb);
39165             return;
39166         }
39167         this.isSlid = false;
39168         this.beforeSlide();
39169         this.el.slideOut(this.getSlideAnchor(), {
39170             callback: function(){
39171                 this.el.setLeftTop(-10000, -10000);
39172                 this.afterSlide();
39173                 this.afterSlideIn();
39174                 Roo.callback(cb);
39175             },
39176             scope: this,
39177             block: true
39178         });
39179     },
39180     
39181     slideInIf : function(e){
39182         if(!e.within(this.el)){
39183             this.slideIn();
39184         }
39185     },
39186
39187     animateCollapse : function(){
39188         this.beforeSlide();
39189         this.el.setStyle("z-index", 20000);
39190         var anchor = this.getSlideAnchor();
39191         this.el.slideOut(anchor, {
39192             callback : function(){
39193                 this.el.setStyle("z-index", "");
39194                 this.collapsedEl.slideIn(anchor, {duration:.3});
39195                 this.afterSlide();
39196                 this.el.setLocation(-10000,-10000);
39197                 this.el.hide();
39198                 this.fireEvent("collapsed", this);
39199             },
39200             scope: this,
39201             block: true
39202         });
39203     },
39204
39205     animateExpand : function(){
39206         this.beforeSlide();
39207         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39208         this.el.setStyle("z-index", 20000);
39209         this.collapsedEl.hide({
39210             duration:.1
39211         });
39212         this.el.slideIn(this.getSlideAnchor(), {
39213             callback : function(){
39214                 this.el.setStyle("z-index", "");
39215                 this.afterSlide();
39216                 if(this.split){
39217                     this.split.el.show();
39218                 }
39219                 this.fireEvent("invalidated", this);
39220                 this.fireEvent("expanded", this);
39221             },
39222             scope: this,
39223             block: true
39224         });
39225     },
39226
39227     anchors : {
39228         "west" : "left",
39229         "east" : "right",
39230         "north" : "top",
39231         "south" : "bottom"
39232     },
39233
39234     sanchors : {
39235         "west" : "l",
39236         "east" : "r",
39237         "north" : "t",
39238         "south" : "b"
39239     },
39240
39241     canchors : {
39242         "west" : "tl-tr",
39243         "east" : "tr-tl",
39244         "north" : "tl-bl",
39245         "south" : "bl-tl"
39246     },
39247
39248     getAnchor : function(){
39249         return this.anchors[this.position];
39250     },
39251
39252     getCollapseAnchor : function(){
39253         return this.canchors[this.position];
39254     },
39255
39256     getSlideAnchor : function(){
39257         return this.sanchors[this.position];
39258     },
39259
39260     getAlignAdj : function(){
39261         var cm = this.cmargins;
39262         switch(this.position){
39263             case "west":
39264                 return [0, 0];
39265             break;
39266             case "east":
39267                 return [0, 0];
39268             break;
39269             case "north":
39270                 return [0, 0];
39271             break;
39272             case "south":
39273                 return [0, 0];
39274             break;
39275         }
39276     },
39277
39278     getExpandAdj : function(){
39279         var c = this.collapsedEl, cm = this.cmargins;
39280         switch(this.position){
39281             case "west":
39282                 return [-(cm.right+c.getWidth()+cm.left), 0];
39283             break;
39284             case "east":
39285                 return [cm.right+c.getWidth()+cm.left, 0];
39286             break;
39287             case "north":
39288                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39289             break;
39290             case "south":
39291                 return [0, cm.top+cm.bottom+c.getHeight()];
39292             break;
39293         }
39294     }
39295 });/*
39296  * Based on:
39297  * Ext JS Library 1.1.1
39298  * Copyright(c) 2006-2007, Ext JS, LLC.
39299  *
39300  * Originally Released Under LGPL - original licence link has changed is not relivant.
39301  *
39302  * Fork - LGPL
39303  * <script type="text/javascript">
39304  */
39305 /*
39306  * These classes are private internal classes
39307  */
39308 Roo.bootstrap.layout.Center = function(config){
39309     config.region = "center";
39310     Roo.bootstrap.layout.Region.call(this, config);
39311     this.visible = true;
39312     this.minWidth = config.minWidth || 20;
39313     this.minHeight = config.minHeight || 20;
39314 };
39315
39316 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39317     hide : function(){
39318         // center panel can't be hidden
39319     },
39320     
39321     show : function(){
39322         // center panel can't be hidden
39323     },
39324     
39325     getMinWidth: function(){
39326         return this.minWidth;
39327     },
39328     
39329     getMinHeight: function(){
39330         return this.minHeight;
39331     }
39332 });
39333
39334
39335
39336
39337  
39338
39339
39340
39341
39342
39343
39344 Roo.bootstrap.layout.North = function(config)
39345 {
39346     config.region = 'north';
39347     config.cursor = 'n-resize';
39348     
39349     Roo.bootstrap.layout.Split.call(this, config);
39350     
39351     
39352     if(this.split){
39353         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39354         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39355         this.split.el.addClass("roo-layout-split-v");
39356     }
39357     //var size = config.initialSize || config.height;
39358     //if(this.el && typeof size != "undefined"){
39359     //    this.el.setHeight(size);
39360     //}
39361 };
39362 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39363 {
39364     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39365      
39366      
39367     onRender : function(ctr, pos)
39368     {
39369         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39370         var size = this.config.initialSize || this.config.height;
39371         if(this.el && typeof size != "undefined"){
39372             this.el.setHeight(size);
39373         }
39374     
39375     },
39376     
39377     getBox : function(){
39378         if(this.collapsed){
39379             return this.collapsedEl.getBox();
39380         }
39381         var box = this.el.getBox();
39382         if(this.split){
39383             box.height += this.split.el.getHeight();
39384         }
39385         return box;
39386     },
39387     
39388     updateBox : function(box){
39389         if(this.split && !this.collapsed){
39390             box.height -= this.split.el.getHeight();
39391             this.split.el.setLeft(box.x);
39392             this.split.el.setTop(box.y+box.height);
39393             this.split.el.setWidth(box.width);
39394         }
39395         if(this.collapsed){
39396             this.updateBody(box.width, null);
39397         }
39398         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39399     }
39400 });
39401
39402
39403
39404
39405
39406 Roo.bootstrap.layout.South = function(config){
39407     config.region = 'south';
39408     config.cursor = 's-resize';
39409     Roo.bootstrap.layout.Split.call(this, config);
39410     if(this.split){
39411         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39412         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39413         this.split.el.addClass("roo-layout-split-v");
39414     }
39415     
39416 };
39417
39418 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39419     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39420     
39421     onRender : function(ctr, pos)
39422     {
39423         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39424         var size = this.config.initialSize || this.config.height;
39425         if(this.el && typeof size != "undefined"){
39426             this.el.setHeight(size);
39427         }
39428     
39429     },
39430     
39431     getBox : function(){
39432         if(this.collapsed){
39433             return this.collapsedEl.getBox();
39434         }
39435         var box = this.el.getBox();
39436         if(this.split){
39437             var sh = this.split.el.getHeight();
39438             box.height += sh;
39439             box.y -= sh;
39440         }
39441         return box;
39442     },
39443     
39444     updateBox : function(box){
39445         if(this.split && !this.collapsed){
39446             var sh = this.split.el.getHeight();
39447             box.height -= sh;
39448             box.y += sh;
39449             this.split.el.setLeft(box.x);
39450             this.split.el.setTop(box.y-sh);
39451             this.split.el.setWidth(box.width);
39452         }
39453         if(this.collapsed){
39454             this.updateBody(box.width, null);
39455         }
39456         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39457     }
39458 });
39459
39460 Roo.bootstrap.layout.East = function(config){
39461     config.region = "east";
39462     config.cursor = "e-resize";
39463     Roo.bootstrap.layout.Split.call(this, config);
39464     if(this.split){
39465         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39466         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39467         this.split.el.addClass("roo-layout-split-h");
39468     }
39469     
39470 };
39471 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39472     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39473     
39474     onRender : function(ctr, pos)
39475     {
39476         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39477         var size = this.config.initialSize || this.config.width;
39478         if(this.el && typeof size != "undefined"){
39479             this.el.setWidth(size);
39480         }
39481     
39482     },
39483     
39484     getBox : function(){
39485         if(this.collapsed){
39486             return this.collapsedEl.getBox();
39487         }
39488         var box = this.el.getBox();
39489         if(this.split){
39490             var sw = this.split.el.getWidth();
39491             box.width += sw;
39492             box.x -= sw;
39493         }
39494         return box;
39495     },
39496
39497     updateBox : function(box){
39498         if(this.split && !this.collapsed){
39499             var sw = this.split.el.getWidth();
39500             box.width -= sw;
39501             this.split.el.setLeft(box.x);
39502             this.split.el.setTop(box.y);
39503             this.split.el.setHeight(box.height);
39504             box.x += sw;
39505         }
39506         if(this.collapsed){
39507             this.updateBody(null, box.height);
39508         }
39509         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39510     }
39511 });
39512
39513 Roo.bootstrap.layout.West = function(config){
39514     config.region = "west";
39515     config.cursor = "w-resize";
39516     
39517     Roo.bootstrap.layout.Split.call(this, config);
39518     if(this.split){
39519         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39520         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39521         this.split.el.addClass("roo-layout-split-h");
39522     }
39523     
39524 };
39525 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39526     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39527     
39528     onRender: function(ctr, pos)
39529     {
39530         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39531         var size = this.config.initialSize || this.config.width;
39532         if(typeof size != "undefined"){
39533             this.el.setWidth(size);
39534         }
39535     },
39536     
39537     getBox : function(){
39538         if(this.collapsed){
39539             return this.collapsedEl.getBox();
39540         }
39541         var box = this.el.getBox();
39542         if (box.width == 0) {
39543             box.width = this.config.width; // kludge?
39544         }
39545         if(this.split){
39546             box.width += this.split.el.getWidth();
39547         }
39548         return box;
39549     },
39550     
39551     updateBox : function(box){
39552         if(this.split && !this.collapsed){
39553             var sw = this.split.el.getWidth();
39554             box.width -= sw;
39555             this.split.el.setLeft(box.x+box.width);
39556             this.split.el.setTop(box.y);
39557             this.split.el.setHeight(box.height);
39558         }
39559         if(this.collapsed){
39560             this.updateBody(null, box.height);
39561         }
39562         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39563     }
39564 });Roo.namespace("Roo.bootstrap.panel");/*
39565  * Based on:
39566  * Ext JS Library 1.1.1
39567  * Copyright(c) 2006-2007, Ext JS, LLC.
39568  *
39569  * Originally Released Under LGPL - original licence link has changed is not relivant.
39570  *
39571  * Fork - LGPL
39572  * <script type="text/javascript">
39573  */
39574 /**
39575  * @class Roo.ContentPanel
39576  * @extends Roo.util.Observable
39577  * A basic ContentPanel element.
39578  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39579  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39580  * @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
39581  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39582  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39583  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39584  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39585  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39586  * @cfg {String} title          The title for this panel
39587  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39588  * @cfg {String} url            Calls {@link #setUrl} with this value
39589  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39590  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39591  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39592  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39593  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39594  * @cfg {Boolean} badges render the badges
39595  * @cfg {String} cls  extra classes to use  
39596  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39597
39598  * @constructor
39599  * Create a new ContentPanel.
39600  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39601  * @param {String/Object} config A string to set only the title or a config object
39602  * @param {String} content (optional) Set the HTML content for this panel
39603  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39604  */
39605 Roo.bootstrap.panel.Content = function( config){
39606     
39607     this.tpl = config.tpl || false;
39608     
39609     var el = config.el;
39610     var content = config.content;
39611
39612     if(config.autoCreate){ // xtype is available if this is called from factory
39613         el = Roo.id();
39614     }
39615     this.el = Roo.get(el);
39616     if(!this.el && config && config.autoCreate){
39617         if(typeof config.autoCreate == "object"){
39618             if(!config.autoCreate.id){
39619                 config.autoCreate.id = config.id||el;
39620             }
39621             this.el = Roo.DomHelper.append(document.body,
39622                         config.autoCreate, true);
39623         }else{
39624             var elcfg =  {
39625                 tag: "div",
39626                 cls: (config.cls || '') +
39627                     (config.background ? ' bg-' + config.background : '') +
39628                     " roo-layout-inactive-content",
39629                 id: config.id||el
39630             };
39631             if (config.iframe) {
39632                 elcfg.cn = [
39633                     {
39634                         tag : 'iframe',
39635                         style : 'border: 0px',
39636                         src : 'about:blank'
39637                     }
39638                 ];
39639             }
39640               
39641             if (config.html) {
39642                 elcfg.html = config.html;
39643                 
39644             }
39645                         
39646             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39647             if (config.iframe) {
39648                 this.iframeEl = this.el.select('iframe',true).first();
39649             }
39650             
39651         }
39652     } 
39653     this.closable = false;
39654     this.loaded = false;
39655     this.active = false;
39656    
39657       
39658     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39659         
39660         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39661         
39662         this.wrapEl = this.el; //this.el.wrap();
39663         var ti = [];
39664         if (config.toolbar.items) {
39665             ti = config.toolbar.items ;
39666             delete config.toolbar.items ;
39667         }
39668         
39669         var nitems = [];
39670         this.toolbar.render(this.wrapEl, 'before');
39671         for(var i =0;i < ti.length;i++) {
39672           //  Roo.log(['add child', items[i]]);
39673             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39674         }
39675         this.toolbar.items = nitems;
39676         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39677         delete config.toolbar;
39678         
39679     }
39680     /*
39681     // xtype created footer. - not sure if will work as we normally have to render first..
39682     if (this.footer && !this.footer.el && this.footer.xtype) {
39683         if (!this.wrapEl) {
39684             this.wrapEl = this.el.wrap();
39685         }
39686     
39687         this.footer.container = this.wrapEl.createChild();
39688          
39689         this.footer = Roo.factory(this.footer, Roo);
39690         
39691     }
39692     */
39693     
39694      if(typeof config == "string"){
39695         this.title = config;
39696     }else{
39697         Roo.apply(this, config);
39698     }
39699     
39700     if(this.resizeEl){
39701         this.resizeEl = Roo.get(this.resizeEl, true);
39702     }else{
39703         this.resizeEl = this.el;
39704     }
39705     // handle view.xtype
39706     
39707  
39708     
39709     
39710     this.addEvents({
39711         /**
39712          * @event activate
39713          * Fires when this panel is activated. 
39714          * @param {Roo.ContentPanel} this
39715          */
39716         "activate" : true,
39717         /**
39718          * @event deactivate
39719          * Fires when this panel is activated. 
39720          * @param {Roo.ContentPanel} this
39721          */
39722         "deactivate" : true,
39723
39724         /**
39725          * @event resize
39726          * Fires when this panel is resized if fitToFrame is true.
39727          * @param {Roo.ContentPanel} this
39728          * @param {Number} width The width after any component adjustments
39729          * @param {Number} height The height after any component adjustments
39730          */
39731         "resize" : true,
39732         
39733          /**
39734          * @event render
39735          * Fires when this tab is created
39736          * @param {Roo.ContentPanel} this
39737          */
39738         "render" : true
39739         
39740         
39741         
39742     });
39743     
39744
39745     
39746     
39747     if(this.autoScroll && !this.iframe){
39748         this.resizeEl.setStyle("overflow", "auto");
39749     } else {
39750         // fix randome scrolling
39751         //this.el.on('scroll', function() {
39752         //    Roo.log('fix random scolling');
39753         //    this.scrollTo('top',0); 
39754         //});
39755     }
39756     content = content || this.content;
39757     if(content){
39758         this.setContent(content);
39759     }
39760     if(config && config.url){
39761         this.setUrl(this.url, this.params, this.loadOnce);
39762     }
39763     
39764     
39765     
39766     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39767     
39768     if (this.view && typeof(this.view.xtype) != 'undefined') {
39769         this.view.el = this.el.appendChild(document.createElement("div"));
39770         this.view = Roo.factory(this.view); 
39771         this.view.render  &&  this.view.render(false, '');  
39772     }
39773     
39774     
39775     this.fireEvent('render', this);
39776 };
39777
39778 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39779     
39780     cls : '',
39781     background : '',
39782     
39783     tabTip : '',
39784     
39785     iframe : false,
39786     iframeEl : false,
39787     
39788     setRegion : function(region){
39789         this.region = region;
39790         this.setActiveClass(region && !this.background);
39791     },
39792     
39793     
39794     setActiveClass: function(state)
39795     {
39796         if(state){
39797            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39798            this.el.setStyle('position','relative');
39799         }else{
39800            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39801            this.el.setStyle('position', 'absolute');
39802         } 
39803     },
39804     
39805     /**
39806      * Returns the toolbar for this Panel if one was configured. 
39807      * @return {Roo.Toolbar} 
39808      */
39809     getToolbar : function(){
39810         return this.toolbar;
39811     },
39812     
39813     setActiveState : function(active)
39814     {
39815         this.active = active;
39816         this.setActiveClass(active);
39817         if(!active){
39818             if(this.fireEvent("deactivate", this) === false){
39819                 return false;
39820             }
39821             return true;
39822         }
39823         this.fireEvent("activate", this);
39824         return true;
39825     },
39826     /**
39827      * Updates this panel's element (not for iframe)
39828      * @param {String} content The new content
39829      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39830     */
39831     setContent : function(content, loadScripts){
39832         if (this.iframe) {
39833             return;
39834         }
39835         
39836         this.el.update(content, loadScripts);
39837     },
39838
39839     ignoreResize : function(w, h){
39840         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39841             return true;
39842         }else{
39843             this.lastSize = {width: w, height: h};
39844             return false;
39845         }
39846     },
39847     /**
39848      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39849      * @return {Roo.UpdateManager} The UpdateManager
39850      */
39851     getUpdateManager : function(){
39852         if (this.iframe) {
39853             return false;
39854         }
39855         return this.el.getUpdateManager();
39856     },
39857      /**
39858      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39859      * Does not work with IFRAME contents
39860      * @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:
39861 <pre><code>
39862 panel.load({
39863     url: "your-url.php",
39864     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39865     callback: yourFunction,
39866     scope: yourObject, //(optional scope)
39867     discardUrl: false,
39868     nocache: false,
39869     text: "Loading...",
39870     timeout: 30,
39871     scripts: false
39872 });
39873 </code></pre>
39874      
39875      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39876      * 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.
39877      * @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}
39878      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39879      * @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.
39880      * @return {Roo.ContentPanel} this
39881      */
39882     load : function(){
39883         
39884         if (this.iframe) {
39885             return this;
39886         }
39887         
39888         var um = this.el.getUpdateManager();
39889         um.update.apply(um, arguments);
39890         return this;
39891     },
39892
39893
39894     /**
39895      * 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.
39896      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39897      * @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)
39898      * @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)
39899      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39900      */
39901     setUrl : function(url, params, loadOnce){
39902         if (this.iframe) {
39903             this.iframeEl.dom.src = url;
39904             return false;
39905         }
39906         
39907         if(this.refreshDelegate){
39908             this.removeListener("activate", this.refreshDelegate);
39909         }
39910         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39911         this.on("activate", this.refreshDelegate);
39912         return this.el.getUpdateManager();
39913     },
39914     
39915     _handleRefresh : function(url, params, loadOnce){
39916         if(!loadOnce || !this.loaded){
39917             var updater = this.el.getUpdateManager();
39918             updater.update(url, params, this._setLoaded.createDelegate(this));
39919         }
39920     },
39921     
39922     _setLoaded : function(){
39923         this.loaded = true;
39924     }, 
39925     
39926     /**
39927      * Returns this panel's id
39928      * @return {String} 
39929      */
39930     getId : function(){
39931         return this.el.id;
39932     },
39933     
39934     /** 
39935      * Returns this panel's element - used by regiosn to add.
39936      * @return {Roo.Element} 
39937      */
39938     getEl : function(){
39939         return this.wrapEl || this.el;
39940     },
39941     
39942    
39943     
39944     adjustForComponents : function(width, height)
39945     {
39946         //Roo.log('adjustForComponents ');
39947         if(this.resizeEl != this.el){
39948             width -= this.el.getFrameWidth('lr');
39949             height -= this.el.getFrameWidth('tb');
39950         }
39951         if(this.toolbar){
39952             var te = this.toolbar.getEl();
39953             te.setWidth(width);
39954             height -= te.getHeight();
39955         }
39956         if(this.footer){
39957             var te = this.footer.getEl();
39958             te.setWidth(width);
39959             height -= te.getHeight();
39960         }
39961         
39962         
39963         if(this.adjustments){
39964             width += this.adjustments[0];
39965             height += this.adjustments[1];
39966         }
39967         return {"width": width, "height": height};
39968     },
39969     
39970     setSize : function(width, height){
39971         if(this.fitToFrame && !this.ignoreResize(width, height)){
39972             if(this.fitContainer && this.resizeEl != this.el){
39973                 this.el.setSize(width, height);
39974             }
39975             var size = this.adjustForComponents(width, height);
39976             if (this.iframe) {
39977                 this.iframeEl.setSize(width,height);
39978             }
39979             
39980             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39981             this.fireEvent('resize', this, size.width, size.height);
39982             
39983             
39984         }
39985     },
39986     
39987     /**
39988      * Returns this panel's title
39989      * @return {String} 
39990      */
39991     getTitle : function(){
39992         
39993         if (typeof(this.title) != 'object') {
39994             return this.title;
39995         }
39996         
39997         var t = '';
39998         for (var k in this.title) {
39999             if (!this.title.hasOwnProperty(k)) {
40000                 continue;
40001             }
40002             
40003             if (k.indexOf('-') >= 0) {
40004                 var s = k.split('-');
40005                 for (var i = 0; i<s.length; i++) {
40006                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40007                 }
40008             } else {
40009                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40010             }
40011         }
40012         return t;
40013     },
40014     
40015     /**
40016      * Set this panel's title
40017      * @param {String} title
40018      */
40019     setTitle : function(title){
40020         this.title = title;
40021         if(this.region){
40022             this.region.updatePanelTitle(this, title);
40023         }
40024     },
40025     
40026     /**
40027      * Returns true is this panel was configured to be closable
40028      * @return {Boolean} 
40029      */
40030     isClosable : function(){
40031         return this.closable;
40032     },
40033     
40034     beforeSlide : function(){
40035         this.el.clip();
40036         this.resizeEl.clip();
40037     },
40038     
40039     afterSlide : function(){
40040         this.el.unclip();
40041         this.resizeEl.unclip();
40042     },
40043     
40044     /**
40045      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40046      *   Will fail silently if the {@link #setUrl} method has not been called.
40047      *   This does not activate the panel, just updates its content.
40048      */
40049     refresh : function(){
40050         if(this.refreshDelegate){
40051            this.loaded = false;
40052            this.refreshDelegate();
40053         }
40054     },
40055     
40056     /**
40057      * Destroys this panel
40058      */
40059     destroy : function(){
40060         this.el.removeAllListeners();
40061         var tempEl = document.createElement("span");
40062         tempEl.appendChild(this.el.dom);
40063         tempEl.innerHTML = "";
40064         this.el.remove();
40065         this.el = null;
40066     },
40067     
40068     /**
40069      * form - if the content panel contains a form - this is a reference to it.
40070      * @type {Roo.form.Form}
40071      */
40072     form : false,
40073     /**
40074      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40075      *    This contains a reference to it.
40076      * @type {Roo.View}
40077      */
40078     view : false,
40079     
40080       /**
40081      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40082      * <pre><code>
40083
40084 layout.addxtype({
40085        xtype : 'Form',
40086        items: [ .... ]
40087    }
40088 );
40089
40090 </code></pre>
40091      * @param {Object} cfg Xtype definition of item to add.
40092      */
40093     
40094     
40095     getChildContainer: function () {
40096         return this.getEl();
40097     }
40098     
40099     
40100     /*
40101         var  ret = new Roo.factory(cfg);
40102         return ret;
40103         
40104         
40105         // add form..
40106         if (cfg.xtype.match(/^Form$/)) {
40107             
40108             var el;
40109             //if (this.footer) {
40110             //    el = this.footer.container.insertSibling(false, 'before');
40111             //} else {
40112                 el = this.el.createChild();
40113             //}
40114
40115             this.form = new  Roo.form.Form(cfg);
40116             
40117             
40118             if ( this.form.allItems.length) {
40119                 this.form.render(el.dom);
40120             }
40121             return this.form;
40122         }
40123         // should only have one of theses..
40124         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40125             // views.. should not be just added - used named prop 'view''
40126             
40127             cfg.el = this.el.appendChild(document.createElement("div"));
40128             // factory?
40129             
40130             var ret = new Roo.factory(cfg);
40131              
40132              ret.render && ret.render(false, ''); // render blank..
40133             this.view = ret;
40134             return ret;
40135         }
40136         return false;
40137     }
40138     \*/
40139 });
40140  
40141 /**
40142  * @class Roo.bootstrap.panel.Grid
40143  * @extends Roo.bootstrap.panel.Content
40144  * @constructor
40145  * Create a new GridPanel.
40146  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40147  * @param {Object} config A the config object
40148   
40149  */
40150
40151
40152
40153 Roo.bootstrap.panel.Grid = function(config)
40154 {
40155     
40156       
40157     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40158         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40159
40160     config.el = this.wrapper;
40161     //this.el = this.wrapper;
40162     
40163       if (config.container) {
40164         // ctor'ed from a Border/panel.grid
40165         
40166         
40167         this.wrapper.setStyle("overflow", "hidden");
40168         this.wrapper.addClass('roo-grid-container');
40169
40170     }
40171     
40172     
40173     if(config.toolbar){
40174         var tool_el = this.wrapper.createChild();    
40175         this.toolbar = Roo.factory(config.toolbar);
40176         var ti = [];
40177         if (config.toolbar.items) {
40178             ti = config.toolbar.items ;
40179             delete config.toolbar.items ;
40180         }
40181         
40182         var nitems = [];
40183         this.toolbar.render(tool_el);
40184         for(var i =0;i < ti.length;i++) {
40185           //  Roo.log(['add child', items[i]]);
40186             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40187         }
40188         this.toolbar.items = nitems;
40189         
40190         delete config.toolbar;
40191     }
40192     
40193     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40194     config.grid.scrollBody = true;;
40195     config.grid.monitorWindowResize = false; // turn off autosizing
40196     config.grid.autoHeight = false;
40197     config.grid.autoWidth = false;
40198     
40199     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40200     
40201     if (config.background) {
40202         // render grid on panel activation (if panel background)
40203         this.on('activate', function(gp) {
40204             if (!gp.grid.rendered) {
40205                 gp.grid.render(this.wrapper);
40206                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40207             }
40208         });
40209             
40210     } else {
40211         this.grid.render(this.wrapper);
40212         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40213
40214     }
40215     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40216     // ??? needed ??? config.el = this.wrapper;
40217     
40218     
40219     
40220   
40221     // xtype created footer. - not sure if will work as we normally have to render first..
40222     if (this.footer && !this.footer.el && this.footer.xtype) {
40223         
40224         var ctr = this.grid.getView().getFooterPanel(true);
40225         this.footer.dataSource = this.grid.dataSource;
40226         this.footer = Roo.factory(this.footer, Roo);
40227         this.footer.render(ctr);
40228         
40229     }
40230     
40231     
40232     
40233     
40234      
40235 };
40236
40237 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40238     getId : function(){
40239         return this.grid.id;
40240     },
40241     
40242     /**
40243      * Returns the grid for this panel
40244      * @return {Roo.bootstrap.Table} 
40245      */
40246     getGrid : function(){
40247         return this.grid;    
40248     },
40249     
40250     setSize : function(width, height){
40251         if(!this.ignoreResize(width, height)){
40252             var grid = this.grid;
40253             var size = this.adjustForComponents(width, height);
40254             // tfoot is not a footer?
40255           
40256             
40257             var gridel = grid.getGridEl();
40258             gridel.setSize(size.width, size.height);
40259             
40260             var tbd = grid.getGridEl().select('tbody', true).first();
40261             var thd = grid.getGridEl().select('thead',true).first();
40262             var tbf= grid.getGridEl().select('tfoot', true).first();
40263
40264             if (tbf) {
40265                 size.height -= tbf.getHeight();
40266             }
40267             if (thd) {
40268                 size.height -= thd.getHeight();
40269             }
40270             
40271             tbd.setSize(size.width, size.height );
40272             // this is for the account management tab -seems to work there.
40273             var thd = grid.getGridEl().select('thead',true).first();
40274             //if (tbd) {
40275             //    tbd.setSize(size.width, size.height - thd.getHeight());
40276             //}
40277              
40278             grid.autoSize();
40279         }
40280     },
40281      
40282     
40283     
40284     beforeSlide : function(){
40285         this.grid.getView().scroller.clip();
40286     },
40287     
40288     afterSlide : function(){
40289         this.grid.getView().scroller.unclip();
40290     },
40291     
40292     destroy : function(){
40293         this.grid.destroy();
40294         delete this.grid;
40295         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40296     }
40297 });
40298
40299 /**
40300  * @class Roo.bootstrap.panel.Nest
40301  * @extends Roo.bootstrap.panel.Content
40302  * @constructor
40303  * Create a new Panel, that can contain a layout.Border.
40304  * 
40305  * 
40306  * @param {Roo.BorderLayout} layout The layout for this panel
40307  * @param {String/Object} config A string to set only the title or a config object
40308  */
40309 Roo.bootstrap.panel.Nest = function(config)
40310 {
40311     // construct with only one argument..
40312     /* FIXME - implement nicer consturctors
40313     if (layout.layout) {
40314         config = layout;
40315         layout = config.layout;
40316         delete config.layout;
40317     }
40318     if (layout.xtype && !layout.getEl) {
40319         // then layout needs constructing..
40320         layout = Roo.factory(layout, Roo);
40321     }
40322     */
40323     
40324     config.el =  config.layout.getEl();
40325     
40326     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40327     
40328     config.layout.monitorWindowResize = false; // turn off autosizing
40329     this.layout = config.layout;
40330     this.layout.getEl().addClass("roo-layout-nested-layout");
40331     this.layout.parent = this;
40332     
40333     
40334     
40335     
40336 };
40337
40338 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40339
40340     setSize : function(width, height){
40341         if(!this.ignoreResize(width, height)){
40342             var size = this.adjustForComponents(width, height);
40343             var el = this.layout.getEl();
40344             if (size.height < 1) {
40345                 el.setWidth(size.width);   
40346             } else {
40347                 el.setSize(size.width, size.height);
40348             }
40349             var touch = el.dom.offsetWidth;
40350             this.layout.layout();
40351             // ie requires a double layout on the first pass
40352             if(Roo.isIE && !this.initialized){
40353                 this.initialized = true;
40354                 this.layout.layout();
40355             }
40356         }
40357     },
40358     
40359     // activate all subpanels if not currently active..
40360     
40361     setActiveState : function(active){
40362         this.active = active;
40363         this.setActiveClass(active);
40364         
40365         if(!active){
40366             this.fireEvent("deactivate", this);
40367             return;
40368         }
40369         
40370         this.fireEvent("activate", this);
40371         // not sure if this should happen before or after..
40372         if (!this.layout) {
40373             return; // should not happen..
40374         }
40375         var reg = false;
40376         for (var r in this.layout.regions) {
40377             reg = this.layout.getRegion(r);
40378             if (reg.getActivePanel()) {
40379                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40380                 reg.setActivePanel(reg.getActivePanel());
40381                 continue;
40382             }
40383             if (!reg.panels.length) {
40384                 continue;
40385             }
40386             reg.showPanel(reg.getPanel(0));
40387         }
40388         
40389         
40390         
40391         
40392     },
40393     
40394     /**
40395      * Returns the nested BorderLayout for this panel
40396      * @return {Roo.BorderLayout} 
40397      */
40398     getLayout : function(){
40399         return this.layout;
40400     },
40401     
40402      /**
40403      * Adds a xtype elements to the layout of the nested panel
40404      * <pre><code>
40405
40406 panel.addxtype({
40407        xtype : 'ContentPanel',
40408        region: 'west',
40409        items: [ .... ]
40410    }
40411 );
40412
40413 panel.addxtype({
40414         xtype : 'NestedLayoutPanel',
40415         region: 'west',
40416         layout: {
40417            center: { },
40418            west: { }   
40419         },
40420         items : [ ... list of content panels or nested layout panels.. ]
40421    }
40422 );
40423 </code></pre>
40424      * @param {Object} cfg Xtype definition of item to add.
40425      */
40426     addxtype : function(cfg) {
40427         return this.layout.addxtype(cfg);
40428     
40429     }
40430 });/*
40431  * Based on:
40432  * Ext JS Library 1.1.1
40433  * Copyright(c) 2006-2007, Ext JS, LLC.
40434  *
40435  * Originally Released Under LGPL - original licence link has changed is not relivant.
40436  *
40437  * Fork - LGPL
40438  * <script type="text/javascript">
40439  */
40440 /**
40441  * @class Roo.TabPanel
40442  * @extends Roo.util.Observable
40443  * A lightweight tab container.
40444  * <br><br>
40445  * Usage:
40446  * <pre><code>
40447 // basic tabs 1, built from existing content
40448 var tabs = new Roo.TabPanel("tabs1");
40449 tabs.addTab("script", "View Script");
40450 tabs.addTab("markup", "View Markup");
40451 tabs.activate("script");
40452
40453 // more advanced tabs, built from javascript
40454 var jtabs = new Roo.TabPanel("jtabs");
40455 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40456
40457 // set up the UpdateManager
40458 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40459 var updater = tab2.getUpdateManager();
40460 updater.setDefaultUrl("ajax1.htm");
40461 tab2.on('activate', updater.refresh, updater, true);
40462
40463 // Use setUrl for Ajax loading
40464 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40465 tab3.setUrl("ajax2.htm", null, true);
40466
40467 // Disabled tab
40468 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40469 tab4.disable();
40470
40471 jtabs.activate("jtabs-1");
40472  * </code></pre>
40473  * @constructor
40474  * Create a new TabPanel.
40475  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40476  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40477  */
40478 Roo.bootstrap.panel.Tabs = function(config){
40479     /**
40480     * The container element for this TabPanel.
40481     * @type Roo.Element
40482     */
40483     this.el = Roo.get(config.el);
40484     delete config.el;
40485     if(config){
40486         if(typeof config == "boolean"){
40487             this.tabPosition = config ? "bottom" : "top";
40488         }else{
40489             Roo.apply(this, config);
40490         }
40491     }
40492     
40493     if(this.tabPosition == "bottom"){
40494         // if tabs are at the bottom = create the body first.
40495         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40496         this.el.addClass("roo-tabs-bottom");
40497     }
40498     // next create the tabs holders
40499     
40500     if (this.tabPosition == "west"){
40501         
40502         var reg = this.region; // fake it..
40503         while (reg) {
40504             if (!reg.mgr.parent) {
40505                 break;
40506             }
40507             reg = reg.mgr.parent.region;
40508         }
40509         Roo.log("got nest?");
40510         Roo.log(reg);
40511         if (reg.mgr.getRegion('west')) {
40512             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40513             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40514             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40515             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40516             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40517         
40518             
40519         }
40520         
40521         
40522     } else {
40523      
40524         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40525         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40526         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40527         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40528     }
40529     
40530     
40531     if(Roo.isIE){
40532         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40533     }
40534     
40535     // finally - if tabs are at the top, then create the body last..
40536     if(this.tabPosition != "bottom"){
40537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40538          * @type Roo.Element
40539          */
40540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40541         this.el.addClass("roo-tabs-top");
40542     }
40543     this.items = [];
40544
40545     this.bodyEl.setStyle("position", "relative");
40546
40547     this.active = null;
40548     this.activateDelegate = this.activate.createDelegate(this);
40549
40550     this.addEvents({
40551         /**
40552          * @event tabchange
40553          * Fires when the active tab changes
40554          * @param {Roo.TabPanel} this
40555          * @param {Roo.TabPanelItem} activePanel The new active tab
40556          */
40557         "tabchange": true,
40558         /**
40559          * @event beforetabchange
40560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40561          * @param {Roo.TabPanel} this
40562          * @param {Object} e Set cancel to true on this object to cancel the tab change
40563          * @param {Roo.TabPanelItem} tab The tab being changed to
40564          */
40565         "beforetabchange" : true
40566     });
40567
40568     Roo.EventManager.onWindowResize(this.onResize, this);
40569     this.cpad = this.el.getPadding("lr");
40570     this.hiddenCount = 0;
40571
40572
40573     // toolbar on the tabbar support...
40574     if (this.toolbar) {
40575         alert("no toolbar support yet");
40576         this.toolbar  = false;
40577         /*
40578         var tcfg = this.toolbar;
40579         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40580         this.toolbar = new Roo.Toolbar(tcfg);
40581         if (Roo.isSafari) {
40582             var tbl = tcfg.container.child('table', true);
40583             tbl.setAttribute('width', '100%');
40584         }
40585         */
40586         
40587     }
40588    
40589
40590
40591     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40592 };
40593
40594 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40595     /*
40596      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40597      */
40598     tabPosition : "top",
40599     /*
40600      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40601      */
40602     currentTabWidth : 0,
40603     /*
40604      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40605      */
40606     minTabWidth : 40,
40607     /*
40608      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40609      */
40610     maxTabWidth : 250,
40611     /*
40612      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40613      */
40614     preferredTabWidth : 175,
40615     /*
40616      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40617      */
40618     resizeTabs : false,
40619     /*
40620      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40621      */
40622     monitorResize : true,
40623     /*
40624      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40625      */
40626     toolbar : false,  // set by caller..
40627     
40628     region : false, /// set by caller
40629     
40630     disableTooltips : true, // not used yet...
40631
40632     /**
40633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40634      * @param {String} id The id of the div to use <b>or create</b>
40635      * @param {String} text The text for the tab
40636      * @param {String} content (optional) Content to put in the TabPanelItem body
40637      * @param {Boolean} closable (optional) True to create a close icon on the tab
40638      * @return {Roo.TabPanelItem} The created TabPanelItem
40639      */
40640     addTab : function(id, text, content, closable, tpl)
40641     {
40642         var item = new Roo.bootstrap.panel.TabItem({
40643             panel: this,
40644             id : id,
40645             text : text,
40646             closable : closable,
40647             tpl : tpl
40648         });
40649         this.addTabItem(item);
40650         if(content){
40651             item.setContent(content);
40652         }
40653         return item;
40654     },
40655
40656     /**
40657      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40658      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40659      * @return {Roo.TabPanelItem}
40660      */
40661     getTab : function(id){
40662         return this.items[id];
40663     },
40664
40665     /**
40666      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40667      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40668      */
40669     hideTab : function(id){
40670         var t = this.items[id];
40671         if(!t.isHidden()){
40672            t.setHidden(true);
40673            this.hiddenCount++;
40674            this.autoSizeTabs();
40675         }
40676     },
40677
40678     /**
40679      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40680      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40681      */
40682     unhideTab : function(id){
40683         var t = this.items[id];
40684         if(t.isHidden()){
40685            t.setHidden(false);
40686            this.hiddenCount--;
40687            this.autoSizeTabs();
40688         }
40689     },
40690
40691     /**
40692      * Adds an existing {@link Roo.TabPanelItem}.
40693      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40694      */
40695     addTabItem : function(item)
40696     {
40697         this.items[item.id] = item;
40698         this.items.push(item);
40699         this.autoSizeTabs();
40700       //  if(this.resizeTabs){
40701     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40702   //         this.autoSizeTabs();
40703 //        }else{
40704 //            item.autoSize();
40705        // }
40706     },
40707
40708     /**
40709      * Removes a {@link Roo.TabPanelItem}.
40710      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40711      */
40712     removeTab : function(id){
40713         var items = this.items;
40714         var tab = items[id];
40715         if(!tab) { return; }
40716         var index = items.indexOf(tab);
40717         if(this.active == tab && items.length > 1){
40718             var newTab = this.getNextAvailable(index);
40719             if(newTab) {
40720                 newTab.activate();
40721             }
40722         }
40723         this.stripEl.dom.removeChild(tab.pnode.dom);
40724         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40725             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40726         }
40727         items.splice(index, 1);
40728         delete this.items[tab.id];
40729         tab.fireEvent("close", tab);
40730         tab.purgeListeners();
40731         this.autoSizeTabs();
40732     },
40733
40734     getNextAvailable : function(start){
40735         var items = this.items;
40736         var index = start;
40737         // look for a next tab that will slide over to
40738         // replace the one being removed
40739         while(index < items.length){
40740             var item = items[++index];
40741             if(item && !item.isHidden()){
40742                 return item;
40743             }
40744         }
40745         // if one isn't found select the previous tab (on the left)
40746         index = start;
40747         while(index >= 0){
40748             var item = items[--index];
40749             if(item && !item.isHidden()){
40750                 return item;
40751             }
40752         }
40753         return null;
40754     },
40755
40756     /**
40757      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40758      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40759      */
40760     disableTab : function(id){
40761         var tab = this.items[id];
40762         if(tab && this.active != tab){
40763             tab.disable();
40764         }
40765     },
40766
40767     /**
40768      * Enables a {@link Roo.TabPanelItem} that is disabled.
40769      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40770      */
40771     enableTab : function(id){
40772         var tab = this.items[id];
40773         tab.enable();
40774     },
40775
40776     /**
40777      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40778      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40779      * @return {Roo.TabPanelItem} The TabPanelItem.
40780      */
40781     activate : function(id)
40782     {
40783         //Roo.log('activite:'  + id);
40784         
40785         var tab = this.items[id];
40786         if(!tab){
40787             return null;
40788         }
40789         if(tab == this.active || tab.disabled){
40790             return tab;
40791         }
40792         var e = {};
40793         this.fireEvent("beforetabchange", this, e, tab);
40794         if(e.cancel !== true && !tab.disabled){
40795             if(this.active){
40796                 this.active.hide();
40797             }
40798             this.active = this.items[id];
40799             this.active.show();
40800             this.fireEvent("tabchange", this, this.active);
40801         }
40802         return tab;
40803     },
40804
40805     /**
40806      * Gets the active {@link Roo.TabPanelItem}.
40807      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40808      */
40809     getActiveTab : function(){
40810         return this.active;
40811     },
40812
40813     /**
40814      * Updates the tab body element to fit the height of the container element
40815      * for overflow scrolling
40816      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40817      */
40818     syncHeight : function(targetHeight){
40819         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40820         var bm = this.bodyEl.getMargins();
40821         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40822         this.bodyEl.setHeight(newHeight);
40823         return newHeight;
40824     },
40825
40826     onResize : function(){
40827         if(this.monitorResize){
40828             this.autoSizeTabs();
40829         }
40830     },
40831
40832     /**
40833      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40834      */
40835     beginUpdate : function(){
40836         this.updating = true;
40837     },
40838
40839     /**
40840      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40841      */
40842     endUpdate : function(){
40843         this.updating = false;
40844         this.autoSizeTabs();
40845     },
40846
40847     /**
40848      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40849      */
40850     autoSizeTabs : function()
40851     {
40852         var count = this.items.length;
40853         var vcount = count - this.hiddenCount;
40854         
40855         if (vcount < 2) {
40856             this.stripEl.hide();
40857         } else {
40858             this.stripEl.show();
40859         }
40860         
40861         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40862             return;
40863         }
40864         
40865         
40866         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40867         var availWidth = Math.floor(w / vcount);
40868         var b = this.stripBody;
40869         if(b.getWidth() > w){
40870             var tabs = this.items;
40871             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40872             if(availWidth < this.minTabWidth){
40873                 /*if(!this.sleft){    // incomplete scrolling code
40874                     this.createScrollButtons();
40875                 }
40876                 this.showScroll();
40877                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40878             }
40879         }else{
40880             if(this.currentTabWidth < this.preferredTabWidth){
40881                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40882             }
40883         }
40884     },
40885
40886     /**
40887      * Returns the number of tabs in this TabPanel.
40888      * @return {Number}
40889      */
40890      getCount : function(){
40891          return this.items.length;
40892      },
40893
40894     /**
40895      * Resizes all the tabs to the passed width
40896      * @param {Number} The new width
40897      */
40898     setTabWidth : function(width){
40899         this.currentTabWidth = width;
40900         for(var i = 0, len = this.items.length; i < len; i++) {
40901                 if(!this.items[i].isHidden()) {
40902                 this.items[i].setWidth(width);
40903             }
40904         }
40905     },
40906
40907     /**
40908      * Destroys this TabPanel
40909      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40910      */
40911     destroy : function(removeEl){
40912         Roo.EventManager.removeResizeListener(this.onResize, this);
40913         for(var i = 0, len = this.items.length; i < len; i++){
40914             this.items[i].purgeListeners();
40915         }
40916         if(removeEl === true){
40917             this.el.update("");
40918             this.el.remove();
40919         }
40920     },
40921     
40922     createStrip : function(container)
40923     {
40924         var strip = document.createElement("nav");
40925         strip.className = Roo.bootstrap.version == 4 ?
40926             "navbar-light bg-light" : 
40927             "navbar navbar-default"; //"x-tabs-wrap";
40928         container.appendChild(strip);
40929         return strip;
40930     },
40931     
40932     createStripList : function(strip)
40933     {
40934         // div wrapper for retard IE
40935         // returns the "tr" element.
40936         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40937         //'<div class="x-tabs-strip-wrap">'+
40938           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40939           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40940         return strip.firstChild; //.firstChild.firstChild.firstChild;
40941     },
40942     createBody : function(container)
40943     {
40944         var body = document.createElement("div");
40945         Roo.id(body, "tab-body");
40946         //Roo.fly(body).addClass("x-tabs-body");
40947         Roo.fly(body).addClass("tab-content");
40948         container.appendChild(body);
40949         return body;
40950     },
40951     createItemBody :function(bodyEl, id){
40952         var body = Roo.getDom(id);
40953         if(!body){
40954             body = document.createElement("div");
40955             body.id = id;
40956         }
40957         //Roo.fly(body).addClass("x-tabs-item-body");
40958         Roo.fly(body).addClass("tab-pane");
40959          bodyEl.insertBefore(body, bodyEl.firstChild);
40960         return body;
40961     },
40962     /** @private */
40963     createStripElements :  function(stripEl, text, closable, tpl)
40964     {
40965         var td = document.createElement("li"); // was td..
40966         td.className = 'nav-item';
40967         
40968         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40969         
40970         
40971         stripEl.appendChild(td);
40972         /*if(closable){
40973             td.className = "x-tabs-closable";
40974             if(!this.closeTpl){
40975                 this.closeTpl = new Roo.Template(
40976                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40977                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40978                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40979                 );
40980             }
40981             var el = this.closeTpl.overwrite(td, {"text": text});
40982             var close = el.getElementsByTagName("div")[0];
40983             var inner = el.getElementsByTagName("em")[0];
40984             return {"el": el, "close": close, "inner": inner};
40985         } else {
40986         */
40987         // not sure what this is..
40988 //            if(!this.tabTpl){
40989                 //this.tabTpl = new Roo.Template(
40990                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40991                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40992                 //);
40993 //                this.tabTpl = new Roo.Template(
40994 //                   '<a href="#">' +
40995 //                   '<span unselectable="on"' +
40996 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40997 //                            ' >{text}</span></a>'
40998 //                );
40999 //                
41000 //            }
41001
41002
41003             var template = tpl || this.tabTpl || false;
41004             
41005             if(!template){
41006                 template =  new Roo.Template(
41007                         Roo.bootstrap.version == 4 ? 
41008                             (
41009                                 '<a class="nav-link" href="#" unselectable="on"' +
41010                                      (this.disableTooltips ? '' : ' title="{text}"') +
41011                                      ' >{text}</a>'
41012                             ) : (
41013                                 '<a class="nav-link" href="#">' +
41014                                 '<span unselectable="on"' +
41015                                          (this.disableTooltips ? '' : ' title="{text}"') +
41016                                     ' >{text}</span></a>'
41017                             )
41018                 );
41019             }
41020             
41021             switch (typeof(template)) {
41022                 case 'object' :
41023                     break;
41024                 case 'string' :
41025                     template = new Roo.Template(template);
41026                     break;
41027                 default :
41028                     break;
41029             }
41030             
41031             var el = template.overwrite(td, {"text": text});
41032             
41033             var inner = el.getElementsByTagName("span")[0];
41034             
41035             return {"el": el, "inner": inner};
41036             
41037     }
41038         
41039     
41040 });
41041
41042 /**
41043  * @class Roo.TabPanelItem
41044  * @extends Roo.util.Observable
41045  * Represents an individual item (tab plus body) in a TabPanel.
41046  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41047  * @param {String} id The id of this TabPanelItem
41048  * @param {String} text The text for the tab of this TabPanelItem
41049  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41050  */
41051 Roo.bootstrap.panel.TabItem = function(config){
41052     /**
41053      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41054      * @type Roo.TabPanel
41055      */
41056     this.tabPanel = config.panel;
41057     /**
41058      * The id for this TabPanelItem
41059      * @type String
41060      */
41061     this.id = config.id;
41062     /** @private */
41063     this.disabled = false;
41064     /** @private */
41065     this.text = config.text;
41066     /** @private */
41067     this.loaded = false;
41068     this.closable = config.closable;
41069
41070     /**
41071      * The body element for this TabPanelItem.
41072      * @type Roo.Element
41073      */
41074     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41075     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41076     this.bodyEl.setStyle("display", "block");
41077     this.bodyEl.setStyle("zoom", "1");
41078     //this.hideAction();
41079
41080     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41081     /** @private */
41082     this.el = Roo.get(els.el);
41083     this.inner = Roo.get(els.inner, true);
41084      this.textEl = Roo.bootstrap.version == 4 ?
41085         this.el : Roo.get(this.el.dom.firstChild, true);
41086
41087     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41088     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41089
41090     
41091 //    this.el.on("mousedown", this.onTabMouseDown, this);
41092     this.el.on("click", this.onTabClick, this);
41093     /** @private */
41094     if(config.closable){
41095         var c = Roo.get(els.close, true);
41096         c.dom.title = this.closeText;
41097         c.addClassOnOver("close-over");
41098         c.on("click", this.closeClick, this);
41099      }
41100
41101     this.addEvents({
41102          /**
41103          * @event activate
41104          * Fires when this tab becomes the active tab.
41105          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41106          * @param {Roo.TabPanelItem} this
41107          */
41108         "activate": true,
41109         /**
41110          * @event beforeclose
41111          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41112          * @param {Roo.TabPanelItem} this
41113          * @param {Object} e Set cancel to true on this object to cancel the close.
41114          */
41115         "beforeclose": true,
41116         /**
41117          * @event close
41118          * Fires when this tab is closed.
41119          * @param {Roo.TabPanelItem} this
41120          */
41121          "close": true,
41122         /**
41123          * @event deactivate
41124          * Fires when this tab is no longer the active tab.
41125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41126          * @param {Roo.TabPanelItem} this
41127          */
41128          "deactivate" : true
41129     });
41130     this.hidden = false;
41131
41132     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41133 };
41134
41135 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41136            {
41137     purgeListeners : function(){
41138        Roo.util.Observable.prototype.purgeListeners.call(this);
41139        this.el.removeAllListeners();
41140     },
41141     /**
41142      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41143      */
41144     show : function(){
41145         this.status_node.addClass("active");
41146         this.showAction();
41147         if(Roo.isOpera){
41148             this.tabPanel.stripWrap.repaint();
41149         }
41150         this.fireEvent("activate", this.tabPanel, this);
41151     },
41152
41153     /**
41154      * Returns true if this tab is the active tab.
41155      * @return {Boolean}
41156      */
41157     isActive : function(){
41158         return this.tabPanel.getActiveTab() == this;
41159     },
41160
41161     /**
41162      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41163      */
41164     hide : function(){
41165         this.status_node.removeClass("active");
41166         this.hideAction();
41167         this.fireEvent("deactivate", this.tabPanel, this);
41168     },
41169
41170     hideAction : function(){
41171         this.bodyEl.hide();
41172         this.bodyEl.setStyle("position", "absolute");
41173         this.bodyEl.setLeft("-20000px");
41174         this.bodyEl.setTop("-20000px");
41175     },
41176
41177     showAction : function(){
41178         this.bodyEl.setStyle("position", "relative");
41179         this.bodyEl.setTop("");
41180         this.bodyEl.setLeft("");
41181         this.bodyEl.show();
41182     },
41183
41184     /**
41185      * Set the tooltip for the tab.
41186      * @param {String} tooltip The tab's tooltip
41187      */
41188     setTooltip : function(text){
41189         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41190             this.textEl.dom.qtip = text;
41191             this.textEl.dom.removeAttribute('title');
41192         }else{
41193             this.textEl.dom.title = text;
41194         }
41195     },
41196
41197     onTabClick : function(e){
41198         e.preventDefault();
41199         this.tabPanel.activate(this.id);
41200     },
41201
41202     onTabMouseDown : function(e){
41203         e.preventDefault();
41204         this.tabPanel.activate(this.id);
41205     },
41206 /*
41207     getWidth : function(){
41208         return this.inner.getWidth();
41209     },
41210
41211     setWidth : function(width){
41212         var iwidth = width - this.linode.getPadding("lr");
41213         this.inner.setWidth(iwidth);
41214         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41215         this.linode.setWidth(width);
41216     },
41217 */
41218     /**
41219      * Show or hide the tab
41220      * @param {Boolean} hidden True to hide or false to show.
41221      */
41222     setHidden : function(hidden){
41223         this.hidden = hidden;
41224         this.linode.setStyle("display", hidden ? "none" : "");
41225     },
41226
41227     /**
41228      * Returns true if this tab is "hidden"
41229      * @return {Boolean}
41230      */
41231     isHidden : function(){
41232         return this.hidden;
41233     },
41234
41235     /**
41236      * Returns the text for this tab
41237      * @return {String}
41238      */
41239     getText : function(){
41240         return this.text;
41241     },
41242     /*
41243     autoSize : function(){
41244         //this.el.beginMeasure();
41245         this.textEl.setWidth(1);
41246         /*
41247          *  #2804 [new] Tabs in Roojs
41248          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41249          */
41250         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41251         //this.el.endMeasure();
41252     //},
41253
41254     /**
41255      * Sets the text for the tab (Note: this also sets the tooltip text)
41256      * @param {String} text The tab's text and tooltip
41257      */
41258     setText : function(text){
41259         this.text = text;
41260         this.textEl.update(text);
41261         this.setTooltip(text);
41262         //if(!this.tabPanel.resizeTabs){
41263         //    this.autoSize();
41264         //}
41265     },
41266     /**
41267      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41268      */
41269     activate : function(){
41270         this.tabPanel.activate(this.id);
41271     },
41272
41273     /**
41274      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41275      */
41276     disable : function(){
41277         if(this.tabPanel.active != this){
41278             this.disabled = true;
41279             this.status_node.addClass("disabled");
41280         }
41281     },
41282
41283     /**
41284      * Enables this TabPanelItem if it was previously disabled.
41285      */
41286     enable : function(){
41287         this.disabled = false;
41288         this.status_node.removeClass("disabled");
41289     },
41290
41291     /**
41292      * Sets the content for this TabPanelItem.
41293      * @param {String} content The content
41294      * @param {Boolean} loadScripts true to look for and load scripts
41295      */
41296     setContent : function(content, loadScripts){
41297         this.bodyEl.update(content, loadScripts);
41298     },
41299
41300     /**
41301      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41302      * @return {Roo.UpdateManager} The UpdateManager
41303      */
41304     getUpdateManager : function(){
41305         return this.bodyEl.getUpdateManager();
41306     },
41307
41308     /**
41309      * Set a URL to be used to load the content for this TabPanelItem.
41310      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41311      * @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)
41312      * @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)
41313      * @return {Roo.UpdateManager} The UpdateManager
41314      */
41315     setUrl : function(url, params, loadOnce){
41316         if(this.refreshDelegate){
41317             this.un('activate', this.refreshDelegate);
41318         }
41319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41320         this.on("activate", this.refreshDelegate);
41321         return this.bodyEl.getUpdateManager();
41322     },
41323
41324     /** @private */
41325     _handleRefresh : function(url, params, loadOnce){
41326         if(!loadOnce || !this.loaded){
41327             var updater = this.bodyEl.getUpdateManager();
41328             updater.update(url, params, this._setLoaded.createDelegate(this));
41329         }
41330     },
41331
41332     /**
41333      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41334      *   Will fail silently if the setUrl method has not been called.
41335      *   This does not activate the panel, just updates its content.
41336      */
41337     refresh : function(){
41338         if(this.refreshDelegate){
41339            this.loaded = false;
41340            this.refreshDelegate();
41341         }
41342     },
41343
41344     /** @private */
41345     _setLoaded : function(){
41346         this.loaded = true;
41347     },
41348
41349     /** @private */
41350     closeClick : function(e){
41351         var o = {};
41352         e.stopEvent();
41353         this.fireEvent("beforeclose", this, o);
41354         if(o.cancel !== true){
41355             this.tabPanel.removeTab(this.id);
41356         }
41357     },
41358     /**
41359      * The text displayed in the tooltip for the close icon.
41360      * @type String
41361      */
41362     closeText : "Close this tab"
41363 });
41364 /**
41365 *    This script refer to:
41366 *    Title: International Telephone Input
41367 *    Author: Jack O'Connor
41368 *    Code version:  v12.1.12
41369 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41370 **/
41371
41372 Roo.bootstrap.PhoneInputData = function() {
41373     var d = [
41374       [
41375         "Afghanistan (‫افغانستان‬‎)",
41376         "af",
41377         "93"
41378       ],
41379       [
41380         "Albania (Shqipëri)",
41381         "al",
41382         "355"
41383       ],
41384       [
41385         "Algeria (‫الجزائر‬‎)",
41386         "dz",
41387         "213"
41388       ],
41389       [
41390         "American Samoa",
41391         "as",
41392         "1684"
41393       ],
41394       [
41395         "Andorra",
41396         "ad",
41397         "376"
41398       ],
41399       [
41400         "Angola",
41401         "ao",
41402         "244"
41403       ],
41404       [
41405         "Anguilla",
41406         "ai",
41407         "1264"
41408       ],
41409       [
41410         "Antigua and Barbuda",
41411         "ag",
41412         "1268"
41413       ],
41414       [
41415         "Argentina",
41416         "ar",
41417         "54"
41418       ],
41419       [
41420         "Armenia (Հայաստան)",
41421         "am",
41422         "374"
41423       ],
41424       [
41425         "Aruba",
41426         "aw",
41427         "297"
41428       ],
41429       [
41430         "Australia",
41431         "au",
41432         "61",
41433         0
41434       ],
41435       [
41436         "Austria (Österreich)",
41437         "at",
41438         "43"
41439       ],
41440       [
41441         "Azerbaijan (Azərbaycan)",
41442         "az",
41443         "994"
41444       ],
41445       [
41446         "Bahamas",
41447         "bs",
41448         "1242"
41449       ],
41450       [
41451         "Bahrain (‫البحرين‬‎)",
41452         "bh",
41453         "973"
41454       ],
41455       [
41456         "Bangladesh (বাংলাদেশ)",
41457         "bd",
41458         "880"
41459       ],
41460       [
41461         "Barbados",
41462         "bb",
41463         "1246"
41464       ],
41465       [
41466         "Belarus (Беларусь)",
41467         "by",
41468         "375"
41469       ],
41470       [
41471         "Belgium (België)",
41472         "be",
41473         "32"
41474       ],
41475       [
41476         "Belize",
41477         "bz",
41478         "501"
41479       ],
41480       [
41481         "Benin (Bénin)",
41482         "bj",
41483         "229"
41484       ],
41485       [
41486         "Bermuda",
41487         "bm",
41488         "1441"
41489       ],
41490       [
41491         "Bhutan (འབྲུག)",
41492         "bt",
41493         "975"
41494       ],
41495       [
41496         "Bolivia",
41497         "bo",
41498         "591"
41499       ],
41500       [
41501         "Bosnia and Herzegovina (Босна и Херцеговина)",
41502         "ba",
41503         "387"
41504       ],
41505       [
41506         "Botswana",
41507         "bw",
41508         "267"
41509       ],
41510       [
41511         "Brazil (Brasil)",
41512         "br",
41513         "55"
41514       ],
41515       [
41516         "British Indian Ocean Territory",
41517         "io",
41518         "246"
41519       ],
41520       [
41521         "British Virgin Islands",
41522         "vg",
41523         "1284"
41524       ],
41525       [
41526         "Brunei",
41527         "bn",
41528         "673"
41529       ],
41530       [
41531         "Bulgaria (България)",
41532         "bg",
41533         "359"
41534       ],
41535       [
41536         "Burkina Faso",
41537         "bf",
41538         "226"
41539       ],
41540       [
41541         "Burundi (Uburundi)",
41542         "bi",
41543         "257"
41544       ],
41545       [
41546         "Cambodia (កម្ពុជា)",
41547         "kh",
41548         "855"
41549       ],
41550       [
41551         "Cameroon (Cameroun)",
41552         "cm",
41553         "237"
41554       ],
41555       [
41556         "Canada",
41557         "ca",
41558         "1",
41559         1,
41560         ["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"]
41561       ],
41562       [
41563         "Cape Verde (Kabu Verdi)",
41564         "cv",
41565         "238"
41566       ],
41567       [
41568         "Caribbean Netherlands",
41569         "bq",
41570         "599",
41571         1
41572       ],
41573       [
41574         "Cayman Islands",
41575         "ky",
41576         "1345"
41577       ],
41578       [
41579         "Central African Republic (République centrafricaine)",
41580         "cf",
41581         "236"
41582       ],
41583       [
41584         "Chad (Tchad)",
41585         "td",
41586         "235"
41587       ],
41588       [
41589         "Chile",
41590         "cl",
41591         "56"
41592       ],
41593       [
41594         "China (中国)",
41595         "cn",
41596         "86"
41597       ],
41598       [
41599         "Christmas Island",
41600         "cx",
41601         "61",
41602         2
41603       ],
41604       [
41605         "Cocos (Keeling) Islands",
41606         "cc",
41607         "61",
41608         1
41609       ],
41610       [
41611         "Colombia",
41612         "co",
41613         "57"
41614       ],
41615       [
41616         "Comoros (‫جزر القمر‬‎)",
41617         "km",
41618         "269"
41619       ],
41620       [
41621         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41622         "cd",
41623         "243"
41624       ],
41625       [
41626         "Congo (Republic) (Congo-Brazzaville)",
41627         "cg",
41628         "242"
41629       ],
41630       [
41631         "Cook Islands",
41632         "ck",
41633         "682"
41634       ],
41635       [
41636         "Costa Rica",
41637         "cr",
41638         "506"
41639       ],
41640       [
41641         "Côte d’Ivoire",
41642         "ci",
41643         "225"
41644       ],
41645       [
41646         "Croatia (Hrvatska)",
41647         "hr",
41648         "385"
41649       ],
41650       [
41651         "Cuba",
41652         "cu",
41653         "53"
41654       ],
41655       [
41656         "Curaçao",
41657         "cw",
41658         "599",
41659         0
41660       ],
41661       [
41662         "Cyprus (Κύπρος)",
41663         "cy",
41664         "357"
41665       ],
41666       [
41667         "Czech Republic (Česká republika)",
41668         "cz",
41669         "420"
41670       ],
41671       [
41672         "Denmark (Danmark)",
41673         "dk",
41674         "45"
41675       ],
41676       [
41677         "Djibouti",
41678         "dj",
41679         "253"
41680       ],
41681       [
41682         "Dominica",
41683         "dm",
41684         "1767"
41685       ],
41686       [
41687         "Dominican Republic (República Dominicana)",
41688         "do",
41689         "1",
41690         2,
41691         ["809", "829", "849"]
41692       ],
41693       [
41694         "Ecuador",
41695         "ec",
41696         "593"
41697       ],
41698       [
41699         "Egypt (‫مصر‬‎)",
41700         "eg",
41701         "20"
41702       ],
41703       [
41704         "El Salvador",
41705         "sv",
41706         "503"
41707       ],
41708       [
41709         "Equatorial Guinea (Guinea Ecuatorial)",
41710         "gq",
41711         "240"
41712       ],
41713       [
41714         "Eritrea",
41715         "er",
41716         "291"
41717       ],
41718       [
41719         "Estonia (Eesti)",
41720         "ee",
41721         "372"
41722       ],
41723       [
41724         "Ethiopia",
41725         "et",
41726         "251"
41727       ],
41728       [
41729         "Falkland Islands (Islas Malvinas)",
41730         "fk",
41731         "500"
41732       ],
41733       [
41734         "Faroe Islands (Føroyar)",
41735         "fo",
41736         "298"
41737       ],
41738       [
41739         "Fiji",
41740         "fj",
41741         "679"
41742       ],
41743       [
41744         "Finland (Suomi)",
41745         "fi",
41746         "358",
41747         0
41748       ],
41749       [
41750         "France",
41751         "fr",
41752         "33"
41753       ],
41754       [
41755         "French Guiana (Guyane française)",
41756         "gf",
41757         "594"
41758       ],
41759       [
41760         "French Polynesia (Polynésie française)",
41761         "pf",
41762         "689"
41763       ],
41764       [
41765         "Gabon",
41766         "ga",
41767         "241"
41768       ],
41769       [
41770         "Gambia",
41771         "gm",
41772         "220"
41773       ],
41774       [
41775         "Georgia (საქართველო)",
41776         "ge",
41777         "995"
41778       ],
41779       [
41780         "Germany (Deutschland)",
41781         "de",
41782         "49"
41783       ],
41784       [
41785         "Ghana (Gaana)",
41786         "gh",
41787         "233"
41788       ],
41789       [
41790         "Gibraltar",
41791         "gi",
41792         "350"
41793       ],
41794       [
41795         "Greece (Ελλάδα)",
41796         "gr",
41797         "30"
41798       ],
41799       [
41800         "Greenland (Kalaallit Nunaat)",
41801         "gl",
41802         "299"
41803       ],
41804       [
41805         "Grenada",
41806         "gd",
41807         "1473"
41808       ],
41809       [
41810         "Guadeloupe",
41811         "gp",
41812         "590",
41813         0
41814       ],
41815       [
41816         "Guam",
41817         "gu",
41818         "1671"
41819       ],
41820       [
41821         "Guatemala",
41822         "gt",
41823         "502"
41824       ],
41825       [
41826         "Guernsey",
41827         "gg",
41828         "44",
41829         1
41830       ],
41831       [
41832         "Guinea (Guinée)",
41833         "gn",
41834         "224"
41835       ],
41836       [
41837         "Guinea-Bissau (Guiné Bissau)",
41838         "gw",
41839         "245"
41840       ],
41841       [
41842         "Guyana",
41843         "gy",
41844         "592"
41845       ],
41846       [
41847         "Haiti",
41848         "ht",
41849         "509"
41850       ],
41851       [
41852         "Honduras",
41853         "hn",
41854         "504"
41855       ],
41856       [
41857         "Hong Kong (香港)",
41858         "hk",
41859         "852"
41860       ],
41861       [
41862         "Hungary (Magyarország)",
41863         "hu",
41864         "36"
41865       ],
41866       [
41867         "Iceland (Ísland)",
41868         "is",
41869         "354"
41870       ],
41871       [
41872         "India (भारत)",
41873         "in",
41874         "91"
41875       ],
41876       [
41877         "Indonesia",
41878         "id",
41879         "62"
41880       ],
41881       [
41882         "Iran (‫ایران‬‎)",
41883         "ir",
41884         "98"
41885       ],
41886       [
41887         "Iraq (‫العراق‬‎)",
41888         "iq",
41889         "964"
41890       ],
41891       [
41892         "Ireland",
41893         "ie",
41894         "353"
41895       ],
41896       [
41897         "Isle of Man",
41898         "im",
41899         "44",
41900         2
41901       ],
41902       [
41903         "Israel (‫ישראל‬‎)",
41904         "il",
41905         "972"
41906       ],
41907       [
41908         "Italy (Italia)",
41909         "it",
41910         "39",
41911         0
41912       ],
41913       [
41914         "Jamaica",
41915         "jm",
41916         "1876"
41917       ],
41918       [
41919         "Japan (日本)",
41920         "jp",
41921         "81"
41922       ],
41923       [
41924         "Jersey",
41925         "je",
41926         "44",
41927         3
41928       ],
41929       [
41930         "Jordan (‫الأردن‬‎)",
41931         "jo",
41932         "962"
41933       ],
41934       [
41935         "Kazakhstan (Казахстан)",
41936         "kz",
41937         "7",
41938         1
41939       ],
41940       [
41941         "Kenya",
41942         "ke",
41943         "254"
41944       ],
41945       [
41946         "Kiribati",
41947         "ki",
41948         "686"
41949       ],
41950       [
41951         "Kosovo",
41952         "xk",
41953         "383"
41954       ],
41955       [
41956         "Kuwait (‫الكويت‬‎)",
41957         "kw",
41958         "965"
41959       ],
41960       [
41961         "Kyrgyzstan (Кыргызстан)",
41962         "kg",
41963         "996"
41964       ],
41965       [
41966         "Laos (ລາວ)",
41967         "la",
41968         "856"
41969       ],
41970       [
41971         "Latvia (Latvija)",
41972         "lv",
41973         "371"
41974       ],
41975       [
41976         "Lebanon (‫لبنان‬‎)",
41977         "lb",
41978         "961"
41979       ],
41980       [
41981         "Lesotho",
41982         "ls",
41983         "266"
41984       ],
41985       [
41986         "Liberia",
41987         "lr",
41988         "231"
41989       ],
41990       [
41991         "Libya (‫ليبيا‬‎)",
41992         "ly",
41993         "218"
41994       ],
41995       [
41996         "Liechtenstein",
41997         "li",
41998         "423"
41999       ],
42000       [
42001         "Lithuania (Lietuva)",
42002         "lt",
42003         "370"
42004       ],
42005       [
42006         "Luxembourg",
42007         "lu",
42008         "352"
42009       ],
42010       [
42011         "Macau (澳門)",
42012         "mo",
42013         "853"
42014       ],
42015       [
42016         "Macedonia (FYROM) (Македонија)",
42017         "mk",
42018         "389"
42019       ],
42020       [
42021         "Madagascar (Madagasikara)",
42022         "mg",
42023         "261"
42024       ],
42025       [
42026         "Malawi",
42027         "mw",
42028         "265"
42029       ],
42030       [
42031         "Malaysia",
42032         "my",
42033         "60"
42034       ],
42035       [
42036         "Maldives",
42037         "mv",
42038         "960"
42039       ],
42040       [
42041         "Mali",
42042         "ml",
42043         "223"
42044       ],
42045       [
42046         "Malta",
42047         "mt",
42048         "356"
42049       ],
42050       [
42051         "Marshall Islands",
42052         "mh",
42053         "692"
42054       ],
42055       [
42056         "Martinique",
42057         "mq",
42058         "596"
42059       ],
42060       [
42061         "Mauritania (‫موريتانيا‬‎)",
42062         "mr",
42063         "222"
42064       ],
42065       [
42066         "Mauritius (Moris)",
42067         "mu",
42068         "230"
42069       ],
42070       [
42071         "Mayotte",
42072         "yt",
42073         "262",
42074         1
42075       ],
42076       [
42077         "Mexico (México)",
42078         "mx",
42079         "52"
42080       ],
42081       [
42082         "Micronesia",
42083         "fm",
42084         "691"
42085       ],
42086       [
42087         "Moldova (Republica Moldova)",
42088         "md",
42089         "373"
42090       ],
42091       [
42092         "Monaco",
42093         "mc",
42094         "377"
42095       ],
42096       [
42097         "Mongolia (Монгол)",
42098         "mn",
42099         "976"
42100       ],
42101       [
42102         "Montenegro (Crna Gora)",
42103         "me",
42104         "382"
42105       ],
42106       [
42107         "Montserrat",
42108         "ms",
42109         "1664"
42110       ],
42111       [
42112         "Morocco (‫المغرب‬‎)",
42113         "ma",
42114         "212",
42115         0
42116       ],
42117       [
42118         "Mozambique (Moçambique)",
42119         "mz",
42120         "258"
42121       ],
42122       [
42123         "Myanmar (Burma) (မြန်မာ)",
42124         "mm",
42125         "95"
42126       ],
42127       [
42128         "Namibia (Namibië)",
42129         "na",
42130         "264"
42131       ],
42132       [
42133         "Nauru",
42134         "nr",
42135         "674"
42136       ],
42137       [
42138         "Nepal (नेपाल)",
42139         "np",
42140         "977"
42141       ],
42142       [
42143         "Netherlands (Nederland)",
42144         "nl",
42145         "31"
42146       ],
42147       [
42148         "New Caledonia (Nouvelle-Calédonie)",
42149         "nc",
42150         "687"
42151       ],
42152       [
42153         "New Zealand",
42154         "nz",
42155         "64"
42156       ],
42157       [
42158         "Nicaragua",
42159         "ni",
42160         "505"
42161       ],
42162       [
42163         "Niger (Nijar)",
42164         "ne",
42165         "227"
42166       ],
42167       [
42168         "Nigeria",
42169         "ng",
42170         "234"
42171       ],
42172       [
42173         "Niue",
42174         "nu",
42175         "683"
42176       ],
42177       [
42178         "Norfolk Island",
42179         "nf",
42180         "672"
42181       ],
42182       [
42183         "North Korea (조선 민주주의 인민 공화국)",
42184         "kp",
42185         "850"
42186       ],
42187       [
42188         "Northern Mariana Islands",
42189         "mp",
42190         "1670"
42191       ],
42192       [
42193         "Norway (Norge)",
42194         "no",
42195         "47",
42196         0
42197       ],
42198       [
42199         "Oman (‫عُمان‬‎)",
42200         "om",
42201         "968"
42202       ],
42203       [
42204         "Pakistan (‫پاکستان‬‎)",
42205         "pk",
42206         "92"
42207       ],
42208       [
42209         "Palau",
42210         "pw",
42211         "680"
42212       ],
42213       [
42214         "Palestine (‫فلسطين‬‎)",
42215         "ps",
42216         "970"
42217       ],
42218       [
42219         "Panama (Panamá)",
42220         "pa",
42221         "507"
42222       ],
42223       [
42224         "Papua New Guinea",
42225         "pg",
42226         "675"
42227       ],
42228       [
42229         "Paraguay",
42230         "py",
42231         "595"
42232       ],
42233       [
42234         "Peru (Perú)",
42235         "pe",
42236         "51"
42237       ],
42238       [
42239         "Philippines",
42240         "ph",
42241         "63"
42242       ],
42243       [
42244         "Poland (Polska)",
42245         "pl",
42246         "48"
42247       ],
42248       [
42249         "Portugal",
42250         "pt",
42251         "351"
42252       ],
42253       [
42254         "Puerto Rico",
42255         "pr",
42256         "1",
42257         3,
42258         ["787", "939"]
42259       ],
42260       [
42261         "Qatar (‫قطر‬‎)",
42262         "qa",
42263         "974"
42264       ],
42265       [
42266         "Réunion (La Réunion)",
42267         "re",
42268         "262",
42269         0
42270       ],
42271       [
42272         "Romania (România)",
42273         "ro",
42274         "40"
42275       ],
42276       [
42277         "Russia (Россия)",
42278         "ru",
42279         "7",
42280         0
42281       ],
42282       [
42283         "Rwanda",
42284         "rw",
42285         "250"
42286       ],
42287       [
42288         "Saint Barthélemy",
42289         "bl",
42290         "590",
42291         1
42292       ],
42293       [
42294         "Saint Helena",
42295         "sh",
42296         "290"
42297       ],
42298       [
42299         "Saint Kitts and Nevis",
42300         "kn",
42301         "1869"
42302       ],
42303       [
42304         "Saint Lucia",
42305         "lc",
42306         "1758"
42307       ],
42308       [
42309         "Saint Martin (Saint-Martin (partie française))",
42310         "mf",
42311         "590",
42312         2
42313       ],
42314       [
42315         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42316         "pm",
42317         "508"
42318       ],
42319       [
42320         "Saint Vincent and the Grenadines",
42321         "vc",
42322         "1784"
42323       ],
42324       [
42325         "Samoa",
42326         "ws",
42327         "685"
42328       ],
42329       [
42330         "San Marino",
42331         "sm",
42332         "378"
42333       ],
42334       [
42335         "São Tomé and Príncipe (São Tomé e Príncipe)",
42336         "st",
42337         "239"
42338       ],
42339       [
42340         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42341         "sa",
42342         "966"
42343       ],
42344       [
42345         "Senegal (Sénégal)",
42346         "sn",
42347         "221"
42348       ],
42349       [
42350         "Serbia (Србија)",
42351         "rs",
42352         "381"
42353       ],
42354       [
42355         "Seychelles",
42356         "sc",
42357         "248"
42358       ],
42359       [
42360         "Sierra Leone",
42361         "sl",
42362         "232"
42363       ],
42364       [
42365         "Singapore",
42366         "sg",
42367         "65"
42368       ],
42369       [
42370         "Sint Maarten",
42371         "sx",
42372         "1721"
42373       ],
42374       [
42375         "Slovakia (Slovensko)",
42376         "sk",
42377         "421"
42378       ],
42379       [
42380         "Slovenia (Slovenija)",
42381         "si",
42382         "386"
42383       ],
42384       [
42385         "Solomon Islands",
42386         "sb",
42387         "677"
42388       ],
42389       [
42390         "Somalia (Soomaaliya)",
42391         "so",
42392         "252"
42393       ],
42394       [
42395         "South Africa",
42396         "za",
42397         "27"
42398       ],
42399       [
42400         "South Korea (대한민국)",
42401         "kr",
42402         "82"
42403       ],
42404       [
42405         "South Sudan (‫جنوب السودان‬‎)",
42406         "ss",
42407         "211"
42408       ],
42409       [
42410         "Spain (España)",
42411         "es",
42412         "34"
42413       ],
42414       [
42415         "Sri Lanka (ශ්‍රී ලංකාව)",
42416         "lk",
42417         "94"
42418       ],
42419       [
42420         "Sudan (‫السودان‬‎)",
42421         "sd",
42422         "249"
42423       ],
42424       [
42425         "Suriname",
42426         "sr",
42427         "597"
42428       ],
42429       [
42430         "Svalbard and Jan Mayen",
42431         "sj",
42432         "47",
42433         1
42434       ],
42435       [
42436         "Swaziland",
42437         "sz",
42438         "268"
42439       ],
42440       [
42441         "Sweden (Sverige)",
42442         "se",
42443         "46"
42444       ],
42445       [
42446         "Switzerland (Schweiz)",
42447         "ch",
42448         "41"
42449       ],
42450       [
42451         "Syria (‫سوريا‬‎)",
42452         "sy",
42453         "963"
42454       ],
42455       [
42456         "Taiwan (台灣)",
42457         "tw",
42458         "886"
42459       ],
42460       [
42461         "Tajikistan",
42462         "tj",
42463         "992"
42464       ],
42465       [
42466         "Tanzania",
42467         "tz",
42468         "255"
42469       ],
42470       [
42471         "Thailand (ไทย)",
42472         "th",
42473         "66"
42474       ],
42475       [
42476         "Timor-Leste",
42477         "tl",
42478         "670"
42479       ],
42480       [
42481         "Togo",
42482         "tg",
42483         "228"
42484       ],
42485       [
42486         "Tokelau",
42487         "tk",
42488         "690"
42489       ],
42490       [
42491         "Tonga",
42492         "to",
42493         "676"
42494       ],
42495       [
42496         "Trinidad and Tobago",
42497         "tt",
42498         "1868"
42499       ],
42500       [
42501         "Tunisia (‫تونس‬‎)",
42502         "tn",
42503         "216"
42504       ],
42505       [
42506         "Turkey (Türkiye)",
42507         "tr",
42508         "90"
42509       ],
42510       [
42511         "Turkmenistan",
42512         "tm",
42513         "993"
42514       ],
42515       [
42516         "Turks and Caicos Islands",
42517         "tc",
42518         "1649"
42519       ],
42520       [
42521         "Tuvalu",
42522         "tv",
42523         "688"
42524       ],
42525       [
42526         "U.S. Virgin Islands",
42527         "vi",
42528         "1340"
42529       ],
42530       [
42531         "Uganda",
42532         "ug",
42533         "256"
42534       ],
42535       [
42536         "Ukraine (Україна)",
42537         "ua",
42538         "380"
42539       ],
42540       [
42541         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42542         "ae",
42543         "971"
42544       ],
42545       [
42546         "United Kingdom",
42547         "gb",
42548         "44",
42549         0
42550       ],
42551       [
42552         "United States",
42553         "us",
42554         "1",
42555         0
42556       ],
42557       [
42558         "Uruguay",
42559         "uy",
42560         "598"
42561       ],
42562       [
42563         "Uzbekistan (Oʻzbekiston)",
42564         "uz",
42565         "998"
42566       ],
42567       [
42568         "Vanuatu",
42569         "vu",
42570         "678"
42571       ],
42572       [
42573         "Vatican City (Città del Vaticano)",
42574         "va",
42575         "39",
42576         1
42577       ],
42578       [
42579         "Venezuela",
42580         "ve",
42581         "58"
42582       ],
42583       [
42584         "Vietnam (Việt Nam)",
42585         "vn",
42586         "84"
42587       ],
42588       [
42589         "Wallis and Futuna (Wallis-et-Futuna)",
42590         "wf",
42591         "681"
42592       ],
42593       [
42594         "Western Sahara (‫الصحراء الغربية‬‎)",
42595         "eh",
42596         "212",
42597         1
42598       ],
42599       [
42600         "Yemen (‫اليمن‬‎)",
42601         "ye",
42602         "967"
42603       ],
42604       [
42605         "Zambia",
42606         "zm",
42607         "260"
42608       ],
42609       [
42610         "Zimbabwe",
42611         "zw",
42612         "263"
42613       ],
42614       [
42615         "Åland Islands",
42616         "ax",
42617         "358",
42618         1
42619       ]
42620   ];
42621   
42622   return d;
42623 }/**
42624 *    This script refer to:
42625 *    Title: International Telephone Input
42626 *    Author: Jack O'Connor
42627 *    Code version:  v12.1.12
42628 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42629 **/
42630
42631 /**
42632  * @class Roo.bootstrap.PhoneInput
42633  * @extends Roo.bootstrap.TriggerField
42634  * An input with International dial-code selection
42635  
42636  * @cfg {String} defaultDialCode default '+852'
42637  * @cfg {Array} preferedCountries default []
42638   
42639  * @constructor
42640  * Create a new PhoneInput.
42641  * @param {Object} config Configuration options
42642  */
42643
42644 Roo.bootstrap.PhoneInput = function(config) {
42645     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42646 };
42647
42648 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42649         
42650         listWidth: undefined,
42651         
42652         selectedClass: 'active',
42653         
42654         invalidClass : "has-warning",
42655         
42656         validClass: 'has-success',
42657         
42658         allowed: '0123456789',
42659         
42660         max_length: 15,
42661         
42662         /**
42663          * @cfg {String} defaultDialCode The default dial code when initializing the input
42664          */
42665         defaultDialCode: '+852',
42666         
42667         /**
42668          * @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
42669          */
42670         preferedCountries: false,
42671         
42672         getAutoCreate : function()
42673         {
42674             var data = Roo.bootstrap.PhoneInputData();
42675             var align = this.labelAlign || this.parentLabelAlign();
42676             var id = Roo.id();
42677             
42678             this.allCountries = [];
42679             this.dialCodeMapping = [];
42680             
42681             for (var i = 0; i < data.length; i++) {
42682               var c = data[i];
42683               this.allCountries[i] = {
42684                 name: c[0],
42685                 iso2: c[1],
42686                 dialCode: c[2],
42687                 priority: c[3] || 0,
42688                 areaCodes: c[4] || null
42689               };
42690               this.dialCodeMapping[c[2]] = {
42691                   name: c[0],
42692                   iso2: c[1],
42693                   priority: c[3] || 0,
42694                   areaCodes: c[4] || null
42695               };
42696             }
42697             
42698             var cfg = {
42699                 cls: 'form-group',
42700                 cn: []
42701             };
42702             
42703             var input =  {
42704                 tag: 'input',
42705                 id : id,
42706                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42707                 maxlength: this.max_length,
42708                 cls : 'form-control tel-input',
42709                 autocomplete: 'new-password'
42710             };
42711             
42712             var hiddenInput = {
42713                 tag: 'input',
42714                 type: 'hidden',
42715                 cls: 'hidden-tel-input'
42716             };
42717             
42718             if (this.name) {
42719                 hiddenInput.name = this.name;
42720             }
42721             
42722             if (this.disabled) {
42723                 input.disabled = true;
42724             }
42725             
42726             var flag_container = {
42727                 tag: 'div',
42728                 cls: 'flag-box',
42729                 cn: [
42730                     {
42731                         tag: 'div',
42732                         cls: 'flag'
42733                     },
42734                     {
42735                         tag: 'div',
42736                         cls: 'caret'
42737                     }
42738                 ]
42739             };
42740             
42741             var box = {
42742                 tag: 'div',
42743                 cls: this.hasFeedback ? 'has-feedback' : '',
42744                 cn: [
42745                     hiddenInput,
42746                     input,
42747                     {
42748                         tag: 'input',
42749                         cls: 'dial-code-holder',
42750                         disabled: true
42751                     }
42752                 ]
42753             };
42754             
42755             var container = {
42756                 cls: 'roo-select2-container input-group',
42757                 cn: [
42758                     flag_container,
42759                     box
42760                 ]
42761             };
42762             
42763             if (this.fieldLabel.length) {
42764                 var indicator = {
42765                     tag: 'i',
42766                     tooltip: 'This field is required'
42767                 };
42768                 
42769                 var label = {
42770                     tag: 'label',
42771                     'for':  id,
42772                     cls: 'control-label',
42773                     cn: []
42774                 };
42775                 
42776                 var label_text = {
42777                     tag: 'span',
42778                     html: this.fieldLabel
42779                 };
42780                 
42781                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42782                 label.cn = [
42783                     indicator,
42784                     label_text
42785                 ];
42786                 
42787                 if(this.indicatorpos == 'right') {
42788                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42789                     label.cn = [
42790                         label_text,
42791                         indicator
42792                     ];
42793                 }
42794                 
42795                 if(align == 'left') {
42796                     container = {
42797                         tag: 'div',
42798                         cn: [
42799                             container
42800                         ]
42801                     };
42802                     
42803                     if(this.labelWidth > 12){
42804                         label.style = "width: " + this.labelWidth + 'px';
42805                     }
42806                     if(this.labelWidth < 13 && this.labelmd == 0){
42807                         this.labelmd = this.labelWidth;
42808                     }
42809                     if(this.labellg > 0){
42810                         label.cls += ' col-lg-' + this.labellg;
42811                         input.cls += ' col-lg-' + (12 - this.labellg);
42812                     }
42813                     if(this.labelmd > 0){
42814                         label.cls += ' col-md-' + this.labelmd;
42815                         container.cls += ' col-md-' + (12 - this.labelmd);
42816                     }
42817                     if(this.labelsm > 0){
42818                         label.cls += ' col-sm-' + this.labelsm;
42819                         container.cls += ' col-sm-' + (12 - this.labelsm);
42820                     }
42821                     if(this.labelxs > 0){
42822                         label.cls += ' col-xs-' + this.labelxs;
42823                         container.cls += ' col-xs-' + (12 - this.labelxs);
42824                     }
42825                 }
42826             }
42827             
42828             cfg.cn = [
42829                 label,
42830                 container
42831             ];
42832             
42833             var settings = this;
42834             
42835             ['xs','sm','md','lg'].map(function(size){
42836                 if (settings[size]) {
42837                     cfg.cls += ' col-' + size + '-' + settings[size];
42838                 }
42839             });
42840             
42841             this.store = new Roo.data.Store({
42842                 proxy : new Roo.data.MemoryProxy({}),
42843                 reader : new Roo.data.JsonReader({
42844                     fields : [
42845                         {
42846                             'name' : 'name',
42847                             'type' : 'string'
42848                         },
42849                         {
42850                             'name' : 'iso2',
42851                             'type' : 'string'
42852                         },
42853                         {
42854                             'name' : 'dialCode',
42855                             'type' : 'string'
42856                         },
42857                         {
42858                             'name' : 'priority',
42859                             'type' : 'string'
42860                         },
42861                         {
42862                             'name' : 'areaCodes',
42863                             'type' : 'string'
42864                         }
42865                     ]
42866                 })
42867             });
42868             
42869             if(!this.preferedCountries) {
42870                 this.preferedCountries = [
42871                     'hk',
42872                     'gb',
42873                     'us'
42874                 ];
42875             }
42876             
42877             var p = this.preferedCountries.reverse();
42878             
42879             if(p) {
42880                 for (var i = 0; i < p.length; i++) {
42881                     for (var j = 0; j < this.allCountries.length; j++) {
42882                         if(this.allCountries[j].iso2 == p[i]) {
42883                             var t = this.allCountries[j];
42884                             this.allCountries.splice(j,1);
42885                             this.allCountries.unshift(t);
42886                         }
42887                     } 
42888                 }
42889             }
42890             
42891             this.store.proxy.data = {
42892                 success: true,
42893                 data: this.allCountries
42894             };
42895             
42896             return cfg;
42897         },
42898         
42899         initEvents : function()
42900         {
42901             this.createList();
42902             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42903             
42904             this.indicator = this.indicatorEl();
42905             this.flag = this.flagEl();
42906             this.dialCodeHolder = this.dialCodeHolderEl();
42907             
42908             this.trigger = this.el.select('div.flag-box',true).first();
42909             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42910             
42911             var _this = this;
42912             
42913             (function(){
42914                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42915                 _this.list.setWidth(lw);
42916             }).defer(100);
42917             
42918             this.list.on('mouseover', this.onViewOver, this);
42919             this.list.on('mousemove', this.onViewMove, this);
42920             this.inputEl().on("keyup", this.onKeyUp, this);
42921             this.inputEl().on("keypress", this.onKeyPress, this);
42922             
42923             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42924
42925             this.view = new Roo.View(this.list, this.tpl, {
42926                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42927             });
42928             
42929             this.view.on('click', this.onViewClick, this);
42930             this.setValue(this.defaultDialCode);
42931         },
42932         
42933         onTriggerClick : function(e)
42934         {
42935             Roo.log('trigger click');
42936             if(this.disabled){
42937                 return;
42938             }
42939             
42940             if(this.isExpanded()){
42941                 this.collapse();
42942                 this.hasFocus = false;
42943             }else {
42944                 this.store.load({});
42945                 this.hasFocus = true;
42946                 this.expand();
42947             }
42948         },
42949         
42950         isExpanded : function()
42951         {
42952             return this.list.isVisible();
42953         },
42954         
42955         collapse : function()
42956         {
42957             if(!this.isExpanded()){
42958                 return;
42959             }
42960             this.list.hide();
42961             Roo.get(document).un('mousedown', this.collapseIf, this);
42962             Roo.get(document).un('mousewheel', this.collapseIf, this);
42963             this.fireEvent('collapse', this);
42964             this.validate();
42965         },
42966         
42967         expand : function()
42968         {
42969             Roo.log('expand');
42970
42971             if(this.isExpanded() || !this.hasFocus){
42972                 return;
42973             }
42974             
42975             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42976             this.list.setWidth(lw);
42977             
42978             this.list.show();
42979             this.restrictHeight();
42980             
42981             Roo.get(document).on('mousedown', this.collapseIf, this);
42982             Roo.get(document).on('mousewheel', this.collapseIf, this);
42983             
42984             this.fireEvent('expand', this);
42985         },
42986         
42987         restrictHeight : function()
42988         {
42989             this.list.alignTo(this.inputEl(), this.listAlign);
42990             this.list.alignTo(this.inputEl(), this.listAlign);
42991         },
42992         
42993         onViewOver : function(e, t)
42994         {
42995             if(this.inKeyMode){
42996                 return;
42997             }
42998             var item = this.view.findItemFromChild(t);
42999             
43000             if(item){
43001                 var index = this.view.indexOf(item);
43002                 this.select(index, false);
43003             }
43004         },
43005
43006         // private
43007         onViewClick : function(view, doFocus, el, e)
43008         {
43009             var index = this.view.getSelectedIndexes()[0];
43010             
43011             var r = this.store.getAt(index);
43012             
43013             if(r){
43014                 this.onSelect(r, index);
43015             }
43016             if(doFocus !== false && !this.blockFocus){
43017                 this.inputEl().focus();
43018             }
43019         },
43020         
43021         onViewMove : function(e, t)
43022         {
43023             this.inKeyMode = false;
43024         },
43025         
43026         select : function(index, scrollIntoView)
43027         {
43028             this.selectedIndex = index;
43029             this.view.select(index);
43030             if(scrollIntoView !== false){
43031                 var el = this.view.getNode(index);
43032                 if(el){
43033                     this.list.scrollChildIntoView(el, false);
43034                 }
43035             }
43036         },
43037         
43038         createList : function()
43039         {
43040             this.list = Roo.get(document.body).createChild({
43041                 tag: 'ul',
43042                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43043                 style: 'display:none'
43044             });
43045             
43046             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43047         },
43048         
43049         collapseIf : function(e)
43050         {
43051             var in_combo  = e.within(this.el);
43052             var in_list =  e.within(this.list);
43053             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43054             
43055             if (in_combo || in_list || is_list) {
43056                 return;
43057             }
43058             this.collapse();
43059         },
43060         
43061         onSelect : function(record, index)
43062         {
43063             if(this.fireEvent('beforeselect', this, record, index) !== false){
43064                 
43065                 this.setFlagClass(record.data.iso2);
43066                 this.setDialCode(record.data.dialCode);
43067                 this.hasFocus = false;
43068                 this.collapse();
43069                 this.fireEvent('select', this, record, index);
43070             }
43071         },
43072         
43073         flagEl : function()
43074         {
43075             var flag = this.el.select('div.flag',true).first();
43076             if(!flag){
43077                 return false;
43078             }
43079             return flag;
43080         },
43081         
43082         dialCodeHolderEl : function()
43083         {
43084             var d = this.el.select('input.dial-code-holder',true).first();
43085             if(!d){
43086                 return false;
43087             }
43088             return d;
43089         },
43090         
43091         setDialCode : function(v)
43092         {
43093             this.dialCodeHolder.dom.value = '+'+v;
43094         },
43095         
43096         setFlagClass : function(n)
43097         {
43098             this.flag.dom.className = 'flag '+n;
43099         },
43100         
43101         getValue : function()
43102         {
43103             var v = this.inputEl().getValue();
43104             if(this.dialCodeHolder) {
43105                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43106             }
43107             return v;
43108         },
43109         
43110         setValue : function(v)
43111         {
43112             var d = this.getDialCode(v);
43113             
43114             //invalid dial code
43115             if(v.length == 0 || !d || d.length == 0) {
43116                 if(this.rendered){
43117                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43118                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43119                 }
43120                 return;
43121             }
43122             
43123             //valid dial code
43124             this.setFlagClass(this.dialCodeMapping[d].iso2);
43125             this.setDialCode(d);
43126             this.inputEl().dom.value = v.replace('+'+d,'');
43127             this.hiddenEl().dom.value = this.getValue();
43128             
43129             this.validate();
43130         },
43131         
43132         getDialCode : function(v)
43133         {
43134             v = v ||  '';
43135             
43136             if (v.length == 0) {
43137                 return this.dialCodeHolder.dom.value;
43138             }
43139             
43140             var dialCode = "";
43141             if (v.charAt(0) != "+") {
43142                 return false;
43143             }
43144             var numericChars = "";
43145             for (var i = 1; i < v.length; i++) {
43146               var c = v.charAt(i);
43147               if (!isNaN(c)) {
43148                 numericChars += c;
43149                 if (this.dialCodeMapping[numericChars]) {
43150                   dialCode = v.substr(1, i);
43151                 }
43152                 if (numericChars.length == 4) {
43153                   break;
43154                 }
43155               }
43156             }
43157             return dialCode;
43158         },
43159         
43160         reset : function()
43161         {
43162             this.setValue(this.defaultDialCode);
43163             this.validate();
43164         },
43165         
43166         hiddenEl : function()
43167         {
43168             return this.el.select('input.hidden-tel-input',true).first();
43169         },
43170         
43171         // after setting val
43172         onKeyUp : function(e){
43173             this.setValue(this.getValue());
43174         },
43175         
43176         onKeyPress : function(e){
43177             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43178                 e.stopEvent();
43179             }
43180         }
43181         
43182 });
43183 /**
43184  * @class Roo.bootstrap.MoneyField
43185  * @extends Roo.bootstrap.ComboBox
43186  * Bootstrap MoneyField class
43187  * 
43188  * @constructor
43189  * Create a new MoneyField.
43190  * @param {Object} config Configuration options
43191  */
43192
43193 Roo.bootstrap.MoneyField = function(config) {
43194     
43195     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43196     
43197 };
43198
43199 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43200     
43201     /**
43202      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43203      */
43204     allowDecimals : true,
43205     /**
43206      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43207      */
43208     decimalSeparator : ".",
43209     /**
43210      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43211      */
43212     decimalPrecision : 0,
43213     /**
43214      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43215      */
43216     allowNegative : true,
43217     /**
43218      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43219      */
43220     allowZero: true,
43221     /**
43222      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43223      */
43224     minValue : Number.NEGATIVE_INFINITY,
43225     /**
43226      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43227      */
43228     maxValue : Number.MAX_VALUE,
43229     /**
43230      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43231      */
43232     minText : "The minimum value for this field is {0}",
43233     /**
43234      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43235      */
43236     maxText : "The maximum value for this field is {0}",
43237     /**
43238      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43239      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43240      */
43241     nanText : "{0} is not a valid number",
43242     /**
43243      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43244      */
43245     castInt : true,
43246     /**
43247      * @cfg {String} defaults currency of the MoneyField
43248      * value should be in lkey
43249      */
43250     defaultCurrency : false,
43251     /**
43252      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43253      */
43254     thousandsDelimiter : false,
43255     /**
43256      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43257      */
43258     max_length: false,
43259     
43260     inputlg : 9,
43261     inputmd : 9,
43262     inputsm : 9,
43263     inputxs : 6,
43264     
43265     store : false,
43266     
43267     getAutoCreate : function()
43268     {
43269         var align = this.labelAlign || this.parentLabelAlign();
43270         
43271         var id = Roo.id();
43272
43273         var cfg = {
43274             cls: 'form-group',
43275             cn: []
43276         };
43277
43278         var input =  {
43279             tag: 'input',
43280             id : id,
43281             cls : 'form-control roo-money-amount-input',
43282             autocomplete: 'new-password'
43283         };
43284         
43285         var hiddenInput = {
43286             tag: 'input',
43287             type: 'hidden',
43288             id: Roo.id(),
43289             cls: 'hidden-number-input'
43290         };
43291         
43292         if(this.max_length) {
43293             input.maxlength = this.max_length; 
43294         }
43295         
43296         if (this.name) {
43297             hiddenInput.name = this.name;
43298         }
43299
43300         if (this.disabled) {
43301             input.disabled = true;
43302         }
43303
43304         var clg = 12 - this.inputlg;
43305         var cmd = 12 - this.inputmd;
43306         var csm = 12 - this.inputsm;
43307         var cxs = 12 - this.inputxs;
43308         
43309         var container = {
43310             tag : 'div',
43311             cls : 'row roo-money-field',
43312             cn : [
43313                 {
43314                     tag : 'div',
43315                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43316                     cn : [
43317                         {
43318                             tag : 'div',
43319                             cls: 'roo-select2-container input-group',
43320                             cn: [
43321                                 {
43322                                     tag : 'input',
43323                                     cls : 'form-control roo-money-currency-input',
43324                                     autocomplete: 'new-password',
43325                                     readOnly : 1,
43326                                     name : this.currencyName
43327                                 },
43328                                 {
43329                                     tag :'span',
43330                                     cls : 'input-group-addon',
43331                                     cn : [
43332                                         {
43333                                             tag: 'span',
43334                                             cls: 'caret'
43335                                         }
43336                                     ]
43337                                 }
43338                             ]
43339                         }
43340                     ]
43341                 },
43342                 {
43343                     tag : 'div',
43344                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43345                     cn : [
43346                         {
43347                             tag: 'div',
43348                             cls: this.hasFeedback ? 'has-feedback' : '',
43349                             cn: [
43350                                 input
43351                             ]
43352                         }
43353                     ]
43354                 }
43355             ]
43356             
43357         };
43358         
43359         if (this.fieldLabel.length) {
43360             var indicator = {
43361                 tag: 'i',
43362                 tooltip: 'This field is required'
43363             };
43364
43365             var label = {
43366                 tag: 'label',
43367                 'for':  id,
43368                 cls: 'control-label',
43369                 cn: []
43370             };
43371
43372             var label_text = {
43373                 tag: 'span',
43374                 html: this.fieldLabel
43375             };
43376
43377             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43378             label.cn = [
43379                 indicator,
43380                 label_text
43381             ];
43382
43383             if(this.indicatorpos == 'right') {
43384                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43385                 label.cn = [
43386                     label_text,
43387                     indicator
43388                 ];
43389             }
43390
43391             if(align == 'left') {
43392                 container = {
43393                     tag: 'div',
43394                     cn: [
43395                         container
43396                     ]
43397                 };
43398
43399                 if(this.labelWidth > 12){
43400                     label.style = "width: " + this.labelWidth + 'px';
43401                 }
43402                 if(this.labelWidth < 13 && this.labelmd == 0){
43403                     this.labelmd = this.labelWidth;
43404                 }
43405                 if(this.labellg > 0){
43406                     label.cls += ' col-lg-' + this.labellg;
43407                     input.cls += ' col-lg-' + (12 - this.labellg);
43408                 }
43409                 if(this.labelmd > 0){
43410                     label.cls += ' col-md-' + this.labelmd;
43411                     container.cls += ' col-md-' + (12 - this.labelmd);
43412                 }
43413                 if(this.labelsm > 0){
43414                     label.cls += ' col-sm-' + this.labelsm;
43415                     container.cls += ' col-sm-' + (12 - this.labelsm);
43416                 }
43417                 if(this.labelxs > 0){
43418                     label.cls += ' col-xs-' + this.labelxs;
43419                     container.cls += ' col-xs-' + (12 - this.labelxs);
43420                 }
43421             }
43422         }
43423
43424         cfg.cn = [
43425             label,
43426             container,
43427             hiddenInput
43428         ];
43429         
43430         var settings = this;
43431
43432         ['xs','sm','md','lg'].map(function(size){
43433             if (settings[size]) {
43434                 cfg.cls += ' col-' + size + '-' + settings[size];
43435             }
43436         });
43437         
43438         return cfg;
43439     },
43440     
43441     initEvents : function()
43442     {
43443         this.indicator = this.indicatorEl();
43444         
43445         this.initCurrencyEvent();
43446         
43447         this.initNumberEvent();
43448     },
43449     
43450     initCurrencyEvent : function()
43451     {
43452         if (!this.store) {
43453             throw "can not find store for combo";
43454         }
43455         
43456         this.store = Roo.factory(this.store, Roo.data);
43457         this.store.parent = this;
43458         
43459         this.createList();
43460         
43461         this.triggerEl = this.el.select('.input-group-addon', true).first();
43462         
43463         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43464         
43465         var _this = this;
43466         
43467         (function(){
43468             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43469             _this.list.setWidth(lw);
43470         }).defer(100);
43471         
43472         this.list.on('mouseover', this.onViewOver, this);
43473         this.list.on('mousemove', this.onViewMove, this);
43474         this.list.on('scroll', this.onViewScroll, this);
43475         
43476         if(!this.tpl){
43477             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43478         }
43479         
43480         this.view = new Roo.View(this.list, this.tpl, {
43481             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43482         });
43483         
43484         this.view.on('click', this.onViewClick, this);
43485         
43486         this.store.on('beforeload', this.onBeforeLoad, this);
43487         this.store.on('load', this.onLoad, this);
43488         this.store.on('loadexception', this.onLoadException, this);
43489         
43490         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43491             "up" : function(e){
43492                 this.inKeyMode = true;
43493                 this.selectPrev();
43494             },
43495
43496             "down" : function(e){
43497                 if(!this.isExpanded()){
43498                     this.onTriggerClick();
43499                 }else{
43500                     this.inKeyMode = true;
43501                     this.selectNext();
43502                 }
43503             },
43504
43505             "enter" : function(e){
43506                 this.collapse();
43507                 
43508                 if(this.fireEvent("specialkey", this, e)){
43509                     this.onViewClick(false);
43510                 }
43511                 
43512                 return true;
43513             },
43514
43515             "esc" : function(e){
43516                 this.collapse();
43517             },
43518
43519             "tab" : function(e){
43520                 this.collapse();
43521                 
43522                 if(this.fireEvent("specialkey", this, e)){
43523                     this.onViewClick(false);
43524                 }
43525                 
43526                 return true;
43527             },
43528
43529             scope : this,
43530
43531             doRelay : function(foo, bar, hname){
43532                 if(hname == 'down' || this.scope.isExpanded()){
43533                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43534                 }
43535                 return true;
43536             },
43537
43538             forceKeyDown: true
43539         });
43540         
43541         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43542         
43543     },
43544     
43545     initNumberEvent : function(e)
43546     {
43547         this.inputEl().on("keydown" , this.fireKey,  this);
43548         this.inputEl().on("focus", this.onFocus,  this);
43549         this.inputEl().on("blur", this.onBlur,  this);
43550         
43551         this.inputEl().relayEvent('keyup', this);
43552         
43553         if(this.indicator){
43554             this.indicator.addClass('invisible');
43555         }
43556  
43557         this.originalValue = this.getValue();
43558         
43559         if(this.validationEvent == 'keyup'){
43560             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43561             this.inputEl().on('keyup', this.filterValidation, this);
43562         }
43563         else if(this.validationEvent !== false){
43564             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43565         }
43566         
43567         if(this.selectOnFocus){
43568             this.on("focus", this.preFocus, this);
43569             
43570         }
43571         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43572             this.inputEl().on("keypress", this.filterKeys, this);
43573         } else {
43574             this.inputEl().relayEvent('keypress', this);
43575         }
43576         
43577         var allowed = "0123456789";
43578         
43579         if(this.allowDecimals){
43580             allowed += this.decimalSeparator;
43581         }
43582         
43583         if(this.allowNegative){
43584             allowed += "-";
43585         }
43586         
43587         if(this.thousandsDelimiter) {
43588             allowed += ",";
43589         }
43590         
43591         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43592         
43593         var keyPress = function(e){
43594             
43595             var k = e.getKey();
43596             
43597             var c = e.getCharCode();
43598             
43599             if(
43600                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43601                     allowed.indexOf(String.fromCharCode(c)) === -1
43602             ){
43603                 e.stopEvent();
43604                 return;
43605             }
43606             
43607             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43608                 return;
43609             }
43610             
43611             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43612                 e.stopEvent();
43613             }
43614         };
43615         
43616         this.inputEl().on("keypress", keyPress, this);
43617         
43618     },
43619     
43620     onTriggerClick : function(e)
43621     {   
43622         if(this.disabled){
43623             return;
43624         }
43625         
43626         this.page = 0;
43627         this.loadNext = false;
43628         
43629         if(this.isExpanded()){
43630             this.collapse();
43631             return;
43632         }
43633         
43634         this.hasFocus = true;
43635         
43636         if(this.triggerAction == 'all') {
43637             this.doQuery(this.allQuery, true);
43638             return;
43639         }
43640         
43641         this.doQuery(this.getRawValue());
43642     },
43643     
43644     getCurrency : function()
43645     {   
43646         var v = this.currencyEl().getValue();
43647         
43648         return v;
43649     },
43650     
43651     restrictHeight : function()
43652     {
43653         this.list.alignTo(this.currencyEl(), this.listAlign);
43654         this.list.alignTo(this.currencyEl(), this.listAlign);
43655     },
43656     
43657     onViewClick : function(view, doFocus, el, e)
43658     {
43659         var index = this.view.getSelectedIndexes()[0];
43660         
43661         var r = this.store.getAt(index);
43662         
43663         if(r){
43664             this.onSelect(r, index);
43665         }
43666     },
43667     
43668     onSelect : function(record, index){
43669         
43670         if(this.fireEvent('beforeselect', this, record, index) !== false){
43671         
43672             this.setFromCurrencyData(index > -1 ? record.data : false);
43673             
43674             this.collapse();
43675             
43676             this.fireEvent('select', this, record, index);
43677         }
43678     },
43679     
43680     setFromCurrencyData : function(o)
43681     {
43682         var currency = '';
43683         
43684         this.lastCurrency = o;
43685         
43686         if (this.currencyField) {
43687             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43688         } else {
43689             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43690         }
43691         
43692         this.lastSelectionText = currency;
43693         
43694         //setting default currency
43695         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43696             this.setCurrency(this.defaultCurrency);
43697             return;
43698         }
43699         
43700         this.setCurrency(currency);
43701     },
43702     
43703     setFromData : function(o)
43704     {
43705         var c = {};
43706         
43707         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43708         
43709         this.setFromCurrencyData(c);
43710         
43711         var value = '';
43712         
43713         if (this.name) {
43714             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43715         } else {
43716             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43717         }
43718         
43719         this.setValue(value);
43720         
43721     },
43722     
43723     setCurrency : function(v)
43724     {   
43725         this.currencyValue = v;
43726         
43727         if(this.rendered){
43728             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43729             this.validate();
43730         }
43731     },
43732     
43733     setValue : function(v)
43734     {
43735         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43736         
43737         this.value = v;
43738         
43739         if(this.rendered){
43740             
43741             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43742             
43743             this.inputEl().dom.value = (v == '') ? '' :
43744                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43745             
43746             if(!this.allowZero && v === '0') {
43747                 this.hiddenEl().dom.value = '';
43748                 this.inputEl().dom.value = '';
43749             }
43750             
43751             this.validate();
43752         }
43753     },
43754     
43755     getRawValue : function()
43756     {
43757         var v = this.inputEl().getValue();
43758         
43759         return v;
43760     },
43761     
43762     getValue : function()
43763     {
43764         return this.fixPrecision(this.parseValue(this.getRawValue()));
43765     },
43766     
43767     parseValue : function(value)
43768     {
43769         if(this.thousandsDelimiter) {
43770             value += "";
43771             r = new RegExp(",", "g");
43772             value = value.replace(r, "");
43773         }
43774         
43775         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43776         return isNaN(value) ? '' : value;
43777         
43778     },
43779     
43780     fixPrecision : function(value)
43781     {
43782         if(this.thousandsDelimiter) {
43783             value += "";
43784             r = new RegExp(",", "g");
43785             value = value.replace(r, "");
43786         }
43787         
43788         var nan = isNaN(value);
43789         
43790         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43791             return nan ? '' : value;
43792         }
43793         return parseFloat(value).toFixed(this.decimalPrecision);
43794     },
43795     
43796     decimalPrecisionFcn : function(v)
43797     {
43798         return Math.floor(v);
43799     },
43800     
43801     validateValue : function(value)
43802     {
43803         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43804             return false;
43805         }
43806         
43807         var num = this.parseValue(value);
43808         
43809         if(isNaN(num)){
43810             this.markInvalid(String.format(this.nanText, value));
43811             return false;
43812         }
43813         
43814         if(num < this.minValue){
43815             this.markInvalid(String.format(this.minText, this.minValue));
43816             return false;
43817         }
43818         
43819         if(num > this.maxValue){
43820             this.markInvalid(String.format(this.maxText, this.maxValue));
43821             return false;
43822         }
43823         
43824         return true;
43825     },
43826     
43827     validate : function()
43828     {
43829         if(this.disabled || this.allowBlank){
43830             this.markValid();
43831             return true;
43832         }
43833         
43834         var currency = this.getCurrency();
43835         
43836         if(this.validateValue(this.getRawValue()) && currency.length){
43837             this.markValid();
43838             return true;
43839         }
43840         
43841         this.markInvalid();
43842         return false;
43843     },
43844     
43845     getName: function()
43846     {
43847         return this.name;
43848     },
43849     
43850     beforeBlur : function()
43851     {
43852         if(!this.castInt){
43853             return;
43854         }
43855         
43856         var v = this.parseValue(this.getRawValue());
43857         
43858         if(v || v == 0){
43859             this.setValue(v);
43860         }
43861     },
43862     
43863     onBlur : function()
43864     {
43865         this.beforeBlur();
43866         
43867         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43868             //this.el.removeClass(this.focusClass);
43869         }
43870         
43871         this.hasFocus = false;
43872         
43873         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43874             this.validate();
43875         }
43876         
43877         var v = this.getValue();
43878         
43879         if(String(v) !== String(this.startValue)){
43880             this.fireEvent('change', this, v, this.startValue);
43881         }
43882         
43883         this.fireEvent("blur", this);
43884     },
43885     
43886     inputEl : function()
43887     {
43888         return this.el.select('.roo-money-amount-input', true).first();
43889     },
43890     
43891     currencyEl : function()
43892     {
43893         return this.el.select('.roo-money-currency-input', true).first();
43894     },
43895     
43896     hiddenEl : function()
43897     {
43898         return this.el.select('input.hidden-number-input',true).first();
43899     }
43900     
43901 });/**
43902  * @class Roo.bootstrap.BezierSignature
43903  * @extends Roo.bootstrap.Component
43904  * Bootstrap BezierSignature class
43905  * This script refer to:
43906  *    Title: Signature Pad
43907  *    Author: szimek
43908  *    Availability: https://github.com/szimek/signature_pad
43909  *
43910  * @constructor
43911  * Create a new BezierSignature
43912  * @param {Object} config The config object
43913  */
43914
43915 Roo.bootstrap.BezierSignature = function(config){
43916     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43917     this.addEvents({
43918         "resize" : true
43919     });
43920 };
43921
43922 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43923 {
43924      
43925     curve_data: [],
43926     
43927     is_empty: true,
43928     
43929     mouse_btn_down: true,
43930     
43931     /**
43932      * @cfg {int} canvas height
43933      */
43934     canvas_height: '200px',
43935     
43936     /**
43937      * @cfg {float|function} Radius of a single dot.
43938      */ 
43939     dot_size: false,
43940     
43941     /**
43942      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43943      */
43944     min_width: 0.5,
43945     
43946     /**
43947      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43948      */
43949     max_width: 2.5,
43950     
43951     /**
43952      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43953      */
43954     throttle: 16,
43955     
43956     /**
43957      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43958      */
43959     min_distance: 5,
43960     
43961     /**
43962      * @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.
43963      */
43964     bg_color: 'rgba(0, 0, 0, 0)',
43965     
43966     /**
43967      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43968      */
43969     dot_color: 'black',
43970     
43971     /**
43972      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43973      */ 
43974     velocity_filter_weight: 0.7,
43975     
43976     /**
43977      * @cfg {function} Callback when stroke begin. 
43978      */
43979     onBegin: false,
43980     
43981     /**
43982      * @cfg {function} Callback when stroke end.
43983      */
43984     onEnd: false,
43985     
43986     getAutoCreate : function()
43987     {
43988         var cls = 'roo-signature column';
43989         
43990         if(this.cls){
43991             cls += ' ' + this.cls;
43992         }
43993         
43994         var col_sizes = [
43995             'lg',
43996             'md',
43997             'sm',
43998             'xs'
43999         ];
44000         
44001         for(var i = 0; i < col_sizes.length; i++) {
44002             if(this[col_sizes[i]]) {
44003                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44004             }
44005         }
44006         
44007         var cfg = {
44008             tag: 'div',
44009             cls: cls,
44010             cn: [
44011                 {
44012                     tag: 'div',
44013                     cls: 'roo-signature-body',
44014                     cn: [
44015                         {
44016                             tag: 'canvas',
44017                             cls: 'roo-signature-body-canvas',
44018                             height: this.canvas_height,
44019                             width: this.canvas_width
44020                         }
44021                     ]
44022                 },
44023                 {
44024                     tag: 'input',
44025                     type: 'file',
44026                     style: 'display: none'
44027                 }
44028             ]
44029         };
44030         
44031         return cfg;
44032     },
44033     
44034     initEvents: function() 
44035     {
44036         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44037         
44038         var canvas = this.canvasEl();
44039         
44040         // mouse && touch event swapping...
44041         canvas.dom.style.touchAction = 'none';
44042         canvas.dom.style.msTouchAction = 'none';
44043         
44044         this.mouse_btn_down = false;
44045         canvas.on('mousedown', this._handleMouseDown, this);
44046         canvas.on('mousemove', this._handleMouseMove, this);
44047         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44048         
44049         if (window.PointerEvent) {
44050             canvas.on('pointerdown', this._handleMouseDown, this);
44051             canvas.on('pointermove', this._handleMouseMove, this);
44052             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44053         }
44054         
44055         if ('ontouchstart' in window) {
44056             canvas.on('touchstart', this._handleTouchStart, this);
44057             canvas.on('touchmove', this._handleTouchMove, this);
44058             canvas.on('touchend', this._handleTouchEnd, this);
44059         }
44060         
44061         Roo.EventManager.onWindowResize(this.resize, this, true);
44062         
44063         // file input event
44064         this.fileEl().on('change', this.uploadImage, this);
44065         
44066         this.clear();
44067         
44068         this.resize();
44069     },
44070     
44071     resize: function(){
44072         
44073         var canvas = this.canvasEl().dom;
44074         var ctx = this.canvasElCtx();
44075         var img_data = false;
44076         
44077         if(canvas.width > 0) {
44078             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44079         }
44080         // setting canvas width will clean img data
44081         canvas.width = 0;
44082         
44083         var style = window.getComputedStyle ? 
44084             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44085             
44086         var padding_left = parseInt(style.paddingLeft) || 0;
44087         var padding_right = parseInt(style.paddingRight) || 0;
44088         
44089         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44090         
44091         if(img_data) {
44092             ctx.putImageData(img_data, 0, 0);
44093         }
44094     },
44095     
44096     _handleMouseDown: function(e)
44097     {
44098         if (e.browserEvent.which === 1) {
44099             this.mouse_btn_down = true;
44100             this.strokeBegin(e);
44101         }
44102     },
44103     
44104     _handleMouseMove: function (e)
44105     {
44106         if (this.mouse_btn_down) {
44107             this.strokeMoveUpdate(e);
44108         }
44109     },
44110     
44111     _handleMouseUp: function (e)
44112     {
44113         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44114             this.mouse_btn_down = false;
44115             this.strokeEnd(e);
44116         }
44117     },
44118     
44119     _handleTouchStart: function (e) {
44120         
44121         e.preventDefault();
44122         if (e.browserEvent.targetTouches.length === 1) {
44123             // var touch = e.browserEvent.changedTouches[0];
44124             // this.strokeBegin(touch);
44125             
44126              this.strokeBegin(e); // assume e catching the correct xy...
44127         }
44128     },
44129     
44130     _handleTouchMove: function (e) {
44131         e.preventDefault();
44132         // var touch = event.targetTouches[0];
44133         // _this._strokeMoveUpdate(touch);
44134         this.strokeMoveUpdate(e);
44135     },
44136     
44137     _handleTouchEnd: function (e) {
44138         var wasCanvasTouched = e.target === this.canvasEl().dom;
44139         if (wasCanvasTouched) {
44140             e.preventDefault();
44141             // var touch = event.changedTouches[0];
44142             // _this._strokeEnd(touch);
44143             this.strokeEnd(e);
44144         }
44145     },
44146     
44147     reset: function () {
44148         this._lastPoints = [];
44149         this._lastVelocity = 0;
44150         this._lastWidth = (this.min_width + this.max_width) / 2;
44151         this.canvasElCtx().fillStyle = this.dot_color;
44152     },
44153     
44154     strokeMoveUpdate: function(e)
44155     {
44156         this.strokeUpdate(e);
44157         
44158         if (this.throttle) {
44159             this.throttleStroke(this.strokeUpdate, this.throttle);
44160         }
44161         else {
44162             this.strokeUpdate(e);
44163         }
44164     },
44165     
44166     strokeBegin: function(e)
44167     {
44168         var newPointGroup = {
44169             color: this.dot_color,
44170             points: []
44171         };
44172         
44173         if (typeof this.onBegin === 'function') {
44174             this.onBegin(e);
44175         }
44176         
44177         this.curve_data.push(newPointGroup);
44178         this.reset();
44179         this.strokeUpdate(e);
44180     },
44181     
44182     strokeUpdate: function(e)
44183     {
44184         var rect = this.canvasEl().dom.getBoundingClientRect();
44185         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44186         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44187         var lastPoints = lastPointGroup.points;
44188         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44189         var isLastPointTooClose = lastPoint
44190             ? point.distanceTo(lastPoint) <= this.min_distance
44191             : false;
44192         var color = lastPointGroup.color;
44193         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44194             var curve = this.addPoint(point);
44195             if (!lastPoint) {
44196                 this.drawDot({color: color, point: point});
44197             }
44198             else if (curve) {
44199                 this.drawCurve({color: color, curve: curve});
44200             }
44201             lastPoints.push({
44202                 time: point.time,
44203                 x: point.x,
44204                 y: point.y
44205             });
44206         }
44207     },
44208     
44209     strokeEnd: function(e)
44210     {
44211         this.strokeUpdate(e);
44212         if (typeof this.onEnd === 'function') {
44213             this.onEnd(e);
44214         }
44215     },
44216     
44217     addPoint:  function (point) {
44218         var _lastPoints = this._lastPoints;
44219         _lastPoints.push(point);
44220         if (_lastPoints.length > 2) {
44221             if (_lastPoints.length === 3) {
44222                 _lastPoints.unshift(_lastPoints[0]);
44223             }
44224             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44225             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44226             _lastPoints.shift();
44227             return curve;
44228         }
44229         return null;
44230     },
44231     
44232     calculateCurveWidths: function (startPoint, endPoint) {
44233         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44234             (1 - this.velocity_filter_weight) * this._lastVelocity;
44235
44236         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44237         var widths = {
44238             end: newWidth,
44239             start: this._lastWidth
44240         };
44241         
44242         this._lastVelocity = velocity;
44243         this._lastWidth = newWidth;
44244         return widths;
44245     },
44246     
44247     drawDot: function (_a) {
44248         var color = _a.color, point = _a.point;
44249         var ctx = this.canvasElCtx();
44250         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44251         ctx.beginPath();
44252         this.drawCurveSegment(point.x, point.y, width);
44253         ctx.closePath();
44254         ctx.fillStyle = color;
44255         ctx.fill();
44256     },
44257     
44258     drawCurve: function (_a) {
44259         var color = _a.color, curve = _a.curve;
44260         var ctx = this.canvasElCtx();
44261         var widthDelta = curve.endWidth - curve.startWidth;
44262         var drawSteps = Math.floor(curve.length()) * 2;
44263         ctx.beginPath();
44264         ctx.fillStyle = color;
44265         for (var i = 0; i < drawSteps; i += 1) {
44266         var t = i / drawSteps;
44267         var tt = t * t;
44268         var ttt = tt * t;
44269         var u = 1 - t;
44270         var uu = u * u;
44271         var uuu = uu * u;
44272         var x = uuu * curve.startPoint.x;
44273         x += 3 * uu * t * curve.control1.x;
44274         x += 3 * u * tt * curve.control2.x;
44275         x += ttt * curve.endPoint.x;
44276         var y = uuu * curve.startPoint.y;
44277         y += 3 * uu * t * curve.control1.y;
44278         y += 3 * u * tt * curve.control2.y;
44279         y += ttt * curve.endPoint.y;
44280         var width = curve.startWidth + ttt * widthDelta;
44281         this.drawCurveSegment(x, y, width);
44282         }
44283         ctx.closePath();
44284         ctx.fill();
44285     },
44286     
44287     drawCurveSegment: function (x, y, width) {
44288         var ctx = this.canvasElCtx();
44289         ctx.moveTo(x, y);
44290         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44291         this.is_empty = false;
44292     },
44293     
44294     clear: function()
44295     {
44296         var ctx = this.canvasElCtx();
44297         var canvas = this.canvasEl().dom;
44298         ctx.fillStyle = this.bg_color;
44299         ctx.clearRect(0, 0, canvas.width, canvas.height);
44300         ctx.fillRect(0, 0, canvas.width, canvas.height);
44301         this.curve_data = [];
44302         this.reset();
44303         this.is_empty = true;
44304     },
44305     
44306     fileEl: function()
44307     {
44308         return  this.el.select('input',true).first();
44309     },
44310     
44311     canvasEl: function()
44312     {
44313         return this.el.select('canvas',true).first();
44314     },
44315     
44316     canvasElCtx: function()
44317     {
44318         return this.el.select('canvas',true).first().dom.getContext('2d');
44319     },
44320     
44321     getImage: function(type)
44322     {
44323         if(this.is_empty) {
44324             return false;
44325         }
44326         
44327         // encryption ?
44328         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44329     },
44330     
44331     drawFromImage: function(img_src)
44332     {
44333         var img = new Image();
44334         
44335         img.onload = function(){
44336             this.canvasElCtx().drawImage(img, 0, 0);
44337         }.bind(this);
44338         
44339         img.src = img_src;
44340         
44341         this.is_empty = false;
44342     },
44343     
44344     selectImage: function()
44345     {
44346         this.fileEl().dom.click();
44347     },
44348     
44349     uploadImage: function(e)
44350     {
44351         var reader = new FileReader();
44352         
44353         reader.onload = function(e){
44354             var img = new Image();
44355             img.onload = function(){
44356                 this.reset();
44357                 this.canvasElCtx().drawImage(img, 0, 0);
44358             }.bind(this);
44359             img.src = e.target.result;
44360         }.bind(this);
44361         
44362         reader.readAsDataURL(e.target.files[0]);
44363     },
44364     
44365     // Bezier Point Constructor
44366     Point: (function () {
44367         function Point(x, y, time) {
44368             this.x = x;
44369             this.y = y;
44370             this.time = time || Date.now();
44371         }
44372         Point.prototype.distanceTo = function (start) {
44373             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44374         };
44375         Point.prototype.equals = function (other) {
44376             return this.x === other.x && this.y === other.y && this.time === other.time;
44377         };
44378         Point.prototype.velocityFrom = function (start) {
44379             return this.time !== start.time
44380             ? this.distanceTo(start) / (this.time - start.time)
44381             : 0;
44382         };
44383         return Point;
44384     }()),
44385     
44386     
44387     // Bezier Constructor
44388     Bezier: (function () {
44389         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44390             this.startPoint = startPoint;
44391             this.control2 = control2;
44392             this.control1 = control1;
44393             this.endPoint = endPoint;
44394             this.startWidth = startWidth;
44395             this.endWidth = endWidth;
44396         }
44397         Bezier.fromPoints = function (points, widths, scope) {
44398             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44399             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44400             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44401         };
44402         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44403             var dx1 = s1.x - s2.x;
44404             var dy1 = s1.y - s2.y;
44405             var dx2 = s2.x - s3.x;
44406             var dy2 = s2.y - s3.y;
44407             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44408             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44409             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44410             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44411             var dxm = m1.x - m2.x;
44412             var dym = m1.y - m2.y;
44413             var k = l2 / (l1 + l2);
44414             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44415             var tx = s2.x - cm.x;
44416             var ty = s2.y - cm.y;
44417             return {
44418                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44419                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44420             };
44421         };
44422         Bezier.prototype.length = function () {
44423             var steps = 10;
44424             var length = 0;
44425             var px;
44426             var py;
44427             for (var i = 0; i <= steps; i += 1) {
44428                 var t = i / steps;
44429                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44430                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44431                 if (i > 0) {
44432                     var xdiff = cx - px;
44433                     var ydiff = cy - py;
44434                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44435                 }
44436                 px = cx;
44437                 py = cy;
44438             }
44439             return length;
44440         };
44441         Bezier.prototype.point = function (t, start, c1, c2, end) {
44442             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44443             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44444             + (3.0 * c2 * (1.0 - t) * t * t)
44445             + (end * t * t * t);
44446         };
44447         return Bezier;
44448     }()),
44449     
44450     throttleStroke: function(fn, wait) {
44451       if (wait === void 0) { wait = 250; }
44452       var previous = 0;
44453       var timeout = null;
44454       var result;
44455       var storedContext;
44456       var storedArgs;
44457       var later = function () {
44458           previous = Date.now();
44459           timeout = null;
44460           result = fn.apply(storedContext, storedArgs);
44461           if (!timeout) {
44462               storedContext = null;
44463               storedArgs = [];
44464           }
44465       };
44466       return function wrapper() {
44467           var args = [];
44468           for (var _i = 0; _i < arguments.length; _i++) {
44469               args[_i] = arguments[_i];
44470           }
44471           var now = Date.now();
44472           var remaining = wait - (now - previous);
44473           storedContext = this;
44474           storedArgs = args;
44475           if (remaining <= 0 || remaining > wait) {
44476               if (timeout) {
44477                   clearTimeout(timeout);
44478                   timeout = null;
44479               }
44480               previous = now;
44481               result = fn.apply(storedContext, storedArgs);
44482               if (!timeout) {
44483                   storedContext = null;
44484                   storedArgs = [];
44485               }
44486           }
44487           else if (!timeout) {
44488               timeout = window.setTimeout(later, remaining);
44489           }
44490           return result;
44491       };
44492   }
44493   
44494 });
44495
44496  
44497
44498