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         
12757         var _t = this;
12758         var reader = new FileReader();
12759         reader.onloadend = function(evt) {  
12760             data.srcdata =  evt.target.result;
12761             _t.updateInput();
12762         };
12763         reader.readAsDataURL(data.src);
12764         
12765         
12766         
12767     },
12768     removeCard : function(id)
12769     {
12770         
12771         var card  = this.fileCollection.get(id);
12772         card.data.is_deleted = 1;
12773         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12774         this.fileCollection.remove(card);
12775         //this.items = this.items.filter(function(e) { return e != card });
12776         // dont' really need ot update items.
12777         card.el.dom.parentNode.removeChild(card.el.dom);
12778         
12779     },
12780     reset: function()
12781     {
12782         this.fileCollection.each(function(card) {
12783             card.el.dom.parentNode.removeChild(card.el.dom);    
12784         });
12785         this.fileCollection.clear();
12786         this.updateInput();
12787     },
12788     
12789     updateInput : function()
12790     {
12791          var data = [];
12792         this.fileCollection.forEach(function(e) {
12793             data.push(e.data);
12794             
12795         });
12796         this.inputEl().dom.value = JSON.stringify(data);
12797         
12798         
12799         
12800     }
12801     
12802     
12803 });
12804
12805
12806 Roo.bootstrap.CardUploader.ID = -1;/*
12807  * Based on:
12808  * Ext JS Library 1.1.1
12809  * Copyright(c) 2006-2007, Ext JS, LLC.
12810  *
12811  * Originally Released Under LGPL - original licence link has changed is not relivant.
12812  *
12813  * Fork - LGPL
12814  * <script type="text/javascript">
12815  */
12816
12817
12818 /**
12819  * @class Roo.data.SortTypes
12820  * @singleton
12821  * Defines the default sorting (casting?) comparison functions used when sorting data.
12822  */
12823 Roo.data.SortTypes = {
12824     /**
12825      * Default sort that does nothing
12826      * @param {Mixed} s The value being converted
12827      * @return {Mixed} The comparison value
12828      */
12829     none : function(s){
12830         return s;
12831     },
12832     
12833     /**
12834      * The regular expression used to strip tags
12835      * @type {RegExp}
12836      * @property
12837      */
12838     stripTagsRE : /<\/?[^>]+>/gi,
12839     
12840     /**
12841      * Strips all HTML tags to sort on text only
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asText : function(s){
12846         return String(s).replace(this.stripTagsRE, "");
12847     },
12848     
12849     /**
12850      * Strips all HTML tags to sort on text only - Case insensitive
12851      * @param {Mixed} s The value being converted
12852      * @return {String} The comparison value
12853      */
12854     asUCText : function(s){
12855         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12856     },
12857     
12858     /**
12859      * Case insensitive string
12860      * @param {Mixed} s The value being converted
12861      * @return {String} The comparison value
12862      */
12863     asUCString : function(s) {
12864         return String(s).toUpperCase();
12865     },
12866     
12867     /**
12868      * Date sorting
12869      * @param {Mixed} s The value being converted
12870      * @return {Number} The comparison value
12871      */
12872     asDate : function(s) {
12873         if(!s){
12874             return 0;
12875         }
12876         if(s instanceof Date){
12877             return s.getTime();
12878         }
12879         return Date.parse(String(s));
12880     },
12881     
12882     /**
12883      * Float sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Float} The comparison value
12886      */
12887     asFloat : function(s) {
12888         var val = parseFloat(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     },
12894     
12895     /**
12896      * Integer sorting
12897      * @param {Mixed} s The value being converted
12898      * @return {Number} The comparison value
12899      */
12900     asInt : function(s) {
12901         var val = parseInt(String(s).replace(/,/g, ""));
12902         if(isNaN(val)) {
12903             val = 0;
12904         }
12905         return val;
12906     }
12907 };/*
12908  * Based on:
12909  * Ext JS Library 1.1.1
12910  * Copyright(c) 2006-2007, Ext JS, LLC.
12911  *
12912  * Originally Released Under LGPL - original licence link has changed is not relivant.
12913  *
12914  * Fork - LGPL
12915  * <script type="text/javascript">
12916  */
12917
12918 /**
12919 * @class Roo.data.Record
12920  * Instances of this class encapsulate both record <em>definition</em> information, and record
12921  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12922  * to access Records cached in an {@link Roo.data.Store} object.<br>
12923  * <p>
12924  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12925  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12926  * objects.<br>
12927  * <p>
12928  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12929  * @constructor
12930  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12931  * {@link #create}. The parameters are the same.
12932  * @param {Array} data An associative Array of data values keyed by the field name.
12933  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12934  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12935  * not specified an integer id is generated.
12936  */
12937 Roo.data.Record = function(data, id){
12938     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12939     this.data = data;
12940 };
12941
12942 /**
12943  * Generate a constructor for a specific record layout.
12944  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12945  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12946  * Each field definition object may contain the following properties: <ul>
12947  * <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,
12948  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12949  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12950  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12951  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12952  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12953  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12954  * this may be omitted.</p></li>
12955  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12956  * <ul><li>auto (Default, implies no conversion)</li>
12957  * <li>string</li>
12958  * <li>int</li>
12959  * <li>float</li>
12960  * <li>boolean</li>
12961  * <li>date</li></ul></p></li>
12962  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12963  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12964  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12965  * by the Reader into an object that will be stored in the Record. It is passed the
12966  * following parameters:<ul>
12967  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12968  * </ul></p></li>
12969  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12970  * </ul>
12971  * <br>usage:<br><pre><code>
12972 var TopicRecord = Roo.data.Record.create(
12973     {name: 'title', mapping: 'topic_title'},
12974     {name: 'author', mapping: 'username'},
12975     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12976     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12977     {name: 'lastPoster', mapping: 'user2'},
12978     {name: 'excerpt', mapping: 'post_text'}
12979 );
12980
12981 var myNewRecord = new TopicRecord({
12982     title: 'Do my job please',
12983     author: 'noobie',
12984     totalPosts: 1,
12985     lastPost: new Date(),
12986     lastPoster: 'Animal',
12987     excerpt: 'No way dude!'
12988 });
12989 myStore.add(myNewRecord);
12990 </code></pre>
12991  * @method create
12992  * @static
12993  */
12994 Roo.data.Record.create = function(o){
12995     var f = function(){
12996         f.superclass.constructor.apply(this, arguments);
12997     };
12998     Roo.extend(f, Roo.data.Record);
12999     var p = f.prototype;
13000     p.fields = new Roo.util.MixedCollection(false, function(field){
13001         return field.name;
13002     });
13003     for(var i = 0, len = o.length; i < len; i++){
13004         p.fields.add(new Roo.data.Field(o[i]));
13005     }
13006     f.getField = function(name){
13007         return p.fields.get(name);  
13008     };
13009     return f;
13010 };
13011
13012 Roo.data.Record.AUTO_ID = 1000;
13013 Roo.data.Record.EDIT = 'edit';
13014 Roo.data.Record.REJECT = 'reject';
13015 Roo.data.Record.COMMIT = 'commit';
13016
13017 Roo.data.Record.prototype = {
13018     /**
13019      * Readonly flag - true if this record has been modified.
13020      * @type Boolean
13021      */
13022     dirty : false,
13023     editing : false,
13024     error: null,
13025     modified: null,
13026
13027     // private
13028     join : function(store){
13029         this.store = store;
13030     },
13031
13032     /**
13033      * Set the named field to the specified value.
13034      * @param {String} name The name of the field to set.
13035      * @param {Object} value The value to set the field to.
13036      */
13037     set : function(name, value){
13038         if(this.data[name] == value){
13039             return;
13040         }
13041         this.dirty = true;
13042         if(!this.modified){
13043             this.modified = {};
13044         }
13045         if(typeof this.modified[name] == 'undefined'){
13046             this.modified[name] = this.data[name];
13047         }
13048         this.data[name] = value;
13049         if(!this.editing && this.store){
13050             this.store.afterEdit(this);
13051         }       
13052     },
13053
13054     /**
13055      * Get the value of the named field.
13056      * @param {String} name The name of the field to get the value of.
13057      * @return {Object} The value of the field.
13058      */
13059     get : function(name){
13060         return this.data[name]; 
13061     },
13062
13063     // private
13064     beginEdit : function(){
13065         this.editing = true;
13066         this.modified = {}; 
13067     },
13068
13069     // private
13070     cancelEdit : function(){
13071         this.editing = false;
13072         delete this.modified;
13073     },
13074
13075     // private
13076     endEdit : function(){
13077         this.editing = false;
13078         if(this.dirty && this.store){
13079             this.store.afterEdit(this);
13080         }
13081     },
13082
13083     /**
13084      * Usually called by the {@link Roo.data.Store} which owns the Record.
13085      * Rejects all changes made to the Record since either creation, or the last commit operation.
13086      * Modified fields are reverted to their original values.
13087      * <p>
13088      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13089      * of reject operations.
13090      */
13091     reject : function(){
13092         var m = this.modified;
13093         for(var n in m){
13094             if(typeof m[n] != "function"){
13095                 this.data[n] = m[n];
13096             }
13097         }
13098         this.dirty = false;
13099         delete this.modified;
13100         this.editing = false;
13101         if(this.store){
13102             this.store.afterReject(this);
13103         }
13104     },
13105
13106     /**
13107      * Usually called by the {@link Roo.data.Store} which owns the Record.
13108      * Commits all changes made to the Record since either creation, or the last commit operation.
13109      * <p>
13110      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13111      * of commit operations.
13112      */
13113     commit : function(){
13114         this.dirty = false;
13115         delete this.modified;
13116         this.editing = false;
13117         if(this.store){
13118             this.store.afterCommit(this);
13119         }
13120     },
13121
13122     // private
13123     hasError : function(){
13124         return this.error != null;
13125     },
13126
13127     // private
13128     clearError : function(){
13129         this.error = null;
13130     },
13131
13132     /**
13133      * Creates a copy of this record.
13134      * @param {String} id (optional) A new record id if you don't want to use this record's id
13135      * @return {Record}
13136      */
13137     copy : function(newId) {
13138         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13139     }
13140 };/*
13141  * Based on:
13142  * Ext JS Library 1.1.1
13143  * Copyright(c) 2006-2007, Ext JS, LLC.
13144  *
13145  * Originally Released Under LGPL - original licence link has changed is not relivant.
13146  *
13147  * Fork - LGPL
13148  * <script type="text/javascript">
13149  */
13150
13151
13152
13153 /**
13154  * @class Roo.data.Store
13155  * @extends Roo.util.Observable
13156  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13157  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13158  * <p>
13159  * 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
13160  * has no knowledge of the format of the data returned by the Proxy.<br>
13161  * <p>
13162  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13163  * instances from the data object. These records are cached and made available through accessor functions.
13164  * @constructor
13165  * Creates a new Store.
13166  * @param {Object} config A config object containing the objects needed for the Store to access data,
13167  * and read the data into Records.
13168  */
13169 Roo.data.Store = function(config){
13170     this.data = new Roo.util.MixedCollection(false);
13171     this.data.getKey = function(o){
13172         return o.id;
13173     };
13174     this.baseParams = {};
13175     // private
13176     this.paramNames = {
13177         "start" : "start",
13178         "limit" : "limit",
13179         "sort" : "sort",
13180         "dir" : "dir",
13181         "multisort" : "_multisort"
13182     };
13183
13184     if(config && config.data){
13185         this.inlineData = config.data;
13186         delete config.data;
13187     }
13188
13189     Roo.apply(this, config);
13190     
13191     if(this.reader){ // reader passed
13192         this.reader = Roo.factory(this.reader, Roo.data);
13193         this.reader.xmodule = this.xmodule || false;
13194         if(!this.recordType){
13195             this.recordType = this.reader.recordType;
13196         }
13197         if(this.reader.onMetaChange){
13198             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13199         }
13200     }
13201
13202     if(this.recordType){
13203         this.fields = this.recordType.prototype.fields;
13204     }
13205     this.modified = [];
13206
13207     this.addEvents({
13208         /**
13209          * @event datachanged
13210          * Fires when the data cache has changed, and a widget which is using this Store
13211          * as a Record cache should refresh its view.
13212          * @param {Store} this
13213          */
13214         datachanged : true,
13215         /**
13216          * @event metachange
13217          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13218          * @param {Store} this
13219          * @param {Object} meta The JSON metadata
13220          */
13221         metachange : true,
13222         /**
13223          * @event add
13224          * Fires when Records have been added to the Store
13225          * @param {Store} this
13226          * @param {Roo.data.Record[]} records The array of Records added
13227          * @param {Number} index The index at which the record(s) were added
13228          */
13229         add : true,
13230         /**
13231          * @event remove
13232          * Fires when a Record has been removed from the Store
13233          * @param {Store} this
13234          * @param {Roo.data.Record} record The Record that was removed
13235          * @param {Number} index The index at which the record was removed
13236          */
13237         remove : true,
13238         /**
13239          * @event update
13240          * Fires when a Record has been updated
13241          * @param {Store} this
13242          * @param {Roo.data.Record} record The Record that was updated
13243          * @param {String} operation The update operation being performed.  Value may be one of:
13244          * <pre><code>
13245  Roo.data.Record.EDIT
13246  Roo.data.Record.REJECT
13247  Roo.data.Record.COMMIT
13248          * </code></pre>
13249          */
13250         update : true,
13251         /**
13252          * @event clear
13253          * Fires when the data cache has been cleared.
13254          * @param {Store} this
13255          */
13256         clear : true,
13257         /**
13258          * @event beforeload
13259          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13260          * the load action will be canceled.
13261          * @param {Store} this
13262          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13263          */
13264         beforeload : true,
13265         /**
13266          * @event beforeloadadd
13267          * Fires after a new set of Records has been loaded.
13268          * @param {Store} this
13269          * @param {Roo.data.Record[]} records The Records that were loaded
13270          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13271          */
13272         beforeloadadd : true,
13273         /**
13274          * @event load
13275          * Fires after a new set of Records has been loaded, before they are added to the store.
13276          * @param {Store} this
13277          * @param {Roo.data.Record[]} records The Records that were loaded
13278          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13279          * @params {Object} return from reader
13280          */
13281         load : true,
13282         /**
13283          * @event loadexception
13284          * Fires if an exception occurs in the Proxy during loading.
13285          * Called with the signature of the Proxy's "loadexception" event.
13286          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13287          * 
13288          * @param {Proxy} 
13289          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13290          * @param {Object} load options 
13291          * @param {Object} jsonData from your request (normally this contains the Exception)
13292          */
13293         loadexception : true
13294     });
13295     
13296     if(this.proxy){
13297         this.proxy = Roo.factory(this.proxy, Roo.data);
13298         this.proxy.xmodule = this.xmodule || false;
13299         this.relayEvents(this.proxy,  ["loadexception"]);
13300     }
13301     this.sortToggle = {};
13302     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13303
13304     Roo.data.Store.superclass.constructor.call(this);
13305
13306     if(this.inlineData){
13307         this.loadData(this.inlineData);
13308         delete this.inlineData;
13309     }
13310 };
13311
13312 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13313      /**
13314     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13315     * without a remote query - used by combo/forms at present.
13316     */
13317     
13318     /**
13319     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13320     */
13321     /**
13322     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13323     */
13324     /**
13325     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13326     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13327     */
13328     /**
13329     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13330     * on any HTTP request
13331     */
13332     /**
13333     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13334     */
13335     /**
13336     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13337     */
13338     multiSort: false,
13339     /**
13340     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13341     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13342     */
13343     remoteSort : false,
13344
13345     /**
13346     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13347      * loaded or when a record is removed. (defaults to false).
13348     */
13349     pruneModifiedRecords : false,
13350
13351     // private
13352     lastOptions : null,
13353
13354     /**
13355      * Add Records to the Store and fires the add event.
13356      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13357      */
13358     add : function(records){
13359         records = [].concat(records);
13360         for(var i = 0, len = records.length; i < len; i++){
13361             records[i].join(this);
13362         }
13363         var index = this.data.length;
13364         this.data.addAll(records);
13365         this.fireEvent("add", this, records, index);
13366     },
13367
13368     /**
13369      * Remove a Record from the Store and fires the remove event.
13370      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13371      */
13372     remove : function(record){
13373         var index = this.data.indexOf(record);
13374         this.data.removeAt(index);
13375  
13376         if(this.pruneModifiedRecords){
13377             this.modified.remove(record);
13378         }
13379         this.fireEvent("remove", this, record, index);
13380     },
13381
13382     /**
13383      * Remove all Records from the Store and fires the clear event.
13384      */
13385     removeAll : function(){
13386         this.data.clear();
13387         if(this.pruneModifiedRecords){
13388             this.modified = [];
13389         }
13390         this.fireEvent("clear", this);
13391     },
13392
13393     /**
13394      * Inserts Records to the Store at the given index and fires the add event.
13395      * @param {Number} index The start index at which to insert the passed Records.
13396      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13397      */
13398     insert : function(index, records){
13399         records = [].concat(records);
13400         for(var i = 0, len = records.length; i < len; i++){
13401             this.data.insert(index, records[i]);
13402             records[i].join(this);
13403         }
13404         this.fireEvent("add", this, records, index);
13405     },
13406
13407     /**
13408      * Get the index within the cache of the passed Record.
13409      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13410      * @return {Number} The index of the passed Record. Returns -1 if not found.
13411      */
13412     indexOf : function(record){
13413         return this.data.indexOf(record);
13414     },
13415
13416     /**
13417      * Get the index within the cache of the Record with the passed id.
13418      * @param {String} id The id of the Record to find.
13419      * @return {Number} The index of the Record. Returns -1 if not found.
13420      */
13421     indexOfId : function(id){
13422         return this.data.indexOfKey(id);
13423     },
13424
13425     /**
13426      * Get the Record with the specified id.
13427      * @param {String} id The id of the Record to find.
13428      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13429      */
13430     getById : function(id){
13431         return this.data.key(id);
13432     },
13433
13434     /**
13435      * Get the Record at the specified index.
13436      * @param {Number} index The index of the Record to find.
13437      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13438      */
13439     getAt : function(index){
13440         return this.data.itemAt(index);
13441     },
13442
13443     /**
13444      * Returns a range of Records between specified indices.
13445      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13446      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13447      * @return {Roo.data.Record[]} An array of Records
13448      */
13449     getRange : function(start, end){
13450         return this.data.getRange(start, end);
13451     },
13452
13453     // private
13454     storeOptions : function(o){
13455         o = Roo.apply({}, o);
13456         delete o.callback;
13457         delete o.scope;
13458         this.lastOptions = o;
13459     },
13460
13461     /**
13462      * Loads the Record cache from the configured Proxy using the configured Reader.
13463      * <p>
13464      * If using remote paging, then the first load call must specify the <em>start</em>
13465      * and <em>limit</em> properties in the options.params property to establish the initial
13466      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13467      * <p>
13468      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13469      * and this call will return before the new data has been loaded. Perform any post-processing
13470      * in a callback function, or in a "load" event handler.</strong>
13471      * <p>
13472      * @param {Object} options An object containing properties which control loading options:<ul>
13473      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13474      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13475      * passed the following arguments:<ul>
13476      * <li>r : Roo.data.Record[]</li>
13477      * <li>options: Options object from the load call</li>
13478      * <li>success: Boolean success indicator</li></ul></li>
13479      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13480      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13481      * </ul>
13482      */
13483     load : function(options){
13484         options = options || {};
13485         if(this.fireEvent("beforeload", this, options) !== false){
13486             this.storeOptions(options);
13487             var p = Roo.apply(options.params || {}, this.baseParams);
13488             // if meta was not loaded from remote source.. try requesting it.
13489             if (!this.reader.metaFromRemote) {
13490                 p._requestMeta = 1;
13491             }
13492             if(this.sortInfo && this.remoteSort){
13493                 var pn = this.paramNames;
13494                 p[pn["sort"]] = this.sortInfo.field;
13495                 p[pn["dir"]] = this.sortInfo.direction;
13496             }
13497             if (this.multiSort) {
13498                 var pn = this.paramNames;
13499                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13500             }
13501             
13502             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13503         }
13504     },
13505
13506     /**
13507      * Reloads the Record cache from the configured Proxy using the configured Reader and
13508      * the options from the last load operation performed.
13509      * @param {Object} options (optional) An object containing properties which may override the options
13510      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13511      * the most recently used options are reused).
13512      */
13513     reload : function(options){
13514         this.load(Roo.applyIf(options||{}, this.lastOptions));
13515     },
13516
13517     // private
13518     // Called as a callback by the Reader during a load operation.
13519     loadRecords : function(o, options, success){
13520         if(!o || success === false){
13521             if(success !== false){
13522                 this.fireEvent("load", this, [], options, o);
13523             }
13524             if(options.callback){
13525                 options.callback.call(options.scope || this, [], options, false);
13526             }
13527             return;
13528         }
13529         // if data returned failure - throw an exception.
13530         if (o.success === false) {
13531             // show a message if no listener is registered.
13532             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13533                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13534             }
13535             // loadmask wil be hooked into this..
13536             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13537             return;
13538         }
13539         var r = o.records, t = o.totalRecords || r.length;
13540         
13541         this.fireEvent("beforeloadadd", this, r, options, o);
13542         
13543         if(!options || options.add !== true){
13544             if(this.pruneModifiedRecords){
13545                 this.modified = [];
13546             }
13547             for(var i = 0, len = r.length; i < len; i++){
13548                 r[i].join(this);
13549             }
13550             if(this.snapshot){
13551                 this.data = this.snapshot;
13552                 delete this.snapshot;
13553             }
13554             this.data.clear();
13555             this.data.addAll(r);
13556             this.totalLength = t;
13557             this.applySort();
13558             this.fireEvent("datachanged", this);
13559         }else{
13560             this.totalLength = Math.max(t, this.data.length+r.length);
13561             this.add(r);
13562         }
13563         
13564         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13565                 
13566             var e = new Roo.data.Record({});
13567
13568             e.set(this.parent.displayField, this.parent.emptyTitle);
13569             e.set(this.parent.valueField, '');
13570
13571             this.insert(0, e);
13572         }
13573             
13574         this.fireEvent("load", this, r, options, o);
13575         if(options.callback){
13576             options.callback.call(options.scope || this, r, options, true);
13577         }
13578     },
13579
13580
13581     /**
13582      * Loads data from a passed data block. A Reader which understands the format of the data
13583      * must have been configured in the constructor.
13584      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13585      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13586      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13587      */
13588     loadData : function(o, append){
13589         var r = this.reader.readRecords(o);
13590         this.loadRecords(r, {add: append}, true);
13591     },
13592     
13593      /**
13594      * using 'cn' the nested child reader read the child array into it's child stores.
13595      * @param {Object} rec The record with a 'children array
13596      */
13597     loadDataFromChildren : function(rec)
13598     {
13599         this.loadData(this.reader.toLoadData(rec));
13600     },
13601     
13602
13603     /**
13604      * Gets the number of cached records.
13605      * <p>
13606      * <em>If using paging, this may not be the total size of the dataset. If the data object
13607      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13608      * the data set size</em>
13609      */
13610     getCount : function(){
13611         return this.data.length || 0;
13612     },
13613
13614     /**
13615      * Gets the total number of records in the dataset as returned by the server.
13616      * <p>
13617      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13618      * the dataset size</em>
13619      */
13620     getTotalCount : function(){
13621         return this.totalLength || 0;
13622     },
13623
13624     /**
13625      * Returns the sort state of the Store as an object with two properties:
13626      * <pre><code>
13627  field {String} The name of the field by which the Records are sorted
13628  direction {String} The sort order, "ASC" or "DESC"
13629      * </code></pre>
13630      */
13631     getSortState : function(){
13632         return this.sortInfo;
13633     },
13634
13635     // private
13636     applySort : function(){
13637         if(this.sortInfo && !this.remoteSort){
13638             var s = this.sortInfo, f = s.field;
13639             var st = this.fields.get(f).sortType;
13640             var fn = function(r1, r2){
13641                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13642                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13643             };
13644             this.data.sort(s.direction, fn);
13645             if(this.snapshot && this.snapshot != this.data){
13646                 this.snapshot.sort(s.direction, fn);
13647             }
13648         }
13649     },
13650
13651     /**
13652      * Sets the default sort column and order to be used by the next load operation.
13653      * @param {String} fieldName The name of the field to sort by.
13654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13655      */
13656     setDefaultSort : function(field, dir){
13657         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13658     },
13659
13660     /**
13661      * Sort the Records.
13662      * If remote sorting is used, the sort is performed on the server, and the cache is
13663      * reloaded. If local sorting is used, the cache is sorted internally.
13664      * @param {String} fieldName The name of the field to sort by.
13665      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13666      */
13667     sort : function(fieldName, dir){
13668         var f = this.fields.get(fieldName);
13669         if(!dir){
13670             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13671             
13672             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13673                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13674             }else{
13675                 dir = f.sortDir;
13676             }
13677         }
13678         this.sortToggle[f.name] = dir;
13679         this.sortInfo = {field: f.name, direction: dir};
13680         if(!this.remoteSort){
13681             this.applySort();
13682             this.fireEvent("datachanged", this);
13683         }else{
13684             this.load(this.lastOptions);
13685         }
13686     },
13687
13688     /**
13689      * Calls the specified function for each of the Records in the cache.
13690      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13691      * Returning <em>false</em> aborts and exits the iteration.
13692      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13693      */
13694     each : function(fn, scope){
13695         this.data.each(fn, scope);
13696     },
13697
13698     /**
13699      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13700      * (e.g., during paging).
13701      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13702      */
13703     getModifiedRecords : function(){
13704         return this.modified;
13705     },
13706
13707     // private
13708     createFilterFn : function(property, value, anyMatch){
13709         if(!value.exec){ // not a regex
13710             value = String(value);
13711             if(value.length == 0){
13712                 return false;
13713             }
13714             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13715         }
13716         return function(r){
13717             return value.test(r.data[property]);
13718         };
13719     },
13720
13721     /**
13722      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13723      * @param {String} property A field on your records
13724      * @param {Number} start The record index to start at (defaults to 0)
13725      * @param {Number} end The last record index to include (defaults to length - 1)
13726      * @return {Number} The sum
13727      */
13728     sum : function(property, start, end){
13729         var rs = this.data.items, v = 0;
13730         start = start || 0;
13731         end = (end || end === 0) ? end : rs.length-1;
13732
13733         for(var i = start; i <= end; i++){
13734             v += (rs[i].data[property] || 0);
13735         }
13736         return v;
13737     },
13738
13739     /**
13740      * Filter the records by a specified property.
13741      * @param {String} field A field on your records
13742      * @param {String/RegExp} value Either a string that the field
13743      * should start with or a RegExp to test against the field
13744      * @param {Boolean} anyMatch True to match any part not just the beginning
13745      */
13746     filter : function(property, value, anyMatch){
13747         var fn = this.createFilterFn(property, value, anyMatch);
13748         return fn ? this.filterBy(fn) : this.clearFilter();
13749     },
13750
13751     /**
13752      * Filter by a function. The specified function will be called with each
13753      * record in this data source. If the function returns true the record is included,
13754      * otherwise it is filtered.
13755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13756      * @param {Object} scope (optional) The scope of the function (defaults to this)
13757      */
13758     filterBy : function(fn, scope){
13759         this.snapshot = this.snapshot || this.data;
13760         this.data = this.queryBy(fn, scope||this);
13761         this.fireEvent("datachanged", this);
13762     },
13763
13764     /**
13765      * Query the records by a specified property.
13766      * @param {String} field A field on your records
13767      * @param {String/RegExp} value Either a string that the field
13768      * should start with or a RegExp to test against the field
13769      * @param {Boolean} anyMatch True to match any part not just the beginning
13770      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      */
13772     query : function(property, value, anyMatch){
13773         var fn = this.createFilterFn(property, value, anyMatch);
13774         return fn ? this.queryBy(fn) : this.data.clone();
13775     },
13776
13777     /**
13778      * Query by a function. The specified function will be called with each
13779      * record in this data source. If the function returns true the record is included
13780      * in the results.
13781      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13782      * @param {Object} scope (optional) The scope of the function (defaults to this)
13783       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13784      **/
13785     queryBy : function(fn, scope){
13786         var data = this.snapshot || this.data;
13787         return data.filterBy(fn, scope||this);
13788     },
13789
13790     /**
13791      * Collects unique values for a particular dataIndex from this store.
13792      * @param {String} dataIndex The property to collect
13793      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13794      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13795      * @return {Array} An array of the unique values
13796      **/
13797     collect : function(dataIndex, allowNull, bypassFilter){
13798         var d = (bypassFilter === true && this.snapshot) ?
13799                 this.snapshot.items : this.data.items;
13800         var v, sv, r = [], l = {};
13801         for(var i = 0, len = d.length; i < len; i++){
13802             v = d[i].data[dataIndex];
13803             sv = String(v);
13804             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13805                 l[sv] = true;
13806                 r[r.length] = v;
13807             }
13808         }
13809         return r;
13810     },
13811
13812     /**
13813      * Revert to a view of the Record cache with no filtering applied.
13814      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13815      */
13816     clearFilter : function(suppressEvent){
13817         if(this.snapshot && this.snapshot != this.data){
13818             this.data = this.snapshot;
13819             delete this.snapshot;
13820             if(suppressEvent !== true){
13821                 this.fireEvent("datachanged", this);
13822             }
13823         }
13824     },
13825
13826     // private
13827     afterEdit : function(record){
13828         if(this.modified.indexOf(record) == -1){
13829             this.modified.push(record);
13830         }
13831         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13832     },
13833     
13834     // private
13835     afterReject : function(record){
13836         this.modified.remove(record);
13837         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13838     },
13839
13840     // private
13841     afterCommit : function(record){
13842         this.modified.remove(record);
13843         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13844     },
13845
13846     /**
13847      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13848      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13849      */
13850     commitChanges : function(){
13851         var m = this.modified.slice(0);
13852         this.modified = [];
13853         for(var i = 0, len = m.length; i < len; i++){
13854             m[i].commit();
13855         }
13856     },
13857
13858     /**
13859      * Cancel outstanding changes on all changed records.
13860      */
13861     rejectChanges : function(){
13862         var m = this.modified.slice(0);
13863         this.modified = [];
13864         for(var i = 0, len = m.length; i < len; i++){
13865             m[i].reject();
13866         }
13867     },
13868
13869     onMetaChange : function(meta, rtype, o){
13870         this.recordType = rtype;
13871         this.fields = rtype.prototype.fields;
13872         delete this.snapshot;
13873         this.sortInfo = meta.sortInfo || this.sortInfo;
13874         this.modified = [];
13875         this.fireEvent('metachange', this, this.reader.meta);
13876     },
13877     
13878     moveIndex : function(data, type)
13879     {
13880         var index = this.indexOf(data);
13881         
13882         var newIndex = index + type;
13883         
13884         this.remove(data);
13885         
13886         this.insert(newIndex, data);
13887         
13888     }
13889 });/*
13890  * Based on:
13891  * Ext JS Library 1.1.1
13892  * Copyright(c) 2006-2007, Ext JS, LLC.
13893  *
13894  * Originally Released Under LGPL - original licence link has changed is not relivant.
13895  *
13896  * Fork - LGPL
13897  * <script type="text/javascript">
13898  */
13899
13900 /**
13901  * @class Roo.data.SimpleStore
13902  * @extends Roo.data.Store
13903  * Small helper class to make creating Stores from Array data easier.
13904  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13905  * @cfg {Array} fields An array of field definition objects, or field name strings.
13906  * @cfg {Object} an existing reader (eg. copied from another store)
13907  * @cfg {Array} data The multi-dimensional array of data
13908  * @constructor
13909  * @param {Object} config
13910  */
13911 Roo.data.SimpleStore = function(config)
13912 {
13913     Roo.data.SimpleStore.superclass.constructor.call(this, {
13914         isLocal : true,
13915         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13916                 id: config.id
13917             },
13918             Roo.data.Record.create(config.fields)
13919         ),
13920         proxy : new Roo.data.MemoryProxy(config.data)
13921     });
13922     this.load();
13923 };
13924 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13925  * Based on:
13926  * Ext JS Library 1.1.1
13927  * Copyright(c) 2006-2007, Ext JS, LLC.
13928  *
13929  * Originally Released Under LGPL - original licence link has changed is not relivant.
13930  *
13931  * Fork - LGPL
13932  * <script type="text/javascript">
13933  */
13934
13935 /**
13936 /**
13937  * @extends Roo.data.Store
13938  * @class Roo.data.JsonStore
13939  * Small helper class to make creating Stores for JSON data easier. <br/>
13940 <pre><code>
13941 var store = new Roo.data.JsonStore({
13942     url: 'get-images.php',
13943     root: 'images',
13944     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13945 });
13946 </code></pre>
13947  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13948  * JsonReader and HttpProxy (unless inline data is provided).</b>
13949  * @cfg {Array} fields An array of field definition objects, or field name strings.
13950  * @constructor
13951  * @param {Object} config
13952  */
13953 Roo.data.JsonStore = function(c){
13954     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13955         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13956         reader: new Roo.data.JsonReader(c, c.fields)
13957     }));
13958 };
13959 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969
13970  
13971 Roo.data.Field = function(config){
13972     if(typeof config == "string"){
13973         config = {name: config};
13974     }
13975     Roo.apply(this, config);
13976     
13977     if(!this.type){
13978         this.type = "auto";
13979     }
13980     
13981     var st = Roo.data.SortTypes;
13982     // named sortTypes are supported, here we look them up
13983     if(typeof this.sortType == "string"){
13984         this.sortType = st[this.sortType];
13985     }
13986     
13987     // set default sortType for strings and dates
13988     if(!this.sortType){
13989         switch(this.type){
13990             case "string":
13991                 this.sortType = st.asUCString;
13992                 break;
13993             case "date":
13994                 this.sortType = st.asDate;
13995                 break;
13996             default:
13997                 this.sortType = st.none;
13998         }
13999     }
14000
14001     // define once
14002     var stripRe = /[\$,%]/g;
14003
14004     // prebuilt conversion function for this field, instead of
14005     // switching every time we're reading a value
14006     if(!this.convert){
14007         var cv, dateFormat = this.dateFormat;
14008         switch(this.type){
14009             case "":
14010             case "auto":
14011             case undefined:
14012                 cv = function(v){ return v; };
14013                 break;
14014             case "string":
14015                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14016                 break;
14017             case "int":
14018                 cv = function(v){
14019                     return v !== undefined && v !== null && v !== '' ?
14020                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14021                     };
14022                 break;
14023             case "float":
14024                 cv = function(v){
14025                     return v !== undefined && v !== null && v !== '' ?
14026                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14027                     };
14028                 break;
14029             case "bool":
14030             case "boolean":
14031                 cv = function(v){ return v === true || v === "true" || v == 1; };
14032                 break;
14033             case "date":
14034                 cv = function(v){
14035                     if(!v){
14036                         return '';
14037                     }
14038                     if(v instanceof Date){
14039                         return v;
14040                     }
14041                     if(dateFormat){
14042                         if(dateFormat == "timestamp"){
14043                             return new Date(v*1000);
14044                         }
14045                         return Date.parseDate(v, dateFormat);
14046                     }
14047                     var parsed = Date.parse(v);
14048                     return parsed ? new Date(parsed) : null;
14049                 };
14050              break;
14051             
14052         }
14053         this.convert = cv;
14054     }
14055 };
14056
14057 Roo.data.Field.prototype = {
14058     dateFormat: null,
14059     defaultValue: "",
14060     mapping: null,
14061     sortType : null,
14062     sortDir : "ASC"
14063 };/*
14064  * Based on:
14065  * Ext JS Library 1.1.1
14066  * Copyright(c) 2006-2007, Ext JS, LLC.
14067  *
14068  * Originally Released Under LGPL - original licence link has changed is not relivant.
14069  *
14070  * Fork - LGPL
14071  * <script type="text/javascript">
14072  */
14073  
14074 // Base class for reading structured data from a data source.  This class is intended to be
14075 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14076
14077 /**
14078  * @class Roo.data.DataReader
14079  * Base class for reading structured data from a data source.  This class is intended to be
14080  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14081  */
14082
14083 Roo.data.DataReader = function(meta, recordType){
14084     
14085     this.meta = meta;
14086     
14087     this.recordType = recordType instanceof Array ? 
14088         Roo.data.Record.create(recordType) : recordType;
14089 };
14090
14091 Roo.data.DataReader.prototype = {
14092     
14093     
14094     readerType : 'Data',
14095      /**
14096      * Create an empty record
14097      * @param {Object} data (optional) - overlay some values
14098      * @return {Roo.data.Record} record created.
14099      */
14100     newRow :  function(d) {
14101         var da =  {};
14102         this.recordType.prototype.fields.each(function(c) {
14103             switch( c.type) {
14104                 case 'int' : da[c.name] = 0; break;
14105                 case 'date' : da[c.name] = new Date(); break;
14106                 case 'float' : da[c.name] = 0.0; break;
14107                 case 'boolean' : da[c.name] = false; break;
14108                 default : da[c.name] = ""; break;
14109             }
14110             
14111         });
14112         return new this.recordType(Roo.apply(da, d));
14113     }
14114     
14115     
14116 };/*
14117  * Based on:
14118  * Ext JS Library 1.1.1
14119  * Copyright(c) 2006-2007, Ext JS, LLC.
14120  *
14121  * Originally Released Under LGPL - original licence link has changed is not relivant.
14122  *
14123  * Fork - LGPL
14124  * <script type="text/javascript">
14125  */
14126
14127 /**
14128  * @class Roo.data.DataProxy
14129  * @extends Roo.data.Observable
14130  * This class is an abstract base class for implementations which provide retrieval of
14131  * unformatted data objects.<br>
14132  * <p>
14133  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14134  * (of the appropriate type which knows how to parse the data object) to provide a block of
14135  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14136  * <p>
14137  * Custom implementations must implement the load method as described in
14138  * {@link Roo.data.HttpProxy#load}.
14139  */
14140 Roo.data.DataProxy = function(){
14141     this.addEvents({
14142         /**
14143          * @event beforeload
14144          * Fires before a network request is made to retrieve a data object.
14145          * @param {Object} This DataProxy object.
14146          * @param {Object} params The params parameter to the load function.
14147          */
14148         beforeload : true,
14149         /**
14150          * @event load
14151          * Fires before the load method's callback is called.
14152          * @param {Object} This DataProxy object.
14153          * @param {Object} o The data object.
14154          * @param {Object} arg The callback argument object passed to the load function.
14155          */
14156         load : true,
14157         /**
14158          * @event loadexception
14159          * Fires if an Exception occurs during data retrieval.
14160          * @param {Object} This DataProxy object.
14161          * @param {Object} o The data object.
14162          * @param {Object} arg The callback argument object passed to the load function.
14163          * @param {Object} e The Exception.
14164          */
14165         loadexception : true
14166     });
14167     Roo.data.DataProxy.superclass.constructor.call(this);
14168 };
14169
14170 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14171
14172     /**
14173      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14174      */
14175 /*
14176  * Based on:
14177  * Ext JS Library 1.1.1
14178  * Copyright(c) 2006-2007, Ext JS, LLC.
14179  *
14180  * Originally Released Under LGPL - original licence link has changed is not relivant.
14181  *
14182  * Fork - LGPL
14183  * <script type="text/javascript">
14184  */
14185 /**
14186  * @class Roo.data.MemoryProxy
14187  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14188  * to the Reader when its load method is called.
14189  * @constructor
14190  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14191  */
14192 Roo.data.MemoryProxy = function(data){
14193     if (data.data) {
14194         data = data.data;
14195     }
14196     Roo.data.MemoryProxy.superclass.constructor.call(this);
14197     this.data = data;
14198 };
14199
14200 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14201     
14202     /**
14203      * Load data from the requested source (in this case an in-memory
14204      * data object passed to the constructor), read the data object into
14205      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14206      * process that block using the passed callback.
14207      * @param {Object} params This parameter is not used by the MemoryProxy class.
14208      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14209      * object into a block of Roo.data.Records.
14210      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14211      * The function must be passed <ul>
14212      * <li>The Record block object</li>
14213      * <li>The "arg" argument from the load function</li>
14214      * <li>A boolean success indicator</li>
14215      * </ul>
14216      * @param {Object} scope The scope in which to call the callback
14217      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14218      */
14219     load : function(params, reader, callback, scope, arg){
14220         params = params || {};
14221         var result;
14222         try {
14223             result = reader.readRecords(params.data ? params.data :this.data);
14224         }catch(e){
14225             this.fireEvent("loadexception", this, arg, null, e);
14226             callback.call(scope, null, arg, false);
14227             return;
14228         }
14229         callback.call(scope, result, arg, true);
14230     },
14231     
14232     // private
14233     update : function(params, records){
14234         
14235     }
14236 });/*
14237  * Based on:
14238  * Ext JS Library 1.1.1
14239  * Copyright(c) 2006-2007, Ext JS, LLC.
14240  *
14241  * Originally Released Under LGPL - original licence link has changed is not relivant.
14242  *
14243  * Fork - LGPL
14244  * <script type="text/javascript">
14245  */
14246 /**
14247  * @class Roo.data.HttpProxy
14248  * @extends Roo.data.DataProxy
14249  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14250  * configured to reference a certain URL.<br><br>
14251  * <p>
14252  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14253  * from which the running page was served.<br><br>
14254  * <p>
14255  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14256  * <p>
14257  * Be aware that to enable the browser to parse an XML document, the server must set
14258  * the Content-Type header in the HTTP response to "text/xml".
14259  * @constructor
14260  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14261  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14262  * will be used to make the request.
14263  */
14264 Roo.data.HttpProxy = function(conn){
14265     Roo.data.HttpProxy.superclass.constructor.call(this);
14266     // is conn a conn config or a real conn?
14267     this.conn = conn;
14268     this.useAjax = !conn || !conn.events;
14269   
14270 };
14271
14272 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14273     // thse are take from connection...
14274     
14275     /**
14276      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14277      */
14278     /**
14279      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14280      * extra parameters to each request made by this object. (defaults to undefined)
14281      */
14282     /**
14283      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14284      *  to each request made by this object. (defaults to undefined)
14285      */
14286     /**
14287      * @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)
14288      */
14289     /**
14290      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14291      */
14292      /**
14293      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14294      * @type Boolean
14295      */
14296   
14297
14298     /**
14299      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14300      * @type Boolean
14301      */
14302     /**
14303      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14304      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14305      * a finer-grained basis than the DataProxy events.
14306      */
14307     getConnection : function(){
14308         return this.useAjax ? Roo.Ajax : this.conn;
14309     },
14310
14311     /**
14312      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14313      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14314      * process that block using the passed callback.
14315      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14316      * for the request to the remote server.
14317      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14318      * object into a block of Roo.data.Records.
14319      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14320      * The function must be passed <ul>
14321      * <li>The Record block object</li>
14322      * <li>The "arg" argument from the load function</li>
14323      * <li>A boolean success indicator</li>
14324      * </ul>
14325      * @param {Object} scope The scope in which to call the callback
14326      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14327      */
14328     load : function(params, reader, callback, scope, arg){
14329         if(this.fireEvent("beforeload", this, params) !== false){
14330             var  o = {
14331                 params : params || {},
14332                 request: {
14333                     callback : callback,
14334                     scope : scope,
14335                     arg : arg
14336                 },
14337                 reader: reader,
14338                 callback : this.loadResponse,
14339                 scope: this
14340             };
14341             if(this.useAjax){
14342                 Roo.applyIf(o, this.conn);
14343                 if(this.activeRequest){
14344                     Roo.Ajax.abort(this.activeRequest);
14345                 }
14346                 this.activeRequest = Roo.Ajax.request(o);
14347             }else{
14348                 this.conn.request(o);
14349             }
14350         }else{
14351             callback.call(scope||this, null, arg, false);
14352         }
14353     },
14354
14355     // private
14356     loadResponse : function(o, success, response){
14357         delete this.activeRequest;
14358         if(!success){
14359             this.fireEvent("loadexception", this, o, response);
14360             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14361             return;
14362         }
14363         var result;
14364         try {
14365             result = o.reader.read(response);
14366         }catch(e){
14367             this.fireEvent("loadexception", this, o, response, e);
14368             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14369             return;
14370         }
14371         
14372         this.fireEvent("load", this, o, o.request.arg);
14373         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14374     },
14375
14376     // private
14377     update : function(dataSet){
14378
14379     },
14380
14381     // private
14382     updateResponse : function(dataSet){
14383
14384     }
14385 });/*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395
14396 /**
14397  * @class Roo.data.ScriptTagProxy
14398  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14399  * other than the originating domain of the running page.<br><br>
14400  * <p>
14401  * <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
14402  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14403  * <p>
14404  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14405  * source code that is used as the source inside a &lt;script> tag.<br><br>
14406  * <p>
14407  * In order for the browser to process the returned data, the server must wrap the data object
14408  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14409  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14410  * depending on whether the callback name was passed:
14411  * <p>
14412  * <pre><code>
14413 boolean scriptTag = false;
14414 String cb = request.getParameter("callback");
14415 if (cb != null) {
14416     scriptTag = true;
14417     response.setContentType("text/javascript");
14418 } else {
14419     response.setContentType("application/x-json");
14420 }
14421 Writer out = response.getWriter();
14422 if (scriptTag) {
14423     out.write(cb + "(");
14424 }
14425 out.print(dataBlock.toJsonString());
14426 if (scriptTag) {
14427     out.write(");");
14428 }
14429 </pre></code>
14430  *
14431  * @constructor
14432  * @param {Object} config A configuration object.
14433  */
14434 Roo.data.ScriptTagProxy = function(config){
14435     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14436     Roo.apply(this, config);
14437     this.head = document.getElementsByTagName("head")[0];
14438 };
14439
14440 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14441
14442 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14443     /**
14444      * @cfg {String} url The URL from which to request the data object.
14445      */
14446     /**
14447      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14448      */
14449     timeout : 30000,
14450     /**
14451      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14452      * the server the name of the callback function set up by the load call to process the returned data object.
14453      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14454      * javascript output which calls this named function passing the data object as its only parameter.
14455      */
14456     callbackParam : "callback",
14457     /**
14458      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14459      * name to the request.
14460      */
14461     nocache : true,
14462
14463     /**
14464      * Load data from the configured URL, read the data object into
14465      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14466      * process that block using the passed callback.
14467      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14468      * for the request to the remote server.
14469      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14470      * object into a block of Roo.data.Records.
14471      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14472      * The function must be passed <ul>
14473      * <li>The Record block object</li>
14474      * <li>The "arg" argument from the load function</li>
14475      * <li>A boolean success indicator</li>
14476      * </ul>
14477      * @param {Object} scope The scope in which to call the callback
14478      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14479      */
14480     load : function(params, reader, callback, scope, arg){
14481         if(this.fireEvent("beforeload", this, params) !== false){
14482
14483             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14484
14485             var url = this.url;
14486             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14487             if(this.nocache){
14488                 url += "&_dc=" + (new Date().getTime());
14489             }
14490             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14491             var trans = {
14492                 id : transId,
14493                 cb : "stcCallback"+transId,
14494                 scriptId : "stcScript"+transId,
14495                 params : params,
14496                 arg : arg,
14497                 url : url,
14498                 callback : callback,
14499                 scope : scope,
14500                 reader : reader
14501             };
14502             var conn = this;
14503
14504             window[trans.cb] = function(o){
14505                 conn.handleResponse(o, trans);
14506             };
14507
14508             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14509
14510             if(this.autoAbort !== false){
14511                 this.abort();
14512             }
14513
14514             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14515
14516             var script = document.createElement("script");
14517             script.setAttribute("src", url);
14518             script.setAttribute("type", "text/javascript");
14519             script.setAttribute("id", trans.scriptId);
14520             this.head.appendChild(script);
14521
14522             this.trans = trans;
14523         }else{
14524             callback.call(scope||this, null, arg, false);
14525         }
14526     },
14527
14528     // private
14529     isLoading : function(){
14530         return this.trans ? true : false;
14531     },
14532
14533     /**
14534      * Abort the current server request.
14535      */
14536     abort : function(){
14537         if(this.isLoading()){
14538             this.destroyTrans(this.trans);
14539         }
14540     },
14541
14542     // private
14543     destroyTrans : function(trans, isLoaded){
14544         this.head.removeChild(document.getElementById(trans.scriptId));
14545         clearTimeout(trans.timeoutId);
14546         if(isLoaded){
14547             window[trans.cb] = undefined;
14548             try{
14549                 delete window[trans.cb];
14550             }catch(e){}
14551         }else{
14552             // if hasn't been loaded, wait for load to remove it to prevent script error
14553             window[trans.cb] = function(){
14554                 window[trans.cb] = undefined;
14555                 try{
14556                     delete window[trans.cb];
14557                 }catch(e){}
14558             };
14559         }
14560     },
14561
14562     // private
14563     handleResponse : function(o, trans){
14564         this.trans = false;
14565         this.destroyTrans(trans, true);
14566         var result;
14567         try {
14568             result = trans.reader.readRecords(o);
14569         }catch(e){
14570             this.fireEvent("loadexception", this, o, trans.arg, e);
14571             trans.callback.call(trans.scope||window, null, trans.arg, false);
14572             return;
14573         }
14574         this.fireEvent("load", this, o, trans.arg);
14575         trans.callback.call(trans.scope||window, result, trans.arg, true);
14576     },
14577
14578     // private
14579     handleFailure : function(trans){
14580         this.trans = false;
14581         this.destroyTrans(trans, false);
14582         this.fireEvent("loadexception", this, null, trans.arg);
14583         trans.callback.call(trans.scope||window, null, trans.arg, false);
14584     }
14585 });/*
14586  * Based on:
14587  * Ext JS Library 1.1.1
14588  * Copyright(c) 2006-2007, Ext JS, LLC.
14589  *
14590  * Originally Released Under LGPL - original licence link has changed is not relivant.
14591  *
14592  * Fork - LGPL
14593  * <script type="text/javascript">
14594  */
14595
14596 /**
14597  * @class Roo.data.JsonReader
14598  * @extends Roo.data.DataReader
14599  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14600  * based on mappings in a provided Roo.data.Record constructor.
14601  * 
14602  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14603  * in the reply previously. 
14604  * 
14605  * <p>
14606  * Example code:
14607  * <pre><code>
14608 var RecordDef = Roo.data.Record.create([
14609     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14610     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14611 ]);
14612 var myReader = new Roo.data.JsonReader({
14613     totalProperty: "results",    // The property which contains the total dataset size (optional)
14614     root: "rows",                // The property which contains an Array of row objects
14615     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14616 }, RecordDef);
14617 </code></pre>
14618  * <p>
14619  * This would consume a JSON file like this:
14620  * <pre><code>
14621 { 'results': 2, 'rows': [
14622     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14623     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14624 }
14625 </code></pre>
14626  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14627  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14628  * paged from the remote server.
14629  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14630  * @cfg {String} root name of the property which contains the Array of row objects.
14631  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14632  * @cfg {Array} fields Array of field definition objects
14633  * @constructor
14634  * Create a new JsonReader
14635  * @param {Object} meta Metadata configuration options
14636  * @param {Object} recordType Either an Array of field definition objects,
14637  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14638  */
14639 Roo.data.JsonReader = function(meta, recordType){
14640     
14641     meta = meta || {};
14642     // set some defaults:
14643     Roo.applyIf(meta, {
14644         totalProperty: 'total',
14645         successProperty : 'success',
14646         root : 'data',
14647         id : 'id'
14648     });
14649     
14650     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14651 };
14652 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14653     
14654     readerType : 'Json',
14655     
14656     /**
14657      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14658      * Used by Store query builder to append _requestMeta to params.
14659      * 
14660      */
14661     metaFromRemote : false,
14662     /**
14663      * This method is only used by a DataProxy which has retrieved data from a remote server.
14664      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14665      * @return {Object} data A data block which is used by an Roo.data.Store object as
14666      * a cache of Roo.data.Records.
14667      */
14668     read : function(response){
14669         var json = response.responseText;
14670        
14671         var o = /* eval:var:o */ eval("("+json+")");
14672         if(!o) {
14673             throw {message: "JsonReader.read: Json object not found"};
14674         }
14675         
14676         if(o.metaData){
14677             
14678             delete this.ef;
14679             this.metaFromRemote = true;
14680             this.meta = o.metaData;
14681             this.recordType = Roo.data.Record.create(o.metaData.fields);
14682             this.onMetaChange(this.meta, this.recordType, o);
14683         }
14684         return this.readRecords(o);
14685     },
14686
14687     // private function a store will implement
14688     onMetaChange : function(meta, recordType, o){
14689
14690     },
14691
14692     /**
14693          * @ignore
14694          */
14695     simpleAccess: function(obj, subsc) {
14696         return obj[subsc];
14697     },
14698
14699         /**
14700          * @ignore
14701          */
14702     getJsonAccessor: function(){
14703         var re = /[\[\.]/;
14704         return function(expr) {
14705             try {
14706                 return(re.test(expr))
14707                     ? new Function("obj", "return obj." + expr)
14708                     : function(obj){
14709                         return obj[expr];
14710                     };
14711             } catch(e){}
14712             return Roo.emptyFn;
14713         };
14714     }(),
14715
14716     /**
14717      * Create a data block containing Roo.data.Records from an XML document.
14718      * @param {Object} o An object which contains an Array of row objects in the property specified
14719      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14720      * which contains the total size of the dataset.
14721      * @return {Object} data A data block which is used by an Roo.data.Store object as
14722      * a cache of Roo.data.Records.
14723      */
14724     readRecords : function(o){
14725         /**
14726          * After any data loads, the raw JSON data is available for further custom processing.
14727          * @type Object
14728          */
14729         this.o = o;
14730         var s = this.meta, Record = this.recordType,
14731             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14732
14733 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14734         if (!this.ef) {
14735             if(s.totalProperty) {
14736                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14737                 }
14738                 if(s.successProperty) {
14739                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14740                 }
14741                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14742                 if (s.id) {
14743                         var g = this.getJsonAccessor(s.id);
14744                         this.getId = function(rec) {
14745                                 var r = g(rec);  
14746                                 return (r === undefined || r === "") ? null : r;
14747                         };
14748                 } else {
14749                         this.getId = function(){return null;};
14750                 }
14751             this.ef = [];
14752             for(var jj = 0; jj < fl; jj++){
14753                 f = fi[jj];
14754                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14755                 this.ef[jj] = this.getJsonAccessor(map);
14756             }
14757         }
14758
14759         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14760         if(s.totalProperty){
14761             var vt = parseInt(this.getTotal(o), 10);
14762             if(!isNaN(vt)){
14763                 totalRecords = vt;
14764             }
14765         }
14766         if(s.successProperty){
14767             var vs = this.getSuccess(o);
14768             if(vs === false || vs === 'false'){
14769                 success = false;
14770             }
14771         }
14772         var records = [];
14773         for(var i = 0; i < c; i++){
14774                 var n = root[i];
14775             var values = {};
14776             var id = this.getId(n);
14777             for(var j = 0; j < fl; j++){
14778                 f = fi[j];
14779             var v = this.ef[j](n);
14780             if (!f.convert) {
14781                 Roo.log('missing convert for ' + f.name);
14782                 Roo.log(f);
14783                 continue;
14784             }
14785             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14786             }
14787             var record = new Record(values, id);
14788             record.json = n;
14789             records[i] = record;
14790         }
14791         return {
14792             raw : o,
14793             success : success,
14794             records : records,
14795             totalRecords : totalRecords
14796         };
14797     },
14798     // used when loading children.. @see loadDataFromChildren
14799     toLoadData: function(rec)
14800     {
14801         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14802         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14803         return { data : data, total : data.length };
14804         
14805     }
14806 });/*
14807  * Based on:
14808  * Ext JS Library 1.1.1
14809  * Copyright(c) 2006-2007, Ext JS, LLC.
14810  *
14811  * Originally Released Under LGPL - original licence link has changed is not relivant.
14812  *
14813  * Fork - LGPL
14814  * <script type="text/javascript">
14815  */
14816
14817 /**
14818  * @class Roo.data.ArrayReader
14819  * @extends Roo.data.DataReader
14820  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14821  * Each element of that Array represents a row of data fields. The
14822  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14823  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14824  * <p>
14825  * Example code:.
14826  * <pre><code>
14827 var RecordDef = Roo.data.Record.create([
14828     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14829     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14830 ]);
14831 var myReader = new Roo.data.ArrayReader({
14832     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14833 }, RecordDef);
14834 </code></pre>
14835  * <p>
14836  * This would consume an Array like this:
14837  * <pre><code>
14838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14839   </code></pre>
14840  
14841  * @constructor
14842  * Create a new JsonReader
14843  * @param {Object} meta Metadata configuration options.
14844  * @param {Object|Array} recordType Either an Array of field definition objects
14845  * 
14846  * @cfg {Array} fields Array of field definition objects
14847  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14848  * as specified to {@link Roo.data.Record#create},
14849  * or an {@link Roo.data.Record} object
14850  *
14851  * 
14852  * created using {@link Roo.data.Record#create}.
14853  */
14854 Roo.data.ArrayReader = function(meta, recordType)
14855 {    
14856     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14857 };
14858
14859 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14860     
14861       /**
14862      * Create a data block containing Roo.data.Records from an XML document.
14863      * @param {Object} o An Array of row objects which represents the dataset.
14864      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14865      * a cache of Roo.data.Records.
14866      */
14867     readRecords : function(o)
14868     {
14869         var sid = this.meta ? this.meta.id : null;
14870         var recordType = this.recordType, fields = recordType.prototype.fields;
14871         var records = [];
14872         var root = o;
14873         for(var i = 0; i < root.length; i++){
14874                 var n = root[i];
14875             var values = {};
14876             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14877             for(var j = 0, jlen = fields.length; j < jlen; j++){
14878                 var f = fields.items[j];
14879                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14880                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14881                 v = f.convert(v);
14882                 values[f.name] = v;
14883             }
14884             var record = new recordType(values, id);
14885             record.json = n;
14886             records[records.length] = record;
14887         }
14888         return {
14889             records : records,
14890             totalRecords : records.length
14891         };
14892     },
14893     // used when loading children.. @see loadDataFromChildren
14894     toLoadData: function(rec)
14895     {
14896         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14897         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14898         
14899     }
14900     
14901     
14902 });/*
14903  * - LGPL
14904  * * 
14905  */
14906
14907 /**
14908  * @class Roo.bootstrap.ComboBox
14909  * @extends Roo.bootstrap.TriggerField
14910  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14911  * @cfg {Boolean} append (true|false) default false
14912  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14913  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14914  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14915  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14916  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14917  * @cfg {Boolean} animate default true
14918  * @cfg {Boolean} emptyResultText only for touch device
14919  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14920  * @cfg {String} emptyTitle default ''
14921  * @cfg {Number} width fixed with? experimental
14922  * @constructor
14923  * Create a new ComboBox.
14924  * @param {Object} config Configuration options
14925  */
14926 Roo.bootstrap.ComboBox = function(config){
14927     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14928     this.addEvents({
14929         /**
14930          * @event expand
14931          * Fires when the dropdown list is expanded
14932         * @param {Roo.bootstrap.ComboBox} combo This combo box
14933         */
14934         'expand' : true,
14935         /**
14936          * @event collapse
14937          * Fires when the dropdown list is collapsed
14938         * @param {Roo.bootstrap.ComboBox} combo This combo box
14939         */
14940         'collapse' : true,
14941         /**
14942          * @event beforeselect
14943          * Fires before a list item is selected. Return false to cancel the selection.
14944         * @param {Roo.bootstrap.ComboBox} combo This combo box
14945         * @param {Roo.data.Record} record The data record returned from the underlying store
14946         * @param {Number} index The index of the selected item in the dropdown list
14947         */
14948         'beforeselect' : true,
14949         /**
14950          * @event select
14951          * Fires when a list item is selected
14952         * @param {Roo.bootstrap.ComboBox} combo This combo box
14953         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14954         * @param {Number} index The index of the selected item in the dropdown list
14955         */
14956         'select' : true,
14957         /**
14958          * @event beforequery
14959          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14960          * The event object passed has these properties:
14961         * @param {Roo.bootstrap.ComboBox} combo This combo box
14962         * @param {String} query The query
14963         * @param {Boolean} forceAll true to force "all" query
14964         * @param {Boolean} cancel true to cancel the query
14965         * @param {Object} e The query event object
14966         */
14967         'beforequery': true,
14968          /**
14969          * @event add
14970          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'add' : true,
14974         /**
14975          * @event edit
14976          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14979         */
14980         'edit' : true,
14981         /**
14982          * @event remove
14983          * Fires when the remove value from the combobox array
14984         * @param {Roo.bootstrap.ComboBox} combo This combo box
14985         */
14986         'remove' : true,
14987         /**
14988          * @event afterremove
14989          * Fires when the remove value from the combobox array
14990         * @param {Roo.bootstrap.ComboBox} combo This combo box
14991         */
14992         'afterremove' : true,
14993         /**
14994          * @event specialfilter
14995          * Fires when specialfilter
14996             * @param {Roo.bootstrap.ComboBox} combo This combo box
14997             */
14998         'specialfilter' : true,
14999         /**
15000          * @event tick
15001          * Fires when tick the element
15002             * @param {Roo.bootstrap.ComboBox} combo This combo box
15003             */
15004         'tick' : true,
15005         /**
15006          * @event touchviewdisplay
15007          * Fires when touch view require special display (default is using displayField)
15008             * @param {Roo.bootstrap.ComboBox} combo This combo box
15009             * @param {Object} cfg set html .
15010             */
15011         'touchviewdisplay' : true
15012         
15013     });
15014     
15015     this.item = [];
15016     this.tickItems = [];
15017     
15018     this.selectedIndex = -1;
15019     if(this.mode == 'local'){
15020         if(config.queryDelay === undefined){
15021             this.queryDelay = 10;
15022         }
15023         if(config.minChars === undefined){
15024             this.minChars = 0;
15025         }
15026     }
15027 };
15028
15029 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15030      
15031     /**
15032      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15033      * rendering into an Roo.Editor, defaults to false)
15034      */
15035     /**
15036      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15037      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15038      */
15039     /**
15040      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15041      */
15042     /**
15043      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15044      * the dropdown list (defaults to undefined, with no header element)
15045      */
15046
15047      /**
15048      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15049      */
15050      
15051      /**
15052      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15053      */
15054     listWidth: undefined,
15055     /**
15056      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15057      * mode = 'remote' or 'text' if mode = 'local')
15058      */
15059     displayField: undefined,
15060     
15061     /**
15062      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15063      * mode = 'remote' or 'value' if mode = 'local'). 
15064      * Note: use of a valueField requires the user make a selection
15065      * in order for a value to be mapped.
15066      */
15067     valueField: undefined,
15068     /**
15069      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15070      */
15071     modalTitle : '',
15072     
15073     /**
15074      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15075      * field's data value (defaults to the underlying DOM element's name)
15076      */
15077     hiddenName: undefined,
15078     /**
15079      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15080      */
15081     listClass: '',
15082     /**
15083      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15084      */
15085     selectedClass: 'active',
15086     
15087     /**
15088      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15089      */
15090     shadow:'sides',
15091     /**
15092      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15093      * anchor positions (defaults to 'tl-bl')
15094      */
15095     listAlign: 'tl-bl?',
15096     /**
15097      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15098      */
15099     maxHeight: 300,
15100     /**
15101      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15102      * query specified by the allQuery config option (defaults to 'query')
15103      */
15104     triggerAction: 'query',
15105     /**
15106      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15107      * (defaults to 4, does not apply if editable = false)
15108      */
15109     minChars : 4,
15110     /**
15111      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15112      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15113      */
15114     typeAhead: false,
15115     /**
15116      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15117      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15118      */
15119     queryDelay: 500,
15120     /**
15121      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15122      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15123      */
15124     pageSize: 0,
15125     /**
15126      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15127      * when editable = true (defaults to false)
15128      */
15129     selectOnFocus:false,
15130     /**
15131      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15132      */
15133     queryParam: 'query',
15134     /**
15135      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15136      * when mode = 'remote' (defaults to 'Loading...')
15137      */
15138     loadingText: 'Loading...',
15139     /**
15140      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15141      */
15142     resizable: false,
15143     /**
15144      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15145      */
15146     handleHeight : 8,
15147     /**
15148      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15149      * traditional select (defaults to true)
15150      */
15151     editable: true,
15152     /**
15153      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15154      */
15155     allQuery: '',
15156     /**
15157      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15158      */
15159     mode: 'remote',
15160     /**
15161      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15162      * listWidth has a higher value)
15163      */
15164     minListWidth : 70,
15165     /**
15166      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15167      * allow the user to set arbitrary text into the field (defaults to false)
15168      */
15169     forceSelection:false,
15170     /**
15171      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15172      * if typeAhead = true (defaults to 250)
15173      */
15174     typeAheadDelay : 250,
15175     /**
15176      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15177      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15178      */
15179     valueNotFoundText : undefined,
15180     /**
15181      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15182      */
15183     blockFocus : false,
15184     
15185     /**
15186      * @cfg {Boolean} disableClear Disable showing of clear button.
15187      */
15188     disableClear : false,
15189     /**
15190      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15191      */
15192     alwaysQuery : false,
15193     
15194     /**
15195      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15196      */
15197     multiple : false,
15198     
15199     /**
15200      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15201      */
15202     invalidClass : "has-warning",
15203     
15204     /**
15205      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15206      */
15207     validClass : "has-success",
15208     
15209     /**
15210      * @cfg {Boolean} specialFilter (true|false) special filter default false
15211      */
15212     specialFilter : false,
15213     
15214     /**
15215      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15216      */
15217     mobileTouchView : true,
15218     
15219     /**
15220      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15221      */
15222     useNativeIOS : false,
15223     
15224     /**
15225      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15226      */
15227     mobile_restrict_height : false,
15228     
15229     ios_options : false,
15230     
15231     //private
15232     addicon : false,
15233     editicon: false,
15234     
15235     page: 0,
15236     hasQuery: false,
15237     append: false,
15238     loadNext: false,
15239     autoFocus : true,
15240     tickable : false,
15241     btnPosition : 'right',
15242     triggerList : true,
15243     showToggleBtn : true,
15244     animate : true,
15245     emptyResultText: 'Empty',
15246     triggerText : 'Select',
15247     emptyTitle : '',
15248     width : false,
15249     
15250     // element that contains real text value.. (when hidden is used..)
15251     
15252     getAutoCreate : function()
15253     {   
15254         var cfg = false;
15255         //render
15256         /*
15257          * Render classic select for iso
15258          */
15259         
15260         if(Roo.isIOS && this.useNativeIOS){
15261             cfg = this.getAutoCreateNativeIOS();
15262             return cfg;
15263         }
15264         
15265         /*
15266          * Touch Devices
15267          */
15268         
15269         if(Roo.isTouch && this.mobileTouchView){
15270             cfg = this.getAutoCreateTouchView();
15271             return cfg;;
15272         }
15273         
15274         /*
15275          *  Normal ComboBox
15276          */
15277         if(!this.tickable){
15278             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15279             return cfg;
15280         }
15281         
15282         /*
15283          *  ComboBox with tickable selections
15284          */
15285              
15286         var align = this.labelAlign || this.parentLabelAlign();
15287         
15288         cfg = {
15289             cls : 'form-group roo-combobox-tickable' //input-group
15290         };
15291         
15292         var btn_text_select = '';
15293         var btn_text_done = '';
15294         var btn_text_cancel = '';
15295         
15296         if (this.btn_text_show) {
15297             btn_text_select = 'Select';
15298             btn_text_done = 'Done';
15299             btn_text_cancel = 'Cancel'; 
15300         }
15301         
15302         var buttons = {
15303             tag : 'div',
15304             cls : 'tickable-buttons',
15305             cn : [
15306                 {
15307                     tag : 'button',
15308                     type : 'button',
15309                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15310                     //html : this.triggerText
15311                     html: btn_text_select
15312                 },
15313                 {
15314                     tag : 'button',
15315                     type : 'button',
15316                     name : 'ok',
15317                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15318                     //html : 'Done'
15319                     html: btn_text_done
15320                 },
15321                 {
15322                     tag : 'button',
15323                     type : 'button',
15324                     name : 'cancel',
15325                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15326                     //html : 'Cancel'
15327                     html: btn_text_cancel
15328                 }
15329             ]
15330         };
15331         
15332         if(this.editable){
15333             buttons.cn.unshift({
15334                 tag: 'input',
15335                 cls: 'roo-select2-search-field-input'
15336             });
15337         }
15338         
15339         var _this = this;
15340         
15341         Roo.each(buttons.cn, function(c){
15342             if (_this.size) {
15343                 c.cls += ' btn-' + _this.size;
15344             }
15345
15346             if (_this.disabled) {
15347                 c.disabled = true;
15348             }
15349         });
15350         
15351         var box = {
15352             tag: 'div',
15353             style : 'display: contents',
15354             cn: [
15355                 {
15356                     tag: 'input',
15357                     type : 'hidden',
15358                     cls: 'form-hidden-field'
15359                 },
15360                 {
15361                     tag: 'ul',
15362                     cls: 'roo-select2-choices',
15363                     cn:[
15364                         {
15365                             tag: 'li',
15366                             cls: 'roo-select2-search-field',
15367                             cn: [
15368                                 buttons
15369                             ]
15370                         }
15371                     ]
15372                 }
15373             ]
15374         };
15375         
15376         var combobox = {
15377             cls: 'roo-select2-container input-group roo-select2-container-multi',
15378             cn: [
15379                 
15380                 box
15381 //                {
15382 //                    tag: 'ul',
15383 //                    cls: 'typeahead typeahead-long dropdown-menu',
15384 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15385 //                }
15386             ]
15387         };
15388         
15389         if(this.hasFeedback && !this.allowBlank){
15390             
15391             var feedback = {
15392                 tag: 'span',
15393                 cls: 'glyphicon form-control-feedback'
15394             };
15395
15396             combobox.cn.push(feedback);
15397         }
15398         
15399         
15400         
15401         var indicator = {
15402             tag : 'i',
15403             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15404             tooltip : 'This field is required'
15405         };
15406         if (Roo.bootstrap.version == 4) {
15407             indicator = {
15408                 tag : 'i',
15409                 style : 'display:none'
15410             };
15411         }
15412         if (align ==='left' && this.fieldLabel.length) {
15413             
15414             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15415             
15416             cfg.cn = [
15417                 indicator,
15418                 {
15419                     tag: 'label',
15420                     'for' :  id,
15421                     cls : 'control-label col-form-label',
15422                     html : this.fieldLabel
15423
15424                 },
15425                 {
15426                     cls : "", 
15427                     cn: [
15428                         combobox
15429                     ]
15430                 }
15431
15432             ];
15433             
15434             var labelCfg = cfg.cn[1];
15435             var contentCfg = cfg.cn[2];
15436             
15437
15438             if(this.indicatorpos == 'right'){
15439                 
15440                 cfg.cn = [
15441                     {
15442                         tag: 'label',
15443                         'for' :  id,
15444                         cls : 'control-label col-form-label',
15445                         cn : [
15446                             {
15447                                 tag : 'span',
15448                                 html : this.fieldLabel
15449                             },
15450                             indicator
15451                         ]
15452                     },
15453                     {
15454                         cls : "",
15455                         cn: [
15456                             combobox
15457                         ]
15458                     }
15459
15460                 ];
15461                 
15462                 
15463                 
15464                 labelCfg = cfg.cn[0];
15465                 contentCfg = cfg.cn[1];
15466             
15467             }
15468             
15469             if(this.labelWidth > 12){
15470                 labelCfg.style = "width: " + this.labelWidth + 'px';
15471             }
15472             if(this.width * 1 > 0){
15473                 contentCfg.style = "width: " + this.width + 'px';
15474             }
15475             if(this.labelWidth < 13 && this.labelmd == 0){
15476                 this.labelmd = this.labelWidth;
15477             }
15478             
15479             if(this.labellg > 0){
15480                 labelCfg.cls += ' col-lg-' + this.labellg;
15481                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15482             }
15483             
15484             if(this.labelmd > 0){
15485                 labelCfg.cls += ' col-md-' + this.labelmd;
15486                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15487             }
15488             
15489             if(this.labelsm > 0){
15490                 labelCfg.cls += ' col-sm-' + this.labelsm;
15491                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15492             }
15493             
15494             if(this.labelxs > 0){
15495                 labelCfg.cls += ' col-xs-' + this.labelxs;
15496                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15497             }
15498                 
15499                 
15500         } else if ( this.fieldLabel.length) {
15501 //                Roo.log(" label");
15502                  cfg.cn = [
15503                    indicator,
15504                     {
15505                         tag: 'label',
15506                         //cls : 'input-group-addon',
15507                         html : this.fieldLabel
15508                     },
15509                     combobox
15510                 ];
15511                 
15512                 if(this.indicatorpos == 'right'){
15513                     cfg.cn = [
15514                         {
15515                             tag: 'label',
15516                             //cls : 'input-group-addon',
15517                             html : this.fieldLabel
15518                         },
15519                         indicator,
15520                         combobox
15521                     ];
15522                     
15523                 }
15524
15525         } else {
15526             
15527 //                Roo.log(" no label && no align");
15528                 cfg = combobox
15529                      
15530                 
15531         }
15532          
15533         var settings=this;
15534         ['xs','sm','md','lg'].map(function(size){
15535             if (settings[size]) {
15536                 cfg.cls += ' col-' + size + '-' + settings[size];
15537             }
15538         });
15539         
15540         return cfg;
15541         
15542     },
15543     
15544     _initEventsCalled : false,
15545     
15546     // private
15547     initEvents: function()
15548     {   
15549         if (this._initEventsCalled) { // as we call render... prevent looping...
15550             return;
15551         }
15552         this._initEventsCalled = true;
15553         
15554         if (!this.store) {
15555             throw "can not find store for combo";
15556         }
15557         
15558         this.indicator = this.indicatorEl();
15559         
15560         this.store = Roo.factory(this.store, Roo.data);
15561         this.store.parent = this;
15562         
15563         // if we are building from html. then this element is so complex, that we can not really
15564         // use the rendered HTML.
15565         // so we have to trash and replace the previous code.
15566         if (Roo.XComponent.build_from_html) {
15567             // remove this element....
15568             var e = this.el.dom, k=0;
15569             while (e ) { e = e.previousSibling;  ++k;}
15570
15571             this.el.remove();
15572             
15573             this.el=false;
15574             this.rendered = false;
15575             
15576             this.render(this.parent().getChildContainer(true), k);
15577         }
15578         
15579         if(Roo.isIOS && this.useNativeIOS){
15580             this.initIOSView();
15581             return;
15582         }
15583         
15584         /*
15585          * Touch Devices
15586          */
15587         
15588         if(Roo.isTouch && this.mobileTouchView){
15589             this.initTouchView();
15590             return;
15591         }
15592         
15593         if(this.tickable){
15594             this.initTickableEvents();
15595             return;
15596         }
15597         
15598         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15599         
15600         if(this.hiddenName){
15601             
15602             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15603             
15604             this.hiddenField.dom.value =
15605                 this.hiddenValue !== undefined ? this.hiddenValue :
15606                 this.value !== undefined ? this.value : '';
15607
15608             // prevent input submission
15609             this.el.dom.removeAttribute('name');
15610             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15611              
15612              
15613         }
15614         //if(Roo.isGecko){
15615         //    this.el.dom.setAttribute('autocomplete', 'off');
15616         //}
15617         
15618         var cls = 'x-combo-list';
15619         
15620         //this.list = new Roo.Layer({
15621         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15622         //});
15623         
15624         var _this = this;
15625         
15626         (function(){
15627             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15628             _this.list.setWidth(lw);
15629         }).defer(100);
15630         
15631         this.list.on('mouseover', this.onViewOver, this);
15632         this.list.on('mousemove', this.onViewMove, this);
15633         this.list.on('scroll', this.onViewScroll, this);
15634         
15635         /*
15636         this.list.swallowEvent('mousewheel');
15637         this.assetHeight = 0;
15638
15639         if(this.title){
15640             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15641             this.assetHeight += this.header.getHeight();
15642         }
15643
15644         this.innerList = this.list.createChild({cls:cls+'-inner'});
15645         this.innerList.on('mouseover', this.onViewOver, this);
15646         this.innerList.on('mousemove', this.onViewMove, this);
15647         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15648         
15649         if(this.allowBlank && !this.pageSize && !this.disableClear){
15650             this.footer = this.list.createChild({cls:cls+'-ft'});
15651             this.pageTb = new Roo.Toolbar(this.footer);
15652            
15653         }
15654         if(this.pageSize){
15655             this.footer = this.list.createChild({cls:cls+'-ft'});
15656             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15657                     {pageSize: this.pageSize});
15658             
15659         }
15660         
15661         if (this.pageTb && this.allowBlank && !this.disableClear) {
15662             var _this = this;
15663             this.pageTb.add(new Roo.Toolbar.Fill(), {
15664                 cls: 'x-btn-icon x-btn-clear',
15665                 text: '&#160;',
15666                 handler: function()
15667                 {
15668                     _this.collapse();
15669                     _this.clearValue();
15670                     _this.onSelect(false, -1);
15671                 }
15672             });
15673         }
15674         if (this.footer) {
15675             this.assetHeight += this.footer.getHeight();
15676         }
15677         */
15678             
15679         if(!this.tpl){
15680             this.tpl = Roo.bootstrap.version == 4 ?
15681                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15682                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15683         }
15684
15685         this.view = new Roo.View(this.list, this.tpl, {
15686             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15687         });
15688         //this.view.wrapEl.setDisplayed(false);
15689         this.view.on('click', this.onViewClick, this);
15690         
15691         
15692         this.store.on('beforeload', this.onBeforeLoad, this);
15693         this.store.on('load', this.onLoad, this);
15694         this.store.on('loadexception', this.onLoadException, this);
15695         /*
15696         if(this.resizable){
15697             this.resizer = new Roo.Resizable(this.list,  {
15698                pinned:true, handles:'se'
15699             });
15700             this.resizer.on('resize', function(r, w, h){
15701                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15702                 this.listWidth = w;
15703                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15704                 this.restrictHeight();
15705             }, this);
15706             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15707         }
15708         */
15709         if(!this.editable){
15710             this.editable = true;
15711             this.setEditable(false);
15712         }
15713         
15714         /*
15715         
15716         if (typeof(this.events.add.listeners) != 'undefined') {
15717             
15718             this.addicon = this.wrap.createChild(
15719                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15720        
15721             this.addicon.on('click', function(e) {
15722                 this.fireEvent('add', this);
15723             }, this);
15724         }
15725         if (typeof(this.events.edit.listeners) != 'undefined') {
15726             
15727             this.editicon = this.wrap.createChild(
15728                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15729             if (this.addicon) {
15730                 this.editicon.setStyle('margin-left', '40px');
15731             }
15732             this.editicon.on('click', function(e) {
15733                 
15734                 // we fire even  if inothing is selected..
15735                 this.fireEvent('edit', this, this.lastData );
15736                 
15737             }, this);
15738         }
15739         */
15740         
15741         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15742             "up" : function(e){
15743                 this.inKeyMode = true;
15744                 this.selectPrev();
15745             },
15746
15747             "down" : function(e){
15748                 if(!this.isExpanded()){
15749                     this.onTriggerClick();
15750                 }else{
15751                     this.inKeyMode = true;
15752                     this.selectNext();
15753                 }
15754             },
15755
15756             "enter" : function(e){
15757 //                this.onViewClick();
15758                 //return true;
15759                 this.collapse();
15760                 
15761                 if(this.fireEvent("specialkey", this, e)){
15762                     this.onViewClick(false);
15763                 }
15764                 
15765                 return true;
15766             },
15767
15768             "esc" : function(e){
15769                 this.collapse();
15770             },
15771
15772             "tab" : function(e){
15773                 this.collapse();
15774                 
15775                 if(this.fireEvent("specialkey", this, e)){
15776                     this.onViewClick(false);
15777                 }
15778                 
15779                 return true;
15780             },
15781
15782             scope : this,
15783
15784             doRelay : function(foo, bar, hname){
15785                 if(hname == 'down' || this.scope.isExpanded()){
15786                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15787                 }
15788                 return true;
15789             },
15790
15791             forceKeyDown: true
15792         });
15793         
15794         
15795         this.queryDelay = Math.max(this.queryDelay || 10,
15796                 this.mode == 'local' ? 10 : 250);
15797         
15798         
15799         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15800         
15801         if(this.typeAhead){
15802             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15803         }
15804         if(this.editable !== false){
15805             this.inputEl().on("keyup", this.onKeyUp, this);
15806         }
15807         if(this.forceSelection){
15808             this.inputEl().on('blur', this.doForce, this);
15809         }
15810         
15811         if(this.multiple){
15812             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15813             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15814         }
15815     },
15816     
15817     initTickableEvents: function()
15818     {   
15819         this.createList();
15820         
15821         if(this.hiddenName){
15822             
15823             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15824             
15825             this.hiddenField.dom.value =
15826                 this.hiddenValue !== undefined ? this.hiddenValue :
15827                 this.value !== undefined ? this.value : '';
15828
15829             // prevent input submission
15830             this.el.dom.removeAttribute('name');
15831             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15832              
15833              
15834         }
15835         
15836 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15837         
15838         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15839         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15840         if(this.triggerList){
15841             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15842         }
15843          
15844         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15845         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15846         
15847         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15848         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15849         
15850         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15851         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15852         
15853         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15854         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15855         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15856         
15857         this.okBtn.hide();
15858         this.cancelBtn.hide();
15859         
15860         var _this = this;
15861         
15862         (function(){
15863             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15864             _this.list.setWidth(lw);
15865         }).defer(100);
15866         
15867         this.list.on('mouseover', this.onViewOver, this);
15868         this.list.on('mousemove', this.onViewMove, this);
15869         
15870         this.list.on('scroll', this.onViewScroll, this);
15871         
15872         if(!this.tpl){
15873             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15874                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15875         }
15876
15877         this.view = new Roo.View(this.list, this.tpl, {
15878             singleSelect:true,
15879             tickable:true,
15880             parent:this,
15881             store: this.store,
15882             selectedClass: this.selectedClass
15883         });
15884         
15885         //this.view.wrapEl.setDisplayed(false);
15886         this.view.on('click', this.onViewClick, this);
15887         
15888         
15889         
15890         this.store.on('beforeload', this.onBeforeLoad, this);
15891         this.store.on('load', this.onLoad, this);
15892         this.store.on('loadexception', this.onLoadException, this);
15893         
15894         if(this.editable){
15895             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15896                 "up" : function(e){
15897                     this.inKeyMode = true;
15898                     this.selectPrev();
15899                 },
15900
15901                 "down" : function(e){
15902                     this.inKeyMode = true;
15903                     this.selectNext();
15904                 },
15905
15906                 "enter" : function(e){
15907                     if(this.fireEvent("specialkey", this, e)){
15908                         this.onViewClick(false);
15909                     }
15910                     
15911                     return true;
15912                 },
15913
15914                 "esc" : function(e){
15915                     this.onTickableFooterButtonClick(e, false, false);
15916                 },
15917
15918                 "tab" : function(e){
15919                     this.fireEvent("specialkey", this, e);
15920                     
15921                     this.onTickableFooterButtonClick(e, false, false);
15922                     
15923                     return true;
15924                 },
15925
15926                 scope : this,
15927
15928                 doRelay : function(e, fn, key){
15929                     if(this.scope.isExpanded()){
15930                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15931                     }
15932                     return true;
15933                 },
15934
15935                 forceKeyDown: true
15936             });
15937         }
15938         
15939         this.queryDelay = Math.max(this.queryDelay || 10,
15940                 this.mode == 'local' ? 10 : 250);
15941         
15942         
15943         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15944         
15945         if(this.typeAhead){
15946             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15947         }
15948         
15949         if(this.editable !== false){
15950             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15951         }
15952         
15953         this.indicator = this.indicatorEl();
15954         
15955         if(this.indicator){
15956             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15957             this.indicator.hide();
15958         }
15959         
15960     },
15961
15962     onDestroy : function(){
15963         if(this.view){
15964             this.view.setStore(null);
15965             this.view.el.removeAllListeners();
15966             this.view.el.remove();
15967             this.view.purgeListeners();
15968         }
15969         if(this.list){
15970             this.list.dom.innerHTML  = '';
15971         }
15972         
15973         if(this.store){
15974             this.store.un('beforeload', this.onBeforeLoad, this);
15975             this.store.un('load', this.onLoad, this);
15976             this.store.un('loadexception', this.onLoadException, this);
15977         }
15978         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15979     },
15980
15981     // private
15982     fireKey : function(e){
15983         if(e.isNavKeyPress() && !this.list.isVisible()){
15984             this.fireEvent("specialkey", this, e);
15985         }
15986     },
15987
15988     // private
15989     onResize: function(w, h)
15990     {
15991         
15992         
15993 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15994 //        
15995 //        if(typeof w != 'number'){
15996 //            // we do not handle it!?!?
15997 //            return;
15998 //        }
15999 //        var tw = this.trigger.getWidth();
16000 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16001 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16002 //        var x = w - tw;
16003 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16004 //            
16005 //        //this.trigger.setStyle('left', x+'px');
16006 //        
16007 //        if(this.list && this.listWidth === undefined){
16008 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16009 //            this.list.setWidth(lw);
16010 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16011 //        }
16012         
16013     
16014         
16015     },
16016
16017     /**
16018      * Allow or prevent the user from directly editing the field text.  If false is passed,
16019      * the user will only be able to select from the items defined in the dropdown list.  This method
16020      * is the runtime equivalent of setting the 'editable' config option at config time.
16021      * @param {Boolean} value True to allow the user to directly edit the field text
16022      */
16023     setEditable : function(value){
16024         if(value == this.editable){
16025             return;
16026         }
16027         this.editable = value;
16028         if(!value){
16029             this.inputEl().dom.setAttribute('readOnly', true);
16030             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16031             this.inputEl().addClass('x-combo-noedit');
16032         }else{
16033             this.inputEl().dom.setAttribute('readOnly', false);
16034             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16035             this.inputEl().removeClass('x-combo-noedit');
16036         }
16037     },
16038
16039     // private
16040     
16041     onBeforeLoad : function(combo,opts){
16042         if(!this.hasFocus){
16043             return;
16044         }
16045          if (!opts.add) {
16046             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16047          }
16048         this.restrictHeight();
16049         this.selectedIndex = -1;
16050     },
16051
16052     // private
16053     onLoad : function(){
16054         
16055         this.hasQuery = false;
16056         
16057         if(!this.hasFocus){
16058             return;
16059         }
16060         
16061         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16062             this.loading.hide();
16063         }
16064         
16065         if(this.store.getCount() > 0){
16066             
16067             this.expand();
16068             this.restrictHeight();
16069             if(this.lastQuery == this.allQuery){
16070                 if(this.editable && !this.tickable){
16071                     this.inputEl().dom.select();
16072                 }
16073                 
16074                 if(
16075                     !this.selectByValue(this.value, true) &&
16076                     this.autoFocus && 
16077                     (
16078                         !this.store.lastOptions ||
16079                         typeof(this.store.lastOptions.add) == 'undefined' || 
16080                         this.store.lastOptions.add != true
16081                     )
16082                 ){
16083                     this.select(0, true);
16084                 }
16085             }else{
16086                 if(this.autoFocus){
16087                     this.selectNext();
16088                 }
16089                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16090                     this.taTask.delay(this.typeAheadDelay);
16091                 }
16092             }
16093         }else{
16094             this.onEmptyResults();
16095         }
16096         
16097         //this.el.focus();
16098     },
16099     // private
16100     onLoadException : function()
16101     {
16102         this.hasQuery = false;
16103         
16104         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16105             this.loading.hide();
16106         }
16107         
16108         if(this.tickable && this.editable){
16109             return;
16110         }
16111         
16112         this.collapse();
16113         // only causes errors at present
16114         //Roo.log(this.store.reader.jsonData);
16115         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16116             // fixme
16117             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16118         //}
16119         
16120         
16121     },
16122     // private
16123     onTypeAhead : function(){
16124         if(this.store.getCount() > 0){
16125             var r = this.store.getAt(0);
16126             var newValue = r.data[this.displayField];
16127             var len = newValue.length;
16128             var selStart = this.getRawValue().length;
16129             
16130             if(selStart != len){
16131                 this.setRawValue(newValue);
16132                 this.selectText(selStart, newValue.length);
16133             }
16134         }
16135     },
16136
16137     // private
16138     onSelect : function(record, index){
16139         
16140         if(this.fireEvent('beforeselect', this, record, index) !== false){
16141         
16142             this.setFromData(index > -1 ? record.data : false);
16143             
16144             this.collapse();
16145             this.fireEvent('select', this, record, index);
16146         }
16147     },
16148
16149     /**
16150      * Returns the currently selected field value or empty string if no value is set.
16151      * @return {String} value The selected value
16152      */
16153     getValue : function()
16154     {
16155         if(Roo.isIOS && this.useNativeIOS){
16156             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16157         }
16158         
16159         if(this.multiple){
16160             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16161         }
16162         
16163         if(this.valueField){
16164             return typeof this.value != 'undefined' ? this.value : '';
16165         }else{
16166             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16167         }
16168     },
16169     
16170     getRawValue : function()
16171     {
16172         if(Roo.isIOS && this.useNativeIOS){
16173             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16174         }
16175         
16176         var v = this.inputEl().getValue();
16177         
16178         return v;
16179     },
16180
16181     /**
16182      * Clears any text/value currently set in the field
16183      */
16184     clearValue : function(){
16185         
16186         if(this.hiddenField){
16187             this.hiddenField.dom.value = '';
16188         }
16189         this.value = '';
16190         this.setRawValue('');
16191         this.lastSelectionText = '';
16192         this.lastData = false;
16193         
16194         var close = this.closeTriggerEl();
16195         
16196         if(close){
16197             close.hide();
16198         }
16199         
16200         this.validate();
16201         
16202     },
16203
16204     /**
16205      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16206      * will be displayed in the field.  If the value does not match the data value of an existing item,
16207      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16208      * Otherwise the field will be blank (although the value will still be set).
16209      * @param {String} value The value to match
16210      */
16211     setValue : function(v)
16212     {
16213         if(Roo.isIOS && this.useNativeIOS){
16214             this.setIOSValue(v);
16215             return;
16216         }
16217         
16218         if(this.multiple){
16219             this.syncValue();
16220             return;
16221         }
16222         
16223         var text = v;
16224         if(this.valueField){
16225             var r = this.findRecord(this.valueField, v);
16226             if(r){
16227                 text = r.data[this.displayField];
16228             }else if(this.valueNotFoundText !== undefined){
16229                 text = this.valueNotFoundText;
16230             }
16231         }
16232         this.lastSelectionText = text;
16233         if(this.hiddenField){
16234             this.hiddenField.dom.value = v;
16235         }
16236         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16237         this.value = v;
16238         
16239         var close = this.closeTriggerEl();
16240         
16241         if(close){
16242             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16243         }
16244         
16245         this.validate();
16246     },
16247     /**
16248      * @property {Object} the last set data for the element
16249      */
16250     
16251     lastData : false,
16252     /**
16253      * Sets the value of the field based on a object which is related to the record format for the store.
16254      * @param {Object} value the value to set as. or false on reset?
16255      */
16256     setFromData : function(o){
16257         
16258         if(this.multiple){
16259             this.addItem(o);
16260             return;
16261         }
16262             
16263         var dv = ''; // display value
16264         var vv = ''; // value value..
16265         this.lastData = o;
16266         if (this.displayField) {
16267             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16268         } else {
16269             // this is an error condition!!!
16270             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16271         }
16272         
16273         if(this.valueField){
16274             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16275         }
16276         
16277         var close = this.closeTriggerEl();
16278         
16279         if(close){
16280             if(dv.length || vv * 1 > 0){
16281                 close.show() ;
16282                 this.blockFocus=true;
16283             } else {
16284                 close.hide();
16285             }             
16286         }
16287         
16288         if(this.hiddenField){
16289             this.hiddenField.dom.value = vv;
16290             
16291             this.lastSelectionText = dv;
16292             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16293             this.value = vv;
16294             return;
16295         }
16296         // no hidden field.. - we store the value in 'value', but still display
16297         // display field!!!!
16298         this.lastSelectionText = dv;
16299         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16300         this.value = vv;
16301         
16302         
16303         
16304     },
16305     // private
16306     reset : function(){
16307         // overridden so that last data is reset..
16308         
16309         if(this.multiple){
16310             this.clearItem();
16311             return;
16312         }
16313         
16314         this.setValue(this.originalValue);
16315         //this.clearInvalid();
16316         this.lastData = false;
16317         if (this.view) {
16318             this.view.clearSelections();
16319         }
16320         
16321         this.validate();
16322     },
16323     // private
16324     findRecord : function(prop, value){
16325         var record;
16326         if(this.store.getCount() > 0){
16327             this.store.each(function(r){
16328                 if(r.data[prop] == value){
16329                     record = r;
16330                     return false;
16331                 }
16332                 return true;
16333             });
16334         }
16335         return record;
16336     },
16337     
16338     getName: function()
16339     {
16340         // returns hidden if it's set..
16341         if (!this.rendered) {return ''};
16342         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16343         
16344     },
16345     // private
16346     onViewMove : function(e, t){
16347         this.inKeyMode = false;
16348     },
16349
16350     // private
16351     onViewOver : function(e, t){
16352         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16353             return;
16354         }
16355         var item = this.view.findItemFromChild(t);
16356         
16357         if(item){
16358             var index = this.view.indexOf(item);
16359             this.select(index, false);
16360         }
16361     },
16362
16363     // private
16364     onViewClick : function(view, doFocus, el, e)
16365     {
16366         var index = this.view.getSelectedIndexes()[0];
16367         
16368         var r = this.store.getAt(index);
16369         
16370         if(this.tickable){
16371             
16372             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16373                 return;
16374             }
16375             
16376             var rm = false;
16377             var _this = this;
16378             
16379             Roo.each(this.tickItems, function(v,k){
16380                 
16381                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16382                     Roo.log(v);
16383                     _this.tickItems.splice(k, 1);
16384                     
16385                     if(typeof(e) == 'undefined' && view == false){
16386                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16387                     }
16388                     
16389                     rm = true;
16390                     return;
16391                 }
16392             });
16393             
16394             if(rm){
16395                 return;
16396             }
16397             
16398             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16399                 this.tickItems.push(r.data);
16400             }
16401             
16402             if(typeof(e) == 'undefined' && view == false){
16403                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16404             }
16405                     
16406             return;
16407         }
16408         
16409         if(r){
16410             this.onSelect(r, index);
16411         }
16412         if(doFocus !== false && !this.blockFocus){
16413             this.inputEl().focus();
16414         }
16415     },
16416
16417     // private
16418     restrictHeight : function(){
16419         //this.innerList.dom.style.height = '';
16420         //var inner = this.innerList.dom;
16421         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16422         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16423         //this.list.beginUpdate();
16424         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16425         this.list.alignTo(this.inputEl(), this.listAlign);
16426         this.list.alignTo(this.inputEl(), this.listAlign);
16427         //this.list.endUpdate();
16428     },
16429
16430     // private
16431     onEmptyResults : function(){
16432         
16433         if(this.tickable && this.editable){
16434             this.hasFocus = false;
16435             this.restrictHeight();
16436             return;
16437         }
16438         
16439         this.collapse();
16440     },
16441
16442     /**
16443      * Returns true if the dropdown list is expanded, else false.
16444      */
16445     isExpanded : function(){
16446         return this.list.isVisible();
16447     },
16448
16449     /**
16450      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16451      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16452      * @param {String} value The data value of the item to select
16453      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16454      * selected item if it is not currently in view (defaults to true)
16455      * @return {Boolean} True if the value matched an item in the list, else false
16456      */
16457     selectByValue : function(v, scrollIntoView){
16458         if(v !== undefined && v !== null){
16459             var r = this.findRecord(this.valueField || this.displayField, v);
16460             if(r){
16461                 this.select(this.store.indexOf(r), scrollIntoView);
16462                 return true;
16463             }
16464         }
16465         return false;
16466     },
16467
16468     /**
16469      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16470      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16471      * @param {Number} index The zero-based index of the list item to select
16472      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16473      * selected item if it is not currently in view (defaults to true)
16474      */
16475     select : function(index, scrollIntoView){
16476         this.selectedIndex = index;
16477         this.view.select(index);
16478         if(scrollIntoView !== false){
16479             var el = this.view.getNode(index);
16480             /*
16481              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16482              */
16483             if(el){
16484                 this.list.scrollChildIntoView(el, false);
16485             }
16486         }
16487     },
16488
16489     // private
16490     selectNext : function(){
16491         var ct = this.store.getCount();
16492         if(ct > 0){
16493             if(this.selectedIndex == -1){
16494                 this.select(0);
16495             }else if(this.selectedIndex < ct-1){
16496                 this.select(this.selectedIndex+1);
16497             }
16498         }
16499     },
16500
16501     // private
16502     selectPrev : function(){
16503         var ct = this.store.getCount();
16504         if(ct > 0){
16505             if(this.selectedIndex == -1){
16506                 this.select(0);
16507             }else if(this.selectedIndex != 0){
16508                 this.select(this.selectedIndex-1);
16509             }
16510         }
16511     },
16512
16513     // private
16514     onKeyUp : function(e){
16515         if(this.editable !== false && !e.isSpecialKey()){
16516             this.lastKey = e.getKey();
16517             this.dqTask.delay(this.queryDelay);
16518         }
16519     },
16520
16521     // private
16522     validateBlur : function(){
16523         return !this.list || !this.list.isVisible();   
16524     },
16525
16526     // private
16527     initQuery : function(){
16528         
16529         var v = this.getRawValue();
16530         
16531         if(this.tickable && this.editable){
16532             v = this.tickableInputEl().getValue();
16533         }
16534         
16535         this.doQuery(v);
16536     },
16537
16538     // private
16539     doForce : function(){
16540         if(this.inputEl().dom.value.length > 0){
16541             this.inputEl().dom.value =
16542                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16543              
16544         }
16545     },
16546
16547     /**
16548      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16549      * query allowing the query action to be canceled if needed.
16550      * @param {String} query The SQL query to execute
16551      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16552      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16553      * saved in the current store (defaults to false)
16554      */
16555     doQuery : function(q, forceAll){
16556         
16557         if(q === undefined || q === null){
16558             q = '';
16559         }
16560         var qe = {
16561             query: q,
16562             forceAll: forceAll,
16563             combo: this,
16564             cancel:false
16565         };
16566         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16567             return false;
16568         }
16569         q = qe.query;
16570         
16571         forceAll = qe.forceAll;
16572         if(forceAll === true || (q.length >= this.minChars)){
16573             
16574             this.hasQuery = true;
16575             
16576             if(this.lastQuery != q || this.alwaysQuery){
16577                 this.lastQuery = q;
16578                 if(this.mode == 'local'){
16579                     this.selectedIndex = -1;
16580                     if(forceAll){
16581                         this.store.clearFilter();
16582                     }else{
16583                         
16584                         if(this.specialFilter){
16585                             this.fireEvent('specialfilter', this);
16586                             this.onLoad();
16587                             return;
16588                         }
16589                         
16590                         this.store.filter(this.displayField, q);
16591                     }
16592                     
16593                     this.store.fireEvent("datachanged", this.store);
16594                     
16595                     this.onLoad();
16596                     
16597                     
16598                 }else{
16599                     
16600                     this.store.baseParams[this.queryParam] = q;
16601                     
16602                     var options = {params : this.getParams(q)};
16603                     
16604                     if(this.loadNext){
16605                         options.add = true;
16606                         options.params.start = this.page * this.pageSize;
16607                     }
16608                     
16609                     this.store.load(options);
16610                     
16611                     /*
16612                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16613                      *  we should expand the list on onLoad
16614                      *  so command out it
16615                      */
16616 //                    this.expand();
16617                 }
16618             }else{
16619                 this.selectedIndex = -1;
16620                 this.onLoad();   
16621             }
16622         }
16623         
16624         this.loadNext = false;
16625     },
16626     
16627     // private
16628     getParams : function(q){
16629         var p = {};
16630         //p[this.queryParam] = q;
16631         
16632         if(this.pageSize){
16633             p.start = 0;
16634             p.limit = this.pageSize;
16635         }
16636         return p;
16637     },
16638
16639     /**
16640      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16641      */
16642     collapse : function(){
16643         if(!this.isExpanded()){
16644             return;
16645         }
16646         
16647         this.list.hide();
16648         
16649         this.hasFocus = false;
16650         
16651         if(this.tickable){
16652             this.okBtn.hide();
16653             this.cancelBtn.hide();
16654             this.trigger.show();
16655             
16656             if(this.editable){
16657                 this.tickableInputEl().dom.value = '';
16658                 this.tickableInputEl().blur();
16659             }
16660             
16661         }
16662         
16663         Roo.get(document).un('mousedown', this.collapseIf, this);
16664         Roo.get(document).un('mousewheel', this.collapseIf, this);
16665         if (!this.editable) {
16666             Roo.get(document).un('keydown', this.listKeyPress, this);
16667         }
16668         this.fireEvent('collapse', this);
16669         
16670         this.validate();
16671     },
16672
16673     // private
16674     collapseIf : function(e){
16675         var in_combo  = e.within(this.el);
16676         var in_list =  e.within(this.list);
16677         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16678         
16679         if (in_combo || in_list || is_list) {
16680             //e.stopPropagation();
16681             return;
16682         }
16683         
16684         if(this.tickable){
16685             this.onTickableFooterButtonClick(e, false, false);
16686         }
16687
16688         this.collapse();
16689         
16690     },
16691
16692     /**
16693      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16694      */
16695     expand : function(){
16696        
16697         if(this.isExpanded() || !this.hasFocus){
16698             return;
16699         }
16700         
16701         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16702         this.list.setWidth(lw);
16703         
16704         Roo.log('expand');
16705         
16706         this.list.show();
16707         
16708         this.restrictHeight();
16709         
16710         if(this.tickable){
16711             
16712             this.tickItems = Roo.apply([], this.item);
16713             
16714             this.okBtn.show();
16715             this.cancelBtn.show();
16716             this.trigger.hide();
16717             
16718             if(this.editable){
16719                 this.tickableInputEl().focus();
16720             }
16721             
16722         }
16723         
16724         Roo.get(document).on('mousedown', this.collapseIf, this);
16725         Roo.get(document).on('mousewheel', this.collapseIf, this);
16726         if (!this.editable) {
16727             Roo.get(document).on('keydown', this.listKeyPress, this);
16728         }
16729         
16730         this.fireEvent('expand', this);
16731     },
16732
16733     // private
16734     // Implements the default empty TriggerField.onTriggerClick function
16735     onTriggerClick : function(e)
16736     {
16737         Roo.log('trigger click');
16738         
16739         if(this.disabled || !this.triggerList){
16740             return;
16741         }
16742         
16743         this.page = 0;
16744         this.loadNext = false;
16745         
16746         if(this.isExpanded()){
16747             this.collapse();
16748             if (!this.blockFocus) {
16749                 this.inputEl().focus();
16750             }
16751             
16752         }else {
16753             this.hasFocus = true;
16754             if(this.triggerAction == 'all') {
16755                 this.doQuery(this.allQuery, true);
16756             } else {
16757                 this.doQuery(this.getRawValue());
16758             }
16759             if (!this.blockFocus) {
16760                 this.inputEl().focus();
16761             }
16762         }
16763     },
16764     
16765     onTickableTriggerClick : function(e)
16766     {
16767         if(this.disabled){
16768             return;
16769         }
16770         
16771         this.page = 0;
16772         this.loadNext = false;
16773         this.hasFocus = true;
16774         
16775         if(this.triggerAction == 'all') {
16776             this.doQuery(this.allQuery, true);
16777         } else {
16778             this.doQuery(this.getRawValue());
16779         }
16780     },
16781     
16782     onSearchFieldClick : function(e)
16783     {
16784         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16785             this.onTickableFooterButtonClick(e, false, false);
16786             return;
16787         }
16788         
16789         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16790             return;
16791         }
16792         
16793         this.page = 0;
16794         this.loadNext = false;
16795         this.hasFocus = true;
16796         
16797         if(this.triggerAction == 'all') {
16798             this.doQuery(this.allQuery, true);
16799         } else {
16800             this.doQuery(this.getRawValue());
16801         }
16802     },
16803     
16804     listKeyPress : function(e)
16805     {
16806         //Roo.log('listkeypress');
16807         // scroll to first matching element based on key pres..
16808         if (e.isSpecialKey()) {
16809             return false;
16810         }
16811         var k = String.fromCharCode(e.getKey()).toUpperCase();
16812         //Roo.log(k);
16813         var match  = false;
16814         var csel = this.view.getSelectedNodes();
16815         var cselitem = false;
16816         if (csel.length) {
16817             var ix = this.view.indexOf(csel[0]);
16818             cselitem  = this.store.getAt(ix);
16819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16820                 cselitem = false;
16821             }
16822             
16823         }
16824         
16825         this.store.each(function(v) { 
16826             if (cselitem) {
16827                 // start at existing selection.
16828                 if (cselitem.id == v.id) {
16829                     cselitem = false;
16830                 }
16831                 return true;
16832             }
16833                 
16834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16835                 match = this.store.indexOf(v);
16836                 return false;
16837             }
16838             return true;
16839         }, this);
16840         
16841         if (match === false) {
16842             return true; // no more action?
16843         }
16844         // scroll to?
16845         this.view.select(match);
16846         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16847         sn.scrollIntoView(sn.dom.parentNode, false);
16848     },
16849     
16850     onViewScroll : function(e, t){
16851         
16852         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){
16853             return;
16854         }
16855         
16856         this.hasQuery = true;
16857         
16858         this.loading = this.list.select('.loading', true).first();
16859         
16860         if(this.loading === null){
16861             this.list.createChild({
16862                 tag: 'div',
16863                 cls: 'loading roo-select2-more-results roo-select2-active',
16864                 html: 'Loading more results...'
16865             });
16866             
16867             this.loading = this.list.select('.loading', true).first();
16868             
16869             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16870             
16871             this.loading.hide();
16872         }
16873         
16874         this.loading.show();
16875         
16876         var _combo = this;
16877         
16878         this.page++;
16879         this.loadNext = true;
16880         
16881         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16882         
16883         return;
16884     },
16885     
16886     addItem : function(o)
16887     {   
16888         var dv = ''; // display value
16889         
16890         if (this.displayField) {
16891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16892         } else {
16893             // this is an error condition!!!
16894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16895         }
16896         
16897         if(!dv.length){
16898             return;
16899         }
16900         
16901         var choice = this.choices.createChild({
16902             tag: 'li',
16903             cls: 'roo-select2-search-choice',
16904             cn: [
16905                 {
16906                     tag: 'div',
16907                     html: dv
16908                 },
16909                 {
16910                     tag: 'a',
16911                     href: '#',
16912                     cls: 'roo-select2-search-choice-close fa fa-times',
16913                     tabindex: '-1'
16914                 }
16915             ]
16916             
16917         }, this.searchField);
16918         
16919         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16920         
16921         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16922         
16923         this.item.push(o);
16924         
16925         this.lastData = o;
16926         
16927         this.syncValue();
16928         
16929         this.inputEl().dom.value = '';
16930         
16931         this.validate();
16932     },
16933     
16934     onRemoveItem : function(e, _self, o)
16935     {
16936         e.preventDefault();
16937         
16938         this.lastItem = Roo.apply([], this.item);
16939         
16940         var index = this.item.indexOf(o.data) * 1;
16941         
16942         if( index < 0){
16943             Roo.log('not this item?!');
16944             return;
16945         }
16946         
16947         this.item.splice(index, 1);
16948         o.item.remove();
16949         
16950         this.syncValue();
16951         
16952         this.fireEvent('remove', this, e);
16953         
16954         this.validate();
16955         
16956     },
16957     
16958     syncValue : function()
16959     {
16960         if(!this.item.length){
16961             this.clearValue();
16962             return;
16963         }
16964             
16965         var value = [];
16966         var _this = this;
16967         Roo.each(this.item, function(i){
16968             if(_this.valueField){
16969                 value.push(i[_this.valueField]);
16970                 return;
16971             }
16972
16973             value.push(i);
16974         });
16975
16976         this.value = value.join(',');
16977
16978         if(this.hiddenField){
16979             this.hiddenField.dom.value = this.value;
16980         }
16981         
16982         this.store.fireEvent("datachanged", this.store);
16983         
16984         this.validate();
16985     },
16986     
16987     clearItem : function()
16988     {
16989         if(!this.multiple){
16990             return;
16991         }
16992         
16993         this.item = [];
16994         
16995         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16996            c.remove();
16997         });
16998         
16999         this.syncValue();
17000         
17001         this.validate();
17002         
17003         if(this.tickable && !Roo.isTouch){
17004             this.view.refresh();
17005         }
17006     },
17007     
17008     inputEl: function ()
17009     {
17010         if(Roo.isIOS && this.useNativeIOS){
17011             return this.el.select('select.roo-ios-select', true).first();
17012         }
17013         
17014         if(Roo.isTouch && this.mobileTouchView){
17015             return this.el.select('input.form-control',true).first();
17016         }
17017         
17018         if(this.tickable){
17019             return this.searchField;
17020         }
17021         
17022         return this.el.select('input.form-control',true).first();
17023     },
17024     
17025     onTickableFooterButtonClick : function(e, btn, el)
17026     {
17027         e.preventDefault();
17028         
17029         this.lastItem = Roo.apply([], this.item);
17030         
17031         if(btn && btn.name == 'cancel'){
17032             this.tickItems = Roo.apply([], this.item);
17033             this.collapse();
17034             return;
17035         }
17036         
17037         this.clearItem();
17038         
17039         var _this = this;
17040         
17041         Roo.each(this.tickItems, function(o){
17042             _this.addItem(o);
17043         });
17044         
17045         this.collapse();
17046         
17047     },
17048     
17049     validate : function()
17050     {
17051         if(this.getVisibilityEl().hasClass('hidden')){
17052             return true;
17053         }
17054         
17055         var v = this.getRawValue();
17056         
17057         if(this.multiple){
17058             v = this.getValue();
17059         }
17060         
17061         if(this.disabled || this.allowBlank || v.length){
17062             this.markValid();
17063             return true;
17064         }
17065         
17066         this.markInvalid();
17067         return false;
17068     },
17069     
17070     tickableInputEl : function()
17071     {
17072         if(!this.tickable || !this.editable){
17073             return this.inputEl();
17074         }
17075         
17076         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17077     },
17078     
17079     
17080     getAutoCreateTouchView : function()
17081     {
17082         var id = Roo.id();
17083         
17084         var cfg = {
17085             cls: 'form-group' //input-group
17086         };
17087         
17088         var input =  {
17089             tag: 'input',
17090             id : id,
17091             type : this.inputType,
17092             cls : 'form-control x-combo-noedit',
17093             autocomplete: 'new-password',
17094             placeholder : this.placeholder || '',
17095             readonly : true
17096         };
17097         
17098         if (this.name) {
17099             input.name = this.name;
17100         }
17101         
17102         if (this.size) {
17103             input.cls += ' input-' + this.size;
17104         }
17105         
17106         if (this.disabled) {
17107             input.disabled = true;
17108         }
17109         
17110         var inputblock = {
17111             cls : 'roo-combobox-wrap',
17112             cn : [
17113                 input
17114             ]
17115         };
17116         
17117         if(this.before){
17118             inputblock.cls += ' input-group';
17119             
17120             inputblock.cn.unshift({
17121                 tag :'span',
17122                 cls : 'input-group-addon input-group-prepend input-group-text',
17123                 html : this.before
17124             });
17125         }
17126         
17127         if(this.removable && !this.multiple){
17128             inputblock.cls += ' roo-removable';
17129             
17130             inputblock.cn.push({
17131                 tag: 'button',
17132                 html : 'x',
17133                 cls : 'roo-combo-removable-btn close'
17134             });
17135         }
17136
17137         if(this.hasFeedback && !this.allowBlank){
17138             
17139             inputblock.cls += ' has-feedback';
17140             
17141             inputblock.cn.push({
17142                 tag: 'span',
17143                 cls: 'glyphicon form-control-feedback'
17144             });
17145             
17146         }
17147         
17148         if (this.after) {
17149             
17150             inputblock.cls += (this.before) ? '' : ' input-group';
17151             
17152             inputblock.cn.push({
17153                 tag :'span',
17154                 cls : 'input-group-addon input-group-append input-group-text',
17155                 html : this.after
17156             });
17157         }
17158
17159         
17160         var ibwrap = inputblock;
17161         
17162         if(this.multiple){
17163             ibwrap = {
17164                 tag: 'ul',
17165                 cls: 'roo-select2-choices',
17166                 cn:[
17167                     {
17168                         tag: 'li',
17169                         cls: 'roo-select2-search-field',
17170                         cn: [
17171
17172                             inputblock
17173                         ]
17174                     }
17175                 ]
17176             };
17177         
17178             
17179         }
17180         
17181         var combobox = {
17182             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17183             cn: [
17184                 {
17185                     tag: 'input',
17186                     type : 'hidden',
17187                     cls: 'form-hidden-field'
17188                 },
17189                 ibwrap
17190             ]
17191         };
17192         
17193         if(!this.multiple && this.showToggleBtn){
17194             
17195             var caret = {
17196                 cls: 'caret'
17197             };
17198             
17199             if (this.caret != false) {
17200                 caret = {
17201                      tag: 'i',
17202                      cls: 'fa fa-' + this.caret
17203                 };
17204                 
17205             }
17206             
17207             combobox.cn.push({
17208                 tag :'span',
17209                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17210                 cn : [
17211                     Roo.bootstrap.version == 3 ? caret : '',
17212                     {
17213                         tag: 'span',
17214                         cls: 'combobox-clear',
17215                         cn  : [
17216                             {
17217                                 tag : 'i',
17218                                 cls: 'icon-remove'
17219                             }
17220                         ]
17221                     }
17222                 ]
17223
17224             })
17225         }
17226         
17227         if(this.multiple){
17228             combobox.cls += ' roo-select2-container-multi';
17229         }
17230         
17231         var align = this.labelAlign || this.parentLabelAlign();
17232         
17233         if (align ==='left' && this.fieldLabel.length) {
17234
17235             cfg.cn = [
17236                 {
17237                    tag : 'i',
17238                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17239                    tooltip : 'This field is required'
17240                 },
17241                 {
17242                     tag: 'label',
17243                     cls : 'control-label col-form-label',
17244                     html : this.fieldLabel
17245
17246                 },
17247                 {
17248                     cls : 'roo-combobox-wrap ', 
17249                     cn: [
17250                         combobox
17251                     ]
17252                 }
17253             ];
17254             
17255             var labelCfg = cfg.cn[1];
17256             var contentCfg = cfg.cn[2];
17257             
17258
17259             if(this.indicatorpos == 'right'){
17260                 cfg.cn = [
17261                     {
17262                         tag: 'label',
17263                         'for' :  id,
17264                         cls : 'control-label col-form-label',
17265                         cn : [
17266                             {
17267                                 tag : 'span',
17268                                 html : this.fieldLabel
17269                             },
17270                             {
17271                                 tag : 'i',
17272                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17273                                 tooltip : 'This field is required'
17274                             }
17275                         ]
17276                     },
17277                     {
17278                         cls : "roo-combobox-wrap ",
17279                         cn: [
17280                             combobox
17281                         ]
17282                     }
17283
17284                 ];
17285                 
17286                 labelCfg = cfg.cn[0];
17287                 contentCfg = cfg.cn[1];
17288             }
17289             
17290            
17291             
17292             if(this.labelWidth > 12){
17293                 labelCfg.style = "width: " + this.labelWidth + 'px';
17294             }
17295            
17296             if(this.labelWidth < 13 && this.labelmd == 0){
17297                 this.labelmd = this.labelWidth;
17298             }
17299             
17300             if(this.labellg > 0){
17301                 labelCfg.cls += ' col-lg-' + this.labellg;
17302                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17303             }
17304             
17305             if(this.labelmd > 0){
17306                 labelCfg.cls += ' col-md-' + this.labelmd;
17307                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17308             }
17309             
17310             if(this.labelsm > 0){
17311                 labelCfg.cls += ' col-sm-' + this.labelsm;
17312                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17313             }
17314             
17315             if(this.labelxs > 0){
17316                 labelCfg.cls += ' col-xs-' + this.labelxs;
17317                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17318             }
17319                 
17320                 
17321         } else if ( this.fieldLabel.length) {
17322             cfg.cn = [
17323                 {
17324                    tag : 'i',
17325                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17326                    tooltip : 'This field is required'
17327                 },
17328                 {
17329                     tag: 'label',
17330                     cls : 'control-label',
17331                     html : this.fieldLabel
17332
17333                 },
17334                 {
17335                     cls : '', 
17336                     cn: [
17337                         combobox
17338                     ]
17339                 }
17340             ];
17341             
17342             if(this.indicatorpos == 'right'){
17343                 cfg.cn = [
17344                     {
17345                         tag: 'label',
17346                         cls : 'control-label',
17347                         html : this.fieldLabel,
17348                         cn : [
17349                             {
17350                                tag : 'i',
17351                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17352                                tooltip : 'This field is required'
17353                             }
17354                         ]
17355                     },
17356                     {
17357                         cls : '', 
17358                         cn: [
17359                             combobox
17360                         ]
17361                     }
17362                 ];
17363             }
17364         } else {
17365             cfg.cn = combobox;    
17366         }
17367         
17368         
17369         var settings = this;
17370         
17371         ['xs','sm','md','lg'].map(function(size){
17372             if (settings[size]) {
17373                 cfg.cls += ' col-' + size + '-' + settings[size];
17374             }
17375         });
17376         
17377         return cfg;
17378     },
17379     
17380     initTouchView : function()
17381     {
17382         this.renderTouchView();
17383         
17384         this.touchViewEl.on('scroll', function(){
17385             this.el.dom.scrollTop = 0;
17386         }, this);
17387         
17388         this.originalValue = this.getValue();
17389         
17390         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17391         
17392         this.inputEl().on("click", this.showTouchView, this);
17393         if (this.triggerEl) {
17394             this.triggerEl.on("click", this.showTouchView, this);
17395         }
17396         
17397         
17398         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17399         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17400         
17401         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17402         
17403         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17404         this.store.on('load', this.onTouchViewLoad, this);
17405         this.store.on('loadexception', this.onTouchViewLoadException, this);
17406         
17407         if(this.hiddenName){
17408             
17409             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17410             
17411             this.hiddenField.dom.value =
17412                 this.hiddenValue !== undefined ? this.hiddenValue :
17413                 this.value !== undefined ? this.value : '';
17414         
17415             this.el.dom.removeAttribute('name');
17416             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17417         }
17418         
17419         if(this.multiple){
17420             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17421             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17422         }
17423         
17424         if(this.removable && !this.multiple){
17425             var close = this.closeTriggerEl();
17426             if(close){
17427                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17428                 close.on('click', this.removeBtnClick, this, close);
17429             }
17430         }
17431         /*
17432          * fix the bug in Safari iOS8
17433          */
17434         this.inputEl().on("focus", function(e){
17435             document.activeElement.blur();
17436         }, this);
17437         
17438         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17439         
17440         return;
17441         
17442         
17443     },
17444     
17445     renderTouchView : function()
17446     {
17447         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17448         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17451         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17452         
17453         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17454         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17455         this.touchViewBodyEl.setStyle('overflow', 'auto');
17456         
17457         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17458         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17459         
17460         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17461         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17462         
17463     },
17464     
17465     showTouchView : function()
17466     {
17467         if(this.disabled){
17468             return;
17469         }
17470         
17471         this.touchViewHeaderEl.hide();
17472
17473         if(this.modalTitle.length){
17474             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17475             this.touchViewHeaderEl.show();
17476         }
17477
17478         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17479         this.touchViewEl.show();
17480
17481         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17482         
17483         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17484         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17485
17486         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17487
17488         if(this.modalTitle.length){
17489             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17490         }
17491         
17492         this.touchViewBodyEl.setHeight(bodyHeight);
17493
17494         if(this.animate){
17495             var _this = this;
17496             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17497         }else{
17498             this.touchViewEl.addClass(['in','show']);
17499         }
17500         
17501         if(this._touchViewMask){
17502             Roo.get(document.body).addClass("x-body-masked");
17503             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17504             this._touchViewMask.setStyle('z-index', 10000);
17505             this._touchViewMask.addClass('show');
17506         }
17507         
17508         this.doTouchViewQuery();
17509         
17510     },
17511     
17512     hideTouchView : function()
17513     {
17514         this.touchViewEl.removeClass(['in','show']);
17515
17516         if(this.animate){
17517             var _this = this;
17518             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17519         }else{
17520             this.touchViewEl.setStyle('display', 'none');
17521         }
17522         
17523         if(this._touchViewMask){
17524             this._touchViewMask.removeClass('show');
17525             Roo.get(document.body).removeClass("x-body-masked");
17526         }
17527     },
17528     
17529     setTouchViewValue : function()
17530     {
17531         if(this.multiple){
17532             this.clearItem();
17533         
17534             var _this = this;
17535
17536             Roo.each(this.tickItems, function(o){
17537                 this.addItem(o);
17538             }, this);
17539         }
17540         
17541         this.hideTouchView();
17542     },
17543     
17544     doTouchViewQuery : function()
17545     {
17546         var qe = {
17547             query: '',
17548             forceAll: true,
17549             combo: this,
17550             cancel:false
17551         };
17552         
17553         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17554             return false;
17555         }
17556         
17557         if(!this.alwaysQuery || this.mode == 'local'){
17558             this.onTouchViewLoad();
17559             return;
17560         }
17561         
17562         this.store.load();
17563     },
17564     
17565     onTouchViewBeforeLoad : function(combo,opts)
17566     {
17567         return;
17568     },
17569
17570     // private
17571     onTouchViewLoad : function()
17572     {
17573         if(this.store.getCount() < 1){
17574             this.onTouchViewEmptyResults();
17575             return;
17576         }
17577         
17578         this.clearTouchView();
17579         
17580         var rawValue = this.getRawValue();
17581         
17582         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17583         
17584         this.tickItems = [];
17585         
17586         this.store.data.each(function(d, rowIndex){
17587             var row = this.touchViewListGroup.createChild(template);
17588             
17589             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17590                 row.addClass(d.data.cls);
17591             }
17592             
17593             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17594                 var cfg = {
17595                     data : d.data,
17596                     html : d.data[this.displayField]
17597                 };
17598                 
17599                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17600                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17601                 }
17602             }
17603             row.removeClass('selected');
17604             if(!this.multiple && this.valueField &&
17605                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17606             {
17607                 // radio buttons..
17608                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17609                 row.addClass('selected');
17610             }
17611             
17612             if(this.multiple && this.valueField &&
17613                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17614             {
17615                 
17616                 // checkboxes...
17617                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17618                 this.tickItems.push(d.data);
17619             }
17620             
17621             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17622             
17623         }, this);
17624         
17625         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17626         
17627         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17628
17629         if(this.modalTitle.length){
17630             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17631         }
17632
17633         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17634         
17635         if(this.mobile_restrict_height && listHeight < bodyHeight){
17636             this.touchViewBodyEl.setHeight(listHeight);
17637         }
17638         
17639         var _this = this;
17640         
17641         if(firstChecked && listHeight > bodyHeight){
17642             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17643         }
17644         
17645     },
17646     
17647     onTouchViewLoadException : function()
17648     {
17649         this.hideTouchView();
17650     },
17651     
17652     onTouchViewEmptyResults : function()
17653     {
17654         this.clearTouchView();
17655         
17656         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17657         
17658         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17659         
17660     },
17661     
17662     clearTouchView : function()
17663     {
17664         this.touchViewListGroup.dom.innerHTML = '';
17665     },
17666     
17667     onTouchViewClick : function(e, el, o)
17668     {
17669         e.preventDefault();
17670         
17671         var row = o.row;
17672         var rowIndex = o.rowIndex;
17673         
17674         var r = this.store.getAt(rowIndex);
17675         
17676         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17677             
17678             if(!this.multiple){
17679                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17680                     c.dom.removeAttribute('checked');
17681                 }, this);
17682
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17684
17685                 this.setFromData(r.data);
17686
17687                 var close = this.closeTriggerEl();
17688
17689                 if(close){
17690                     close.show();
17691                 }
17692
17693                 this.hideTouchView();
17694
17695                 this.fireEvent('select', this, r, rowIndex);
17696
17697                 return;
17698             }
17699
17700             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17701                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17702                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17703                 return;
17704             }
17705
17706             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17707             this.addItem(r.data);
17708             this.tickItems.push(r.data);
17709         }
17710     },
17711     
17712     getAutoCreateNativeIOS : function()
17713     {
17714         var cfg = {
17715             cls: 'form-group' //input-group,
17716         };
17717         
17718         var combobox =  {
17719             tag: 'select',
17720             cls : 'roo-ios-select'
17721         };
17722         
17723         if (this.name) {
17724             combobox.name = this.name;
17725         }
17726         
17727         if (this.disabled) {
17728             combobox.disabled = true;
17729         }
17730         
17731         var settings = this;
17732         
17733         ['xs','sm','md','lg'].map(function(size){
17734             if (settings[size]) {
17735                 cfg.cls += ' col-' + size + '-' + settings[size];
17736             }
17737         });
17738         
17739         cfg.cn = combobox;
17740         
17741         return cfg;
17742         
17743     },
17744     
17745     initIOSView : function()
17746     {
17747         this.store.on('load', this.onIOSViewLoad, this);
17748         
17749         return;
17750     },
17751     
17752     onIOSViewLoad : function()
17753     {
17754         if(this.store.getCount() < 1){
17755             return;
17756         }
17757         
17758         this.clearIOSView();
17759         
17760         if(this.allowBlank) {
17761             
17762             var default_text = '-- SELECT --';
17763             
17764             if(this.placeholder.length){
17765                 default_text = this.placeholder;
17766             }
17767             
17768             if(this.emptyTitle.length){
17769                 default_text += ' - ' + this.emptyTitle + ' -';
17770             }
17771             
17772             var opt = this.inputEl().createChild({
17773                 tag: 'option',
17774                 value : 0,
17775                 html : default_text
17776             });
17777             
17778             var o = {};
17779             o[this.valueField] = 0;
17780             o[this.displayField] = default_text;
17781             
17782             this.ios_options.push({
17783                 data : o,
17784                 el : opt
17785             });
17786             
17787         }
17788         
17789         this.store.data.each(function(d, rowIndex){
17790             
17791             var html = '';
17792             
17793             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17794                 html = d.data[this.displayField];
17795             }
17796             
17797             var value = '';
17798             
17799             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17800                 value = d.data[this.valueField];
17801             }
17802             
17803             var option = {
17804                 tag: 'option',
17805                 value : value,
17806                 html : html
17807             };
17808             
17809             if(this.value == d.data[this.valueField]){
17810                 option['selected'] = true;
17811             }
17812             
17813             var opt = this.inputEl().createChild(option);
17814             
17815             this.ios_options.push({
17816                 data : d.data,
17817                 el : opt
17818             });
17819             
17820         }, this);
17821         
17822         this.inputEl().on('change', function(){
17823            this.fireEvent('select', this);
17824         }, this);
17825         
17826     },
17827     
17828     clearIOSView: function()
17829     {
17830         this.inputEl().dom.innerHTML = '';
17831         
17832         this.ios_options = [];
17833     },
17834     
17835     setIOSValue: function(v)
17836     {
17837         this.value = v;
17838         
17839         if(!this.ios_options){
17840             return;
17841         }
17842         
17843         Roo.each(this.ios_options, function(opts){
17844            
17845            opts.el.dom.removeAttribute('selected');
17846            
17847            if(opts.data[this.valueField] != v){
17848                return;
17849            }
17850            
17851            opts.el.dom.setAttribute('selected', true);
17852            
17853         }, this);
17854     }
17855
17856     /** 
17857     * @cfg {Boolean} grow 
17858     * @hide 
17859     */
17860     /** 
17861     * @cfg {Number} growMin 
17862     * @hide 
17863     */
17864     /** 
17865     * @cfg {Number} growMax 
17866     * @hide 
17867     */
17868     /**
17869      * @hide
17870      * @method autoSize
17871      */
17872 });
17873
17874 Roo.apply(Roo.bootstrap.ComboBox,  {
17875     
17876     header : {
17877         tag: 'div',
17878         cls: 'modal-header',
17879         cn: [
17880             {
17881                 tag: 'h4',
17882                 cls: 'modal-title'
17883             }
17884         ]
17885     },
17886     
17887     body : {
17888         tag: 'div',
17889         cls: 'modal-body',
17890         cn: [
17891             {
17892                 tag: 'ul',
17893                 cls: 'list-group'
17894             }
17895         ]
17896     },
17897     
17898     listItemRadio : {
17899         tag: 'li',
17900         cls: 'list-group-item',
17901         cn: [
17902             {
17903                 tag: 'span',
17904                 cls: 'roo-combobox-list-group-item-value'
17905             },
17906             {
17907                 tag: 'div',
17908                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17909                 cn: [
17910                     {
17911                         tag: 'input',
17912                         type: 'radio'
17913                     },
17914                     {
17915                         tag: 'label'
17916                     }
17917                 ]
17918             }
17919         ]
17920     },
17921     
17922     listItemCheckbox : {
17923         tag: 'li',
17924         cls: 'list-group-item',
17925         cn: [
17926             {
17927                 tag: 'span',
17928                 cls: 'roo-combobox-list-group-item-value'
17929             },
17930             {
17931                 tag: 'div',
17932                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17933                 cn: [
17934                     {
17935                         tag: 'input',
17936                         type: 'checkbox'
17937                     },
17938                     {
17939                         tag: 'label'
17940                     }
17941                 ]
17942             }
17943         ]
17944     },
17945     
17946     emptyResult : {
17947         tag: 'div',
17948         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17949     },
17950     
17951     footer : {
17952         tag: 'div',
17953         cls: 'modal-footer',
17954         cn: [
17955             {
17956                 tag: 'div',
17957                 cls: 'row',
17958                 cn: [
17959                     {
17960                         tag: 'div',
17961                         cls: 'col-xs-6 text-left',
17962                         cn: {
17963                             tag: 'button',
17964                             cls: 'btn btn-danger roo-touch-view-cancel',
17965                             html: 'Cancel'
17966                         }
17967                     },
17968                     {
17969                         tag: 'div',
17970                         cls: 'col-xs-6 text-right',
17971                         cn: {
17972                             tag: 'button',
17973                             cls: 'btn btn-success roo-touch-view-ok',
17974                             html: 'OK'
17975                         }
17976                     }
17977                 ]
17978             }
17979         ]
17980         
17981     }
17982 });
17983
17984 Roo.apply(Roo.bootstrap.ComboBox,  {
17985     
17986     touchViewTemplate : {
17987         tag: 'div',
17988         cls: 'modal fade roo-combobox-touch-view',
17989         cn: [
17990             {
17991                 tag: 'div',
17992                 cls: 'modal-dialog',
17993                 style : 'position:fixed', // we have to fix position....
17994                 cn: [
17995                     {
17996                         tag: 'div',
17997                         cls: 'modal-content',
17998                         cn: [
17999                             Roo.bootstrap.ComboBox.header,
18000                             Roo.bootstrap.ComboBox.body,
18001                             Roo.bootstrap.ComboBox.footer
18002                         ]
18003                     }
18004                 ]
18005             }
18006         ]
18007     }
18008 });/*
18009  * Based on:
18010  * Ext JS Library 1.1.1
18011  * Copyright(c) 2006-2007, Ext JS, LLC.
18012  *
18013  * Originally Released Under LGPL - original licence link has changed is not relivant.
18014  *
18015  * Fork - LGPL
18016  * <script type="text/javascript">
18017  */
18018
18019 /**
18020  * @class Roo.View
18021  * @extends Roo.util.Observable
18022  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18023  * This class also supports single and multi selection modes. <br>
18024  * Create a data model bound view:
18025  <pre><code>
18026  var store = new Roo.data.Store(...);
18027
18028  var view = new Roo.View({
18029     el : "my-element",
18030     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18031  
18032     singleSelect: true,
18033     selectedClass: "ydataview-selected",
18034     store: store
18035  });
18036
18037  // listen for node click?
18038  view.on("click", function(vw, index, node, e){
18039  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18040  });
18041
18042  // load XML data
18043  dataModel.load("foobar.xml");
18044  </code></pre>
18045  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18046  * <br><br>
18047  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18048  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18049  * 
18050  * Note: old style constructor is still suported (container, template, config)
18051  * 
18052  * @constructor
18053  * Create a new View
18054  * @param {Object} config The config object
18055  * 
18056  */
18057 Roo.View = function(config, depreciated_tpl, depreciated_config){
18058     
18059     this.parent = false;
18060     
18061     if (typeof(depreciated_tpl) == 'undefined') {
18062         // new way.. - universal constructor.
18063         Roo.apply(this, config);
18064         this.el  = Roo.get(this.el);
18065     } else {
18066         // old format..
18067         this.el  = Roo.get(config);
18068         this.tpl = depreciated_tpl;
18069         Roo.apply(this, depreciated_config);
18070     }
18071     this.wrapEl  = this.el.wrap().wrap();
18072     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18073     
18074     
18075     if(typeof(this.tpl) == "string"){
18076         this.tpl = new Roo.Template(this.tpl);
18077     } else {
18078         // support xtype ctors..
18079         this.tpl = new Roo.factory(this.tpl, Roo);
18080     }
18081     
18082     
18083     this.tpl.compile();
18084     
18085     /** @private */
18086     this.addEvents({
18087         /**
18088          * @event beforeclick
18089          * Fires before a click is processed. Returns false to cancel the default action.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "beforeclick" : true,
18096         /**
18097          * @event click
18098          * Fires when a template node is clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "click" : true,
18105         /**
18106          * @event dblclick
18107          * Fires when a template node is double clicked.
18108          * @param {Roo.View} this
18109          * @param {Number} index The index of the target node
18110          * @param {HTMLElement} node The target node
18111          * @param {Roo.EventObject} e The raw event object
18112          */
18113             "dblclick" : true,
18114         /**
18115          * @event contextmenu
18116          * Fires when a template node is right clicked.
18117          * @param {Roo.View} this
18118          * @param {Number} index The index of the target node
18119          * @param {HTMLElement} node The target node
18120          * @param {Roo.EventObject} e The raw event object
18121          */
18122             "contextmenu" : true,
18123         /**
18124          * @event selectionchange
18125          * Fires when the selected nodes change.
18126          * @param {Roo.View} this
18127          * @param {Array} selections Array of the selected nodes
18128          */
18129             "selectionchange" : true,
18130     
18131         /**
18132          * @event beforeselect
18133          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18134          * @param {Roo.View} this
18135          * @param {HTMLElement} node The node to be selected
18136          * @param {Array} selections Array of currently selected nodes
18137          */
18138             "beforeselect" : true,
18139         /**
18140          * @event preparedata
18141          * Fires on every row to render, to allow you to change the data.
18142          * @param {Roo.View} this
18143          * @param {Object} data to be rendered (change this)
18144          */
18145           "preparedata" : true
18146           
18147           
18148         });
18149
18150
18151
18152     this.el.on({
18153         "click": this.onClick,
18154         "dblclick": this.onDblClick,
18155         "contextmenu": this.onContextMenu,
18156         scope:this
18157     });
18158
18159     this.selections = [];
18160     this.nodes = [];
18161     this.cmp = new Roo.CompositeElementLite([]);
18162     if(this.store){
18163         this.store = Roo.factory(this.store, Roo.data);
18164         this.setStore(this.store, true);
18165     }
18166     
18167     if ( this.footer && this.footer.xtype) {
18168            
18169          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18170         
18171         this.footer.dataSource = this.store;
18172         this.footer.container = fctr;
18173         this.footer = Roo.factory(this.footer, Roo);
18174         fctr.insertFirst(this.el);
18175         
18176         // this is a bit insane - as the paging toolbar seems to detach the el..
18177 //        dom.parentNode.parentNode.parentNode
18178          // they get detached?
18179     }
18180     
18181     
18182     Roo.View.superclass.constructor.call(this);
18183     
18184     
18185 };
18186
18187 Roo.extend(Roo.View, Roo.util.Observable, {
18188     
18189      /**
18190      * @cfg {Roo.data.Store} store Data store to load data from.
18191      */
18192     store : false,
18193     
18194     /**
18195      * @cfg {String|Roo.Element} el The container element.
18196      */
18197     el : '',
18198     
18199     /**
18200      * @cfg {String|Roo.Template} tpl The template used by this View 
18201      */
18202     tpl : false,
18203     /**
18204      * @cfg {String} dataName the named area of the template to use as the data area
18205      *                          Works with domtemplates roo-name="name"
18206      */
18207     dataName: false,
18208     /**
18209      * @cfg {String} selectedClass The css class to add to selected nodes
18210      */
18211     selectedClass : "x-view-selected",
18212      /**
18213      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18214      */
18215     emptyText : "",
18216     
18217     /**
18218      * @cfg {String} text to display on mask (default Loading)
18219      */
18220     mask : false,
18221     /**
18222      * @cfg {Boolean} multiSelect Allow multiple selection
18223      */
18224     multiSelect : false,
18225     /**
18226      * @cfg {Boolean} singleSelect Allow single selection
18227      */
18228     singleSelect:  false,
18229     
18230     /**
18231      * @cfg {Boolean} toggleSelect - selecting 
18232      */
18233     toggleSelect : false,
18234     
18235     /**
18236      * @cfg {Boolean} tickable - selecting 
18237      */
18238     tickable : false,
18239     
18240     /**
18241      * Returns the element this view is bound to.
18242      * @return {Roo.Element}
18243      */
18244     getEl : function(){
18245         return this.wrapEl;
18246     },
18247     
18248     
18249
18250     /**
18251      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18252      */
18253     refresh : function(){
18254         //Roo.log('refresh');
18255         var t = this.tpl;
18256         
18257         // if we are using something like 'domtemplate', then
18258         // the what gets used is:
18259         // t.applySubtemplate(NAME, data, wrapping data..)
18260         // the outer template then get' applied with
18261         //     the store 'extra data'
18262         // and the body get's added to the
18263         //      roo-name="data" node?
18264         //      <span class='roo-tpl-{name}'></span> ?????
18265         
18266         
18267         
18268         this.clearSelections();
18269         this.el.update("");
18270         var html = [];
18271         var records = this.store.getRange();
18272         if(records.length < 1) {
18273             
18274             // is this valid??  = should it render a template??
18275             
18276             this.el.update(this.emptyText);
18277             return;
18278         }
18279         var el = this.el;
18280         if (this.dataName) {
18281             this.el.update(t.apply(this.store.meta)); //????
18282             el = this.el.child('.roo-tpl-' + this.dataName);
18283         }
18284         
18285         for(var i = 0, len = records.length; i < len; i++){
18286             var data = this.prepareData(records[i].data, i, records[i]);
18287             this.fireEvent("preparedata", this, data, i, records[i]);
18288             
18289             var d = Roo.apply({}, data);
18290             
18291             if(this.tickable){
18292                 Roo.apply(d, {'roo-id' : Roo.id()});
18293                 
18294                 var _this = this;
18295             
18296                 Roo.each(this.parent.item, function(item){
18297                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18298                         return;
18299                     }
18300                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18301                 });
18302             }
18303             
18304             html[html.length] = Roo.util.Format.trim(
18305                 this.dataName ?
18306                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18307                     t.apply(d)
18308             );
18309         }
18310         
18311         
18312         
18313         el.update(html.join(""));
18314         this.nodes = el.dom.childNodes;
18315         this.updateIndexes(0);
18316     },
18317     
18318
18319     /**
18320      * Function to override to reformat the data that is sent to
18321      * the template for each node.
18322      * DEPRICATED - use the preparedata event handler.
18323      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18324      * a JSON object for an UpdateManager bound view).
18325      */
18326     prepareData : function(data, index, record)
18327     {
18328         this.fireEvent("preparedata", this, data, index, record);
18329         return data;
18330     },
18331
18332     onUpdate : function(ds, record){
18333         // Roo.log('on update');   
18334         this.clearSelections();
18335         var index = this.store.indexOf(record);
18336         var n = this.nodes[index];
18337         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18338         n.parentNode.removeChild(n);
18339         this.updateIndexes(index, index);
18340     },
18341
18342     
18343     
18344 // --------- FIXME     
18345     onAdd : function(ds, records, index)
18346     {
18347         //Roo.log(['on Add', ds, records, index] );        
18348         this.clearSelections();
18349         if(this.nodes.length == 0){
18350             this.refresh();
18351             return;
18352         }
18353         var n = this.nodes[index];
18354         for(var i = 0, len = records.length; i < len; i++){
18355             var d = this.prepareData(records[i].data, i, records[i]);
18356             if(n){
18357                 this.tpl.insertBefore(n, d);
18358             }else{
18359                 
18360                 this.tpl.append(this.el, d);
18361             }
18362         }
18363         this.updateIndexes(index);
18364     },
18365
18366     onRemove : function(ds, record, index){
18367        // Roo.log('onRemove');
18368         this.clearSelections();
18369         var el = this.dataName  ?
18370             this.el.child('.roo-tpl-' + this.dataName) :
18371             this.el; 
18372         
18373         el.dom.removeChild(this.nodes[index]);
18374         this.updateIndexes(index);
18375     },
18376
18377     /**
18378      * Refresh an individual node.
18379      * @param {Number} index
18380      */
18381     refreshNode : function(index){
18382         this.onUpdate(this.store, this.store.getAt(index));
18383     },
18384
18385     updateIndexes : function(startIndex, endIndex){
18386         var ns = this.nodes;
18387         startIndex = startIndex || 0;
18388         endIndex = endIndex || ns.length - 1;
18389         for(var i = startIndex; i <= endIndex; i++){
18390             ns[i].nodeIndex = i;
18391         }
18392     },
18393
18394     /**
18395      * Changes the data store this view uses and refresh the view.
18396      * @param {Store} store
18397      */
18398     setStore : function(store, initial){
18399         if(!initial && this.store){
18400             this.store.un("datachanged", this.refresh);
18401             this.store.un("add", this.onAdd);
18402             this.store.un("remove", this.onRemove);
18403             this.store.un("update", this.onUpdate);
18404             this.store.un("clear", this.refresh);
18405             this.store.un("beforeload", this.onBeforeLoad);
18406             this.store.un("load", this.onLoad);
18407             this.store.un("loadexception", this.onLoad);
18408         }
18409         if(store){
18410           
18411             store.on("datachanged", this.refresh, this);
18412             store.on("add", this.onAdd, this);
18413             store.on("remove", this.onRemove, this);
18414             store.on("update", this.onUpdate, this);
18415             store.on("clear", this.refresh, this);
18416             store.on("beforeload", this.onBeforeLoad, this);
18417             store.on("load", this.onLoad, this);
18418             store.on("loadexception", this.onLoad, this);
18419         }
18420         
18421         if(store){
18422             this.refresh();
18423         }
18424     },
18425     /**
18426      * onbeforeLoad - masks the loading area.
18427      *
18428      */
18429     onBeforeLoad : function(store,opts)
18430     {
18431          //Roo.log('onBeforeLoad');   
18432         if (!opts.add) {
18433             this.el.update("");
18434         }
18435         this.el.mask(this.mask ? this.mask : "Loading" ); 
18436     },
18437     onLoad : function ()
18438     {
18439         this.el.unmask();
18440     },
18441     
18442
18443     /**
18444      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18445      * @param {HTMLElement} node
18446      * @return {HTMLElement} The template node
18447      */
18448     findItemFromChild : function(node){
18449         var el = this.dataName  ?
18450             this.el.child('.roo-tpl-' + this.dataName,true) :
18451             this.el.dom; 
18452         
18453         if(!node || node.parentNode == el){
18454                     return node;
18455             }
18456             var p = node.parentNode;
18457             while(p && p != el){
18458             if(p.parentNode == el){
18459                 return p;
18460             }
18461             p = p.parentNode;
18462         }
18463             return null;
18464     },
18465
18466     /** @ignore */
18467     onClick : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             var index = this.indexOf(item);
18471             if(this.onItemClick(item, index, e) !== false){
18472                 this.fireEvent("click", this, index, item, e);
18473             }
18474         }else{
18475             this.clearSelections();
18476         }
18477     },
18478
18479     /** @ignore */
18480     onContextMenu : function(e){
18481         var item = this.findItemFromChild(e.getTarget());
18482         if(item){
18483             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18484         }
18485     },
18486
18487     /** @ignore */
18488     onDblClick : function(e){
18489         var item = this.findItemFromChild(e.getTarget());
18490         if(item){
18491             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18492         }
18493     },
18494
18495     onItemClick : function(item, index, e)
18496     {
18497         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18498             return false;
18499         }
18500         if (this.toggleSelect) {
18501             var m = this.isSelected(item) ? 'unselect' : 'select';
18502             //Roo.log(m);
18503             var _t = this;
18504             _t[m](item, true, false);
18505             return true;
18506         }
18507         if(this.multiSelect || this.singleSelect){
18508             if(this.multiSelect && e.shiftKey && this.lastSelection){
18509                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18510             }else{
18511                 this.select(item, this.multiSelect && e.ctrlKey);
18512                 this.lastSelection = item;
18513             }
18514             
18515             if(!this.tickable){
18516                 e.preventDefault();
18517             }
18518             
18519         }
18520         return true;
18521     },
18522
18523     /**
18524      * Get the number of selected nodes.
18525      * @return {Number}
18526      */
18527     getSelectionCount : function(){
18528         return this.selections.length;
18529     },
18530
18531     /**
18532      * Get the currently selected nodes.
18533      * @return {Array} An array of HTMLElements
18534      */
18535     getSelectedNodes : function(){
18536         return this.selections;
18537     },
18538
18539     /**
18540      * Get the indexes of the selected nodes.
18541      * @return {Array}
18542      */
18543     getSelectedIndexes : function(){
18544         var indexes = [], s = this.selections;
18545         for(var i = 0, len = s.length; i < len; i++){
18546             indexes.push(s[i].nodeIndex);
18547         }
18548         return indexes;
18549     },
18550
18551     /**
18552      * Clear all selections
18553      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18554      */
18555     clearSelections : function(suppressEvent){
18556         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18557             this.cmp.elements = this.selections;
18558             this.cmp.removeClass(this.selectedClass);
18559             this.selections = [];
18560             if(!suppressEvent){
18561                 this.fireEvent("selectionchange", this, this.selections);
18562             }
18563         }
18564     },
18565
18566     /**
18567      * Returns true if the passed node is selected
18568      * @param {HTMLElement/Number} node The node or node index
18569      * @return {Boolean}
18570      */
18571     isSelected : function(node){
18572         var s = this.selections;
18573         if(s.length < 1){
18574             return false;
18575         }
18576         node = this.getNode(node);
18577         return s.indexOf(node) !== -1;
18578     },
18579
18580     /**
18581      * Selects nodes.
18582      * @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
18583      * @param {Boolean} keepExisting (optional) true to keep existing selections
18584      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18585      */
18586     select : function(nodeInfo, keepExisting, suppressEvent){
18587         if(nodeInfo instanceof Array){
18588             if(!keepExisting){
18589                 this.clearSelections(true);
18590             }
18591             for(var i = 0, len = nodeInfo.length; i < len; i++){
18592                 this.select(nodeInfo[i], true, true);
18593             }
18594             return;
18595         } 
18596         var node = this.getNode(nodeInfo);
18597         if(!node || this.isSelected(node)){
18598             return; // already selected.
18599         }
18600         if(!keepExisting){
18601             this.clearSelections(true);
18602         }
18603         
18604         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18605             Roo.fly(node).addClass(this.selectedClass);
18606             this.selections.push(node);
18607             if(!suppressEvent){
18608                 this.fireEvent("selectionchange", this, this.selections);
18609             }
18610         }
18611         
18612         
18613     },
18614       /**
18615      * Unselects nodes.
18616      * @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
18617      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18619      */
18620     unselect : function(nodeInfo, keepExisting, suppressEvent)
18621     {
18622         if(nodeInfo instanceof Array){
18623             Roo.each(this.selections, function(s) {
18624                 this.unselect(s, nodeInfo);
18625             }, this);
18626             return;
18627         }
18628         var node = this.getNode(nodeInfo);
18629         if(!node || !this.isSelected(node)){
18630             //Roo.log("not selected");
18631             return; // not selected.
18632         }
18633         // fireevent???
18634         var ns = [];
18635         Roo.each(this.selections, function(s) {
18636             if (s == node ) {
18637                 Roo.fly(node).removeClass(this.selectedClass);
18638
18639                 return;
18640             }
18641             ns.push(s);
18642         },this);
18643         
18644         this.selections= ns;
18645         this.fireEvent("selectionchange", this, this.selections);
18646     },
18647
18648     /**
18649      * Gets a template node.
18650      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18651      * @return {HTMLElement} The node or null if it wasn't found
18652      */
18653     getNode : function(nodeInfo){
18654         if(typeof nodeInfo == "string"){
18655             return document.getElementById(nodeInfo);
18656         }else if(typeof nodeInfo == "number"){
18657             return this.nodes[nodeInfo];
18658         }
18659         return nodeInfo;
18660     },
18661
18662     /**
18663      * Gets a range template nodes.
18664      * @param {Number} startIndex
18665      * @param {Number} endIndex
18666      * @return {Array} An array of nodes
18667      */
18668     getNodes : function(start, end){
18669         var ns = this.nodes;
18670         start = start || 0;
18671         end = typeof end == "undefined" ? ns.length - 1 : end;
18672         var nodes = [];
18673         if(start <= end){
18674             for(var i = start; i <= end; i++){
18675                 nodes.push(ns[i]);
18676             }
18677         } else{
18678             for(var i = start; i >= end; i--){
18679                 nodes.push(ns[i]);
18680             }
18681         }
18682         return nodes;
18683     },
18684
18685     /**
18686      * Finds the index of the passed node
18687      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18688      * @return {Number} The index of the node or -1
18689      */
18690     indexOf : function(node){
18691         node = this.getNode(node);
18692         if(typeof node.nodeIndex == "number"){
18693             return node.nodeIndex;
18694         }
18695         var ns = this.nodes;
18696         for(var i = 0, len = ns.length; i < len; i++){
18697             if(ns[i] == node){
18698                 return i;
18699             }
18700         }
18701         return -1;
18702     }
18703 });
18704 /*
18705  * - LGPL
18706  *
18707  * based on jquery fullcalendar
18708  * 
18709  */
18710
18711 Roo.bootstrap = Roo.bootstrap || {};
18712 /**
18713  * @class Roo.bootstrap.Calendar
18714  * @extends Roo.bootstrap.Component
18715  * Bootstrap Calendar class
18716  * @cfg {Boolean} loadMask (true|false) default false
18717  * @cfg {Object} header generate the user specific header of the calendar, default false
18718
18719  * @constructor
18720  * Create a new Container
18721  * @param {Object} config The config object
18722  */
18723
18724
18725
18726 Roo.bootstrap.Calendar = function(config){
18727     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18728      this.addEvents({
18729         /**
18730              * @event select
18731              * Fires when a date is selected
18732              * @param {DatePicker} this
18733              * @param {Date} date The selected date
18734              */
18735         'select': true,
18736         /**
18737              * @event monthchange
18738              * Fires when the displayed month changes 
18739              * @param {DatePicker} this
18740              * @param {Date} date The selected month
18741              */
18742         'monthchange': true,
18743         /**
18744              * @event evententer
18745              * Fires when mouse over an event
18746              * @param {Calendar} this
18747              * @param {event} Event
18748              */
18749         'evententer': true,
18750         /**
18751              * @event eventleave
18752              * Fires when the mouse leaves an
18753              * @param {Calendar} this
18754              * @param {event}
18755              */
18756         'eventleave': true,
18757         /**
18758              * @event eventclick
18759              * Fires when the mouse click an
18760              * @param {Calendar} this
18761              * @param {event}
18762              */
18763         'eventclick': true
18764         
18765     });
18766
18767 };
18768
18769 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18770     
18771      /**
18772      * @cfg {Number} startDay
18773      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18774      */
18775     startDay : 0,
18776     
18777     loadMask : false,
18778     
18779     header : false,
18780       
18781     getAutoCreate : function(){
18782         
18783         
18784         var fc_button = function(name, corner, style, content ) {
18785             return Roo.apply({},{
18786                 tag : 'span',
18787                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18788                          (corner.length ?
18789                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18790                             ''
18791                         ),
18792                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18793                 unselectable: 'on'
18794             });
18795         };
18796         
18797         var header = {};
18798         
18799         if(!this.header){
18800             header = {
18801                 tag : 'table',
18802                 cls : 'fc-header',
18803                 style : 'width:100%',
18804                 cn : [
18805                     {
18806                         tag: 'tr',
18807                         cn : [
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-left',
18811                                 cn : [
18812                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18813                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18814                                     { tag: 'span', cls: 'fc-header-space' },
18815                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18816
18817
18818                                 ]
18819                             },
18820
18821                             {
18822                                 tag : 'td',
18823                                 cls : 'fc-header-center',
18824                                 cn : [
18825                                     {
18826                                         tag: 'span',
18827                                         cls: 'fc-header-title',
18828                                         cn : {
18829                                             tag: 'H2',
18830                                             html : 'month / year'
18831                                         }
18832                                     }
18833
18834                                 ]
18835                             },
18836                             {
18837                                 tag : 'td',
18838                                 cls : 'fc-header-right',
18839                                 cn : [
18840                               /*      fc_button('month', 'left', '', 'month' ),
18841                                     fc_button('week', '', '', 'week' ),
18842                                     fc_button('day', 'right', '', 'day' )
18843                                 */    
18844
18845                                 ]
18846                             }
18847
18848                         ]
18849                     }
18850                 ]
18851             };
18852         }
18853         
18854         header = this.header;
18855         
18856        
18857         var cal_heads = function() {
18858             var ret = [];
18859             // fixme - handle this.
18860             
18861             for (var i =0; i < Date.dayNames.length; i++) {
18862                 var d = Date.dayNames[i];
18863                 ret.push({
18864                     tag: 'th',
18865                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18866                     html : d.substring(0,3)
18867                 });
18868                 
18869             }
18870             ret[0].cls += ' fc-first';
18871             ret[6].cls += ' fc-last';
18872             return ret;
18873         };
18874         var cal_cell = function(n) {
18875             return  {
18876                 tag: 'td',
18877                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18878                 cn : [
18879                     {
18880                         cn : [
18881                             {
18882                                 cls: 'fc-day-number',
18883                                 html: 'D'
18884                             },
18885                             {
18886                                 cls: 'fc-day-content',
18887                              
18888                                 cn : [
18889                                      {
18890                                         style: 'position: relative;' // height: 17px;
18891                                     }
18892                                 ]
18893                             }
18894                             
18895                             
18896                         ]
18897                     }
18898                 ]
18899                 
18900             }
18901         };
18902         var cal_rows = function() {
18903             
18904             var ret = [];
18905             for (var r = 0; r < 6; r++) {
18906                 var row= {
18907                     tag : 'tr',
18908                     cls : 'fc-week',
18909                     cn : []
18910                 };
18911                 
18912                 for (var i =0; i < Date.dayNames.length; i++) {
18913                     var d = Date.dayNames[i];
18914                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18915
18916                 }
18917                 row.cn[0].cls+=' fc-first';
18918                 row.cn[0].cn[0].style = 'min-height:90px';
18919                 row.cn[6].cls+=' fc-last';
18920                 ret.push(row);
18921                 
18922             }
18923             ret[0].cls += ' fc-first';
18924             ret[4].cls += ' fc-prev-last';
18925             ret[5].cls += ' fc-last';
18926             return ret;
18927             
18928         };
18929         
18930         var cal_table = {
18931             tag: 'table',
18932             cls: 'fc-border-separate',
18933             style : 'width:100%',
18934             cellspacing  : 0,
18935             cn : [
18936                 { 
18937                     tag: 'thead',
18938                     cn : [
18939                         { 
18940                             tag: 'tr',
18941                             cls : 'fc-first fc-last',
18942                             cn : cal_heads()
18943                         }
18944                     ]
18945                 },
18946                 { 
18947                     tag: 'tbody',
18948                     cn : cal_rows()
18949                 }
18950                   
18951             ]
18952         };
18953          
18954          var cfg = {
18955             cls : 'fc fc-ltr',
18956             cn : [
18957                 header,
18958                 {
18959                     cls : 'fc-content',
18960                     style : "position: relative;",
18961                     cn : [
18962                         {
18963                             cls : 'fc-view fc-view-month fc-grid',
18964                             style : 'position: relative',
18965                             unselectable : 'on',
18966                             cn : [
18967                                 {
18968                                     cls : 'fc-event-container',
18969                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18970                                 },
18971                                 cal_table
18972                             ]
18973                         }
18974                     ]
18975     
18976                 }
18977            ] 
18978             
18979         };
18980         
18981          
18982         
18983         return cfg;
18984     },
18985     
18986     
18987     initEvents : function()
18988     {
18989         if(!this.store){
18990             throw "can not find store for calendar";
18991         }
18992         
18993         var mark = {
18994             tag: "div",
18995             cls:"x-dlg-mask",
18996             style: "text-align:center",
18997             cn: [
18998                 {
18999                     tag: "div",
19000                     style: "background-color:white;width:50%;margin:250 auto",
19001                     cn: [
19002                         {
19003                             tag: "img",
19004                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19005                         },
19006                         {
19007                             tag: "span",
19008                             html: "Loading"
19009                         }
19010                         
19011                     ]
19012                 }
19013             ]
19014         };
19015         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19016         
19017         var size = this.el.select('.fc-content', true).first().getSize();
19018         this.maskEl.setSize(size.width, size.height);
19019         this.maskEl.enableDisplayMode("block");
19020         if(!this.loadMask){
19021             this.maskEl.hide();
19022         }
19023         
19024         this.store = Roo.factory(this.store, Roo.data);
19025         this.store.on('load', this.onLoad, this);
19026         this.store.on('beforeload', this.onBeforeLoad, this);
19027         
19028         this.resize();
19029         
19030         this.cells = this.el.select('.fc-day',true);
19031         //Roo.log(this.cells);
19032         this.textNodes = this.el.query('.fc-day-number');
19033         this.cells.addClassOnOver('fc-state-hover');
19034         
19035         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19036         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19037         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19038         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19039         
19040         this.on('monthchange', this.onMonthChange, this);
19041         
19042         this.update(new Date().clearTime());
19043     },
19044     
19045     resize : function() {
19046         var sz  = this.el.getSize();
19047         
19048         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19049         this.el.select('.fc-day-content div',true).setHeight(34);
19050     },
19051     
19052     
19053     // private
19054     showPrevMonth : function(e){
19055         this.update(this.activeDate.add("mo", -1));
19056     },
19057     showToday : function(e){
19058         this.update(new Date().clearTime());
19059     },
19060     // private
19061     showNextMonth : function(e){
19062         this.update(this.activeDate.add("mo", 1));
19063     },
19064
19065     // private
19066     showPrevYear : function(){
19067         this.update(this.activeDate.add("y", -1));
19068     },
19069
19070     // private
19071     showNextYear : function(){
19072         this.update(this.activeDate.add("y", 1));
19073     },
19074
19075     
19076    // private
19077     update : function(date)
19078     {
19079         var vd = this.activeDate;
19080         this.activeDate = date;
19081 //        if(vd && this.el){
19082 //            var t = date.getTime();
19083 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19084 //                Roo.log('using add remove');
19085 //                
19086 //                this.fireEvent('monthchange', this, date);
19087 //                
19088 //                this.cells.removeClass("fc-state-highlight");
19089 //                this.cells.each(function(c){
19090 //                   if(c.dateValue == t){
19091 //                       c.addClass("fc-state-highlight");
19092 //                       setTimeout(function(){
19093 //                            try{c.dom.firstChild.focus();}catch(e){}
19094 //                       }, 50);
19095 //                       return false;
19096 //                   }
19097 //                   return true;
19098 //                });
19099 //                return;
19100 //            }
19101 //        }
19102         
19103         var days = date.getDaysInMonth();
19104         
19105         var firstOfMonth = date.getFirstDateOfMonth();
19106         var startingPos = firstOfMonth.getDay()-this.startDay;
19107         
19108         if(startingPos < this.startDay){
19109             startingPos += 7;
19110         }
19111         
19112         var pm = date.add(Date.MONTH, -1);
19113         var prevStart = pm.getDaysInMonth()-startingPos;
19114 //        
19115         this.cells = this.el.select('.fc-day',true);
19116         this.textNodes = this.el.query('.fc-day-number');
19117         this.cells.addClassOnOver('fc-state-hover');
19118         
19119         var cells = this.cells.elements;
19120         var textEls = this.textNodes;
19121         
19122         Roo.each(cells, function(cell){
19123             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19124         });
19125         
19126         days += startingPos;
19127
19128         // convert everything to numbers so it's fast
19129         var day = 86400000;
19130         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19131         //Roo.log(d);
19132         //Roo.log(pm);
19133         //Roo.log(prevStart);
19134         
19135         var today = new Date().clearTime().getTime();
19136         var sel = date.clearTime().getTime();
19137         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19138         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19139         var ddMatch = this.disabledDatesRE;
19140         var ddText = this.disabledDatesText;
19141         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19142         var ddaysText = this.disabledDaysText;
19143         var format = this.format;
19144         
19145         var setCellClass = function(cal, cell){
19146             cell.row = 0;
19147             cell.events = [];
19148             cell.more = [];
19149             //Roo.log('set Cell Class');
19150             cell.title = "";
19151             var t = d.getTime();
19152             
19153             //Roo.log(d);
19154             
19155             cell.dateValue = t;
19156             if(t == today){
19157                 cell.className += " fc-today";
19158                 cell.className += " fc-state-highlight";
19159                 cell.title = cal.todayText;
19160             }
19161             if(t == sel){
19162                 // disable highlight in other month..
19163                 //cell.className += " fc-state-highlight";
19164                 
19165             }
19166             // disabling
19167             if(t < min) {
19168                 cell.className = " fc-state-disabled";
19169                 cell.title = cal.minText;
19170                 return;
19171             }
19172             if(t > max) {
19173                 cell.className = " fc-state-disabled";
19174                 cell.title = cal.maxText;
19175                 return;
19176             }
19177             if(ddays){
19178                 if(ddays.indexOf(d.getDay()) != -1){
19179                     cell.title = ddaysText;
19180                     cell.className = " fc-state-disabled";
19181                 }
19182             }
19183             if(ddMatch && format){
19184                 var fvalue = d.dateFormat(format);
19185                 if(ddMatch.test(fvalue)){
19186                     cell.title = ddText.replace("%0", fvalue);
19187                     cell.className = " fc-state-disabled";
19188                 }
19189             }
19190             
19191             if (!cell.initialClassName) {
19192                 cell.initialClassName = cell.dom.className;
19193             }
19194             
19195             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19196         };
19197
19198         var i = 0;
19199         
19200         for(; i < startingPos; i++) {
19201             textEls[i].innerHTML = (++prevStart);
19202             d.setDate(d.getDate()+1);
19203             
19204             cells[i].className = "fc-past fc-other-month";
19205             setCellClass(this, cells[i]);
19206         }
19207         
19208         var intDay = 0;
19209         
19210         for(; i < days; i++){
19211             intDay = i - startingPos + 1;
19212             textEls[i].innerHTML = (intDay);
19213             d.setDate(d.getDate()+1);
19214             
19215             cells[i].className = ''; // "x-date-active";
19216             setCellClass(this, cells[i]);
19217         }
19218         var extraDays = 0;
19219         
19220         for(; i < 42; i++) {
19221             textEls[i].innerHTML = (++extraDays);
19222             d.setDate(d.getDate()+1);
19223             
19224             cells[i].className = "fc-future fc-other-month";
19225             setCellClass(this, cells[i]);
19226         }
19227         
19228         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19229         
19230         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19231         
19232         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19233         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19234         
19235         if(totalRows != 6){
19236             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19237             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19238         }
19239         
19240         this.fireEvent('monthchange', this, date);
19241         
19242         
19243         /*
19244         if(!this.internalRender){
19245             var main = this.el.dom.firstChild;
19246             var w = main.offsetWidth;
19247             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19248             Roo.fly(main).setWidth(w);
19249             this.internalRender = true;
19250             // opera does not respect the auto grow header center column
19251             // then, after it gets a width opera refuses to recalculate
19252             // without a second pass
19253             if(Roo.isOpera && !this.secondPass){
19254                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19255                 this.secondPass = true;
19256                 this.update.defer(10, this, [date]);
19257             }
19258         }
19259         */
19260         
19261     },
19262     
19263     findCell : function(dt) {
19264         dt = dt.clearTime().getTime();
19265         var ret = false;
19266         this.cells.each(function(c){
19267             //Roo.log("check " +c.dateValue + '?=' + dt);
19268             if(c.dateValue == dt){
19269                 ret = c;
19270                 return false;
19271             }
19272             return true;
19273         });
19274         
19275         return ret;
19276     },
19277     
19278     findCells : function(ev) {
19279         var s = ev.start.clone().clearTime().getTime();
19280        // Roo.log(s);
19281         var e= ev.end.clone().clearTime().getTime();
19282        // Roo.log(e);
19283         var ret = [];
19284         this.cells.each(function(c){
19285              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19286             
19287             if(c.dateValue > e){
19288                 return ;
19289             }
19290             if(c.dateValue < s){
19291                 return ;
19292             }
19293             ret.push(c);
19294         });
19295         
19296         return ret;    
19297     },
19298     
19299 //    findBestRow: function(cells)
19300 //    {
19301 //        var ret = 0;
19302 //        
19303 //        for (var i =0 ; i < cells.length;i++) {
19304 //            ret  = Math.max(cells[i].rows || 0,ret);
19305 //        }
19306 //        return ret;
19307 //        
19308 //    },
19309     
19310     
19311     addItem : function(ev)
19312     {
19313         // look for vertical location slot in
19314         var cells = this.findCells(ev);
19315         
19316 //        ev.row = this.findBestRow(cells);
19317         
19318         // work out the location.
19319         
19320         var crow = false;
19321         var rows = [];
19322         for(var i =0; i < cells.length; i++) {
19323             
19324             cells[i].row = cells[0].row;
19325             
19326             if(i == 0){
19327                 cells[i].row = cells[i].row + 1;
19328             }
19329             
19330             if (!crow) {
19331                 crow = {
19332                     start : cells[i],
19333                     end :  cells[i]
19334                 };
19335                 continue;
19336             }
19337             if (crow.start.getY() == cells[i].getY()) {
19338                 // on same row.
19339                 crow.end = cells[i];
19340                 continue;
19341             }
19342             // different row.
19343             rows.push(crow);
19344             crow = {
19345                 start: cells[i],
19346                 end : cells[i]
19347             };
19348             
19349         }
19350         
19351         rows.push(crow);
19352         ev.els = [];
19353         ev.rows = rows;
19354         ev.cells = cells;
19355         
19356         cells[0].events.push(ev);
19357         
19358         this.calevents.push(ev);
19359     },
19360     
19361     clearEvents: function() {
19362         
19363         if(!this.calevents){
19364             return;
19365         }
19366         
19367         Roo.each(this.cells.elements, function(c){
19368             c.row = 0;
19369             c.events = [];
19370             c.more = [];
19371         });
19372         
19373         Roo.each(this.calevents, function(e) {
19374             Roo.each(e.els, function(el) {
19375                 el.un('mouseenter' ,this.onEventEnter, this);
19376                 el.un('mouseleave' ,this.onEventLeave, this);
19377                 el.remove();
19378             },this);
19379         },this);
19380         
19381         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19382             e.remove();
19383         });
19384         
19385     },
19386     
19387     renderEvents: function()
19388     {   
19389         var _this = this;
19390         
19391         this.cells.each(function(c) {
19392             
19393             if(c.row < 5){
19394                 return;
19395             }
19396             
19397             var ev = c.events;
19398             
19399             var r = 4;
19400             if(c.row != c.events.length){
19401                 r = 4 - (4 - (c.row - c.events.length));
19402             }
19403             
19404             c.events = ev.slice(0, r);
19405             c.more = ev.slice(r);
19406             
19407             if(c.more.length && c.more.length == 1){
19408                 c.events.push(c.more.pop());
19409             }
19410             
19411             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19412             
19413         });
19414             
19415         this.cells.each(function(c) {
19416             
19417             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19418             
19419             
19420             for (var e = 0; e < c.events.length; e++){
19421                 var ev = c.events[e];
19422                 var rows = ev.rows;
19423                 
19424                 for(var i = 0; i < rows.length; i++) {
19425                 
19426                     // how many rows should it span..
19427
19428                     var  cfg = {
19429                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19430                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19431
19432                         unselectable : "on",
19433                         cn : [
19434                             {
19435                                 cls: 'fc-event-inner',
19436                                 cn : [
19437     //                                {
19438     //                                  tag:'span',
19439     //                                  cls: 'fc-event-time',
19440     //                                  html : cells.length > 1 ? '' : ev.time
19441     //                                },
19442                                     {
19443                                       tag:'span',
19444                                       cls: 'fc-event-title',
19445                                       html : String.format('{0}', ev.title)
19446                                     }
19447
19448
19449                                 ]
19450                             },
19451                             {
19452                                 cls: 'ui-resizable-handle ui-resizable-e',
19453                                 html : '&nbsp;&nbsp;&nbsp'
19454                             }
19455
19456                         ]
19457                     };
19458
19459                     if (i == 0) {
19460                         cfg.cls += ' fc-event-start';
19461                     }
19462                     if ((i+1) == rows.length) {
19463                         cfg.cls += ' fc-event-end';
19464                     }
19465
19466                     var ctr = _this.el.select('.fc-event-container',true).first();
19467                     var cg = ctr.createChild(cfg);
19468
19469                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19470                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19471
19472                     var r = (c.more.length) ? 1 : 0;
19473                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19474                     cg.setWidth(ebox.right - sbox.x -2);
19475
19476                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19477                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19478                     cg.on('click', _this.onEventClick, _this, ev);
19479
19480                     ev.els.push(cg);
19481                     
19482                 }
19483                 
19484             }
19485             
19486             
19487             if(c.more.length){
19488                 var  cfg = {
19489                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19490                     style : 'position: absolute',
19491                     unselectable : "on",
19492                     cn : [
19493                         {
19494                             cls: 'fc-event-inner',
19495                             cn : [
19496                                 {
19497                                   tag:'span',
19498                                   cls: 'fc-event-title',
19499                                   html : 'More'
19500                                 }
19501
19502
19503                             ]
19504                         },
19505                         {
19506                             cls: 'ui-resizable-handle ui-resizable-e',
19507                             html : '&nbsp;&nbsp;&nbsp'
19508                         }
19509
19510                     ]
19511                 };
19512
19513                 var ctr = _this.el.select('.fc-event-container',true).first();
19514                 var cg = ctr.createChild(cfg);
19515
19516                 var sbox = c.select('.fc-day-content',true).first().getBox();
19517                 var ebox = c.select('.fc-day-content',true).first().getBox();
19518                 //Roo.log(cg);
19519                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19520                 cg.setWidth(ebox.right - sbox.x -2);
19521
19522                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19523                 
19524             }
19525             
19526         });
19527         
19528         
19529         
19530     },
19531     
19532     onEventEnter: function (e, el,event,d) {
19533         this.fireEvent('evententer', this, el, event);
19534     },
19535     
19536     onEventLeave: function (e, el,event,d) {
19537         this.fireEvent('eventleave', this, el, event);
19538     },
19539     
19540     onEventClick: function (e, el,event,d) {
19541         this.fireEvent('eventclick', this, el, event);
19542     },
19543     
19544     onMonthChange: function () {
19545         this.store.load();
19546     },
19547     
19548     onMoreEventClick: function(e, el, more)
19549     {
19550         var _this = this;
19551         
19552         this.calpopover.placement = 'right';
19553         this.calpopover.setTitle('More');
19554         
19555         this.calpopover.setContent('');
19556         
19557         var ctr = this.calpopover.el.select('.popover-content', true).first();
19558         
19559         Roo.each(more, function(m){
19560             var cfg = {
19561                 cls : 'fc-event-hori fc-event-draggable',
19562                 html : m.title
19563             };
19564             var cg = ctr.createChild(cfg);
19565             
19566             cg.on('click', _this.onEventClick, _this, m);
19567         });
19568         
19569         this.calpopover.show(el);
19570         
19571         
19572     },
19573     
19574     onLoad: function () 
19575     {   
19576         this.calevents = [];
19577         var cal = this;
19578         
19579         if(this.store.getCount() > 0){
19580             this.store.data.each(function(d){
19581                cal.addItem({
19582                     id : d.data.id,
19583                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19584                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19585                     time : d.data.start_time,
19586                     title : d.data.title,
19587                     description : d.data.description,
19588                     venue : d.data.venue
19589                 });
19590             });
19591         }
19592         
19593         this.renderEvents();
19594         
19595         if(this.calevents.length && this.loadMask){
19596             this.maskEl.hide();
19597         }
19598     },
19599     
19600     onBeforeLoad: function()
19601     {
19602         this.clearEvents();
19603         if(this.loadMask){
19604             this.maskEl.show();
19605         }
19606     }
19607 });
19608
19609  
19610  /*
19611  * - LGPL
19612  *
19613  * element
19614  * 
19615  */
19616
19617 /**
19618  * @class Roo.bootstrap.Popover
19619  * @extends Roo.bootstrap.Component
19620  * Bootstrap Popover class
19621  * @cfg {String} html contents of the popover   (or false to use children..)
19622  * @cfg {String} title of popover (or false to hide)
19623  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19624  * @cfg {String} trigger click || hover (or false to trigger manually)
19625  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19626  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19627  *      - if false and it has a 'parent' then it will be automatically added to that element
19628  *      - if string - Roo.get  will be called 
19629  * @cfg {Number} delay - delay before showing
19630  
19631  * @constructor
19632  * Create a new Popover
19633  * @param {Object} config The config object
19634  */
19635
19636 Roo.bootstrap.Popover = function(config){
19637     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19638     
19639     this.addEvents({
19640         // raw events
19641          /**
19642          * @event show
19643          * After the popover show
19644          * 
19645          * @param {Roo.bootstrap.Popover} this
19646          */
19647         "show" : true,
19648         /**
19649          * @event hide
19650          * After the popover hide
19651          * 
19652          * @param {Roo.bootstrap.Popover} this
19653          */
19654         "hide" : true
19655     });
19656 };
19657
19658 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19659     
19660     title: false,
19661     html: false,
19662     
19663     placement : 'right',
19664     trigger : 'hover', // hover
19665     modal : false,
19666     delay : 0,
19667     
19668     over: false,
19669     
19670     can_build_overlaid : false,
19671     
19672     maskEl : false, // the mask element
19673     headerEl : false,
19674     contentEl : false,
19675     alignEl : false, // when show is called with an element - this get's stored.
19676     
19677     getChildContainer : function()
19678     {
19679         return this.contentEl;
19680         
19681     },
19682     getPopoverHeader : function()
19683     {
19684         this.title = true; // flag not to hide it..
19685         this.headerEl.addClass('p-0');
19686         return this.headerEl
19687     },
19688     
19689     
19690     getAutoCreate : function(){
19691          
19692         var cfg = {
19693            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19694            style: 'display:block',
19695            cn : [
19696                 {
19697                     cls : 'arrow'
19698                 },
19699                 {
19700                     cls : 'popover-inner ',
19701                     cn : [
19702                         {
19703                             tag: 'h3',
19704                             cls: 'popover-title popover-header',
19705                             html : this.title === false ? '' : this.title
19706                         },
19707                         {
19708                             cls : 'popover-content popover-body '  + (this.cls || ''),
19709                             html : this.html || ''
19710                         }
19711                     ]
19712                     
19713                 }
19714            ]
19715         };
19716         
19717         return cfg;
19718     },
19719     /**
19720      * @param {string} the title
19721      */
19722     setTitle: function(str)
19723     {
19724         this.title = str;
19725         if (this.el) {
19726             this.headerEl.dom.innerHTML = str;
19727         }
19728         
19729     },
19730     /**
19731      * @param {string} the body content
19732      */
19733     setContent: function(str)
19734     {
19735         this.html = str;
19736         if (this.contentEl) {
19737             this.contentEl.dom.innerHTML = str;
19738         }
19739         
19740     },
19741     // as it get's added to the bottom of the page.
19742     onRender : function(ct, position)
19743     {
19744         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19745         
19746         
19747         
19748         if(!this.el){
19749             var cfg = Roo.apply({},  this.getAutoCreate());
19750             cfg.id = Roo.id();
19751             
19752             if (this.cls) {
19753                 cfg.cls += ' ' + this.cls;
19754             }
19755             if (this.style) {
19756                 cfg.style = this.style;
19757             }
19758             //Roo.log("adding to ");
19759             this.el = Roo.get(document.body).createChild(cfg, position);
19760 //            Roo.log(this.el);
19761         }
19762         
19763         this.contentEl = this.el.select('.popover-content',true).first();
19764         this.headerEl =  this.el.select('.popover-title',true).first();
19765         
19766         var nitems = [];
19767         if(typeof(this.items) != 'undefined'){
19768             var items = this.items;
19769             delete this.items;
19770
19771             for(var i =0;i < items.length;i++) {
19772                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19773             }
19774         }
19775
19776         this.items = nitems;
19777         
19778         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19779         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19780         
19781         
19782         
19783         this.initEvents();
19784     },
19785     
19786     resizeMask : function()
19787     {
19788         this.maskEl.setSize(
19789             Roo.lib.Dom.getViewWidth(true),
19790             Roo.lib.Dom.getViewHeight(true)
19791         );
19792     },
19793     
19794     initEvents : function()
19795     {
19796         
19797         if (!this.modal) { 
19798             Roo.bootstrap.Popover.register(this);
19799         }
19800          
19801         this.arrowEl = this.el.select('.arrow',true).first();
19802         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19803         this.el.enableDisplayMode('block');
19804         this.el.hide();
19805  
19806         
19807         if (this.over === false && !this.parent()) {
19808             return; 
19809         }
19810         if (this.triggers === false) {
19811             return;
19812         }
19813          
19814         // support parent
19815         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19816         var triggers = this.trigger ? this.trigger.split(' ') : [];
19817         Roo.each(triggers, function(trigger) {
19818         
19819             if (trigger == 'click') {
19820                 on_el.on('click', this.toggle, this);
19821             } else if (trigger != 'manual') {
19822                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19823                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19824       
19825                 on_el.on(eventIn  ,this.enter, this);
19826                 on_el.on(eventOut, this.leave, this);
19827             }
19828         }, this);
19829     },
19830     
19831     
19832     // private
19833     timeout : null,
19834     hoverState : null,
19835     
19836     toggle : function () {
19837         this.hoverState == 'in' ? this.leave() : this.enter();
19838     },
19839     
19840     enter : function () {
19841         
19842         clearTimeout(this.timeout);
19843     
19844         this.hoverState = 'in';
19845     
19846         if (!this.delay || !this.delay.show) {
19847             this.show();
19848             return;
19849         }
19850         var _t = this;
19851         this.timeout = setTimeout(function () {
19852             if (_t.hoverState == 'in') {
19853                 _t.show();
19854             }
19855         }, this.delay.show)
19856     },
19857     
19858     leave : function() {
19859         clearTimeout(this.timeout);
19860     
19861         this.hoverState = 'out';
19862     
19863         if (!this.delay || !this.delay.hide) {
19864             this.hide();
19865             return;
19866         }
19867         var _t = this;
19868         this.timeout = setTimeout(function () {
19869             if (_t.hoverState == 'out') {
19870                 _t.hide();
19871             }
19872         }, this.delay.hide)
19873     },
19874     /**
19875      * Show the popover
19876      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19877      * @param {string} (left|right|top|bottom) position
19878      */
19879     show : function (on_el, placement)
19880     {
19881         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19882         on_el = on_el || false; // default to false
19883          
19884         if (!on_el) {
19885             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19886                 on_el = this.parent().el;
19887             } else if (this.over) {
19888                 Roo.get(this.over);
19889             }
19890             
19891         }
19892         
19893         this.alignEl = Roo.get( on_el );
19894
19895         if (!this.el) {
19896             this.render(document.body);
19897         }
19898         
19899         
19900          
19901         
19902         if (this.title === false) {
19903             this.headerEl.hide();
19904         }
19905         
19906        
19907         this.el.show();
19908         this.el.dom.style.display = 'block';
19909          
19910  
19911         if (this.alignEl) {
19912             this.updatePosition(this.placement, true);
19913              
19914         } else {
19915             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19916             var es = this.el.getSize();
19917             var x = Roo.lib.Dom.getViewWidth()/2;
19918             var y = Roo.lib.Dom.getViewHeight()/2;
19919             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19920             
19921         }
19922
19923         
19924         //var arrow = this.el.select('.arrow',true).first();
19925         //arrow.set(align[2], 
19926         
19927         this.el.addClass('in');
19928         
19929          
19930         
19931         this.hoverState = 'in';
19932         
19933         if (this.modal) {
19934             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19935             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19936             this.maskEl.dom.style.display = 'block';
19937             this.maskEl.addClass('show');
19938         }
19939         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19940  
19941         this.fireEvent('show', this);
19942         
19943     },
19944     /**
19945      * fire this manually after loading a grid in the table for example
19946      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19947      * @param {Boolean} try and move it if we cant get right position.
19948      */
19949     updatePosition : function(placement, try_move)
19950     {
19951         // allow for calling with no parameters
19952         placement = placement   ? placement :  this.placement;
19953         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19954         
19955         this.el.removeClass([
19956             'fade','top','bottom', 'left', 'right','in',
19957             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19958         ]);
19959         this.el.addClass(placement + ' bs-popover-' + placement);
19960         
19961         if (!this.alignEl ) {
19962             return false;
19963         }
19964         
19965         switch (placement) {
19966             case 'right':
19967                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19968                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19969                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19970                     //normal display... or moved up/down.
19971                     this.el.setXY(offset);
19972                     var xy = this.alignEl.getAnchorXY('tr', false);
19973                     xy[0]+=2;xy[1]+=5;
19974                     this.arrowEl.setXY(xy);
19975                     return true;
19976                 }
19977                 // continue through...
19978                 return this.updatePosition('left', false);
19979                 
19980             
19981             case 'left':
19982                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19983                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19984                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19985                     //normal display... or moved up/down.
19986                     this.el.setXY(offset);
19987                     var xy = this.alignEl.getAnchorXY('tl', false);
19988                     xy[0]-=10;xy[1]+=5; // << fix me
19989                     this.arrowEl.setXY(xy);
19990                     return true;
19991                 }
19992                 // call self...
19993                 return this.updatePosition('right', false);
19994             
19995             case 'top':
19996                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
19997                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
19998                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19999                     //normal display... or moved up/down.
20000                     this.el.setXY(offset);
20001                     var xy = this.alignEl.getAnchorXY('t', false);
20002                     xy[1]-=10; // << fix me
20003                     this.arrowEl.setXY(xy);
20004                     return true;
20005                 }
20006                 // fall through
20007                return this.updatePosition('bottom', false);
20008             
20009             case 'bottom':
20010                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20011                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20012                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20013                     //normal display... or moved up/down.
20014                     this.el.setXY(offset);
20015                     var xy = this.alignEl.getAnchorXY('b', false);
20016                      xy[1]+=2; // << fix me
20017                     this.arrowEl.setXY(xy);
20018                     return true;
20019                 }
20020                 // fall through
20021                 return this.updatePosition('top', false);
20022                 
20023             
20024         }
20025         
20026         
20027         return false;
20028     },
20029     
20030     hide : function()
20031     {
20032         this.el.setXY([0,0]);
20033         this.el.removeClass('in');
20034         this.el.hide();
20035         this.hoverState = null;
20036         this.maskEl.hide(); // always..
20037         this.fireEvent('hide', this);
20038     }
20039     
20040 });
20041
20042
20043 Roo.apply(Roo.bootstrap.Popover, {
20044
20045     alignment : {
20046         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20047         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20048         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20049         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20050     },
20051     
20052     zIndex : 20001,
20053
20054     clickHander : false,
20055     
20056
20057     onMouseDown : function(e)
20058     {
20059         if (!e.getTarget(".roo-popover")) {
20060             this.hideAll();
20061         }
20062          
20063     },
20064     
20065     popups : [],
20066     
20067     register : function(popup)
20068     {
20069         if (!Roo.bootstrap.Popover.clickHandler) {
20070             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20071         }
20072         // hide other popups.
20073         this.hideAll();
20074         this.popups.push(popup);
20075     },
20076     hideAll : function()
20077     {
20078         this.popups.forEach(function(p) {
20079             p.hide();
20080         });
20081     }
20082
20083 });/*
20084  * - LGPL
20085  *
20086  * Card header - holder for the card header elements.
20087  * 
20088  */
20089
20090 /**
20091  * @class Roo.bootstrap.PopoverNav
20092  * @extends Roo.bootstrap.NavGroup
20093  * Bootstrap Popover header navigation class
20094  * @constructor
20095  * Create a new Popover Header Navigation 
20096  * @param {Object} config The config object
20097  */
20098
20099 Roo.bootstrap.PopoverNav = function(config){
20100     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20101 };
20102
20103 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20104     
20105     
20106     container_method : 'getPopoverHeader' 
20107     
20108      
20109     
20110     
20111    
20112 });
20113
20114  
20115
20116  /*
20117  * - LGPL
20118  *
20119  * Progress
20120  * 
20121  */
20122
20123 /**
20124  * @class Roo.bootstrap.Progress
20125  * @extends Roo.bootstrap.Component
20126  * Bootstrap Progress class
20127  * @cfg {Boolean} striped striped of the progress bar
20128  * @cfg {Boolean} active animated of the progress bar
20129  * 
20130  * 
20131  * @constructor
20132  * Create a new Progress
20133  * @param {Object} config The config object
20134  */
20135
20136 Roo.bootstrap.Progress = function(config){
20137     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20138 };
20139
20140 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20141     
20142     striped : false,
20143     active: false,
20144     
20145     getAutoCreate : function(){
20146         var cfg = {
20147             tag: 'div',
20148             cls: 'progress'
20149         };
20150         
20151         
20152         if(this.striped){
20153             cfg.cls += ' progress-striped';
20154         }
20155       
20156         if(this.active){
20157             cfg.cls += ' active';
20158         }
20159         
20160         
20161         return cfg;
20162     }
20163    
20164 });
20165
20166  
20167
20168  /*
20169  * - LGPL
20170  *
20171  * ProgressBar
20172  * 
20173  */
20174
20175 /**
20176  * @class Roo.bootstrap.ProgressBar
20177  * @extends Roo.bootstrap.Component
20178  * Bootstrap ProgressBar class
20179  * @cfg {Number} aria_valuenow aria-value now
20180  * @cfg {Number} aria_valuemin aria-value min
20181  * @cfg {Number} aria_valuemax aria-value max
20182  * @cfg {String} label label for the progress bar
20183  * @cfg {String} panel (success | info | warning | danger )
20184  * @cfg {String} role role of the progress bar
20185  * @cfg {String} sr_only text
20186  * 
20187  * 
20188  * @constructor
20189  * Create a new ProgressBar
20190  * @param {Object} config The config object
20191  */
20192
20193 Roo.bootstrap.ProgressBar = function(config){
20194     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20195 };
20196
20197 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20198     
20199     aria_valuenow : 0,
20200     aria_valuemin : 0,
20201     aria_valuemax : 100,
20202     label : false,
20203     panel : false,
20204     role : false,
20205     sr_only: false,
20206     
20207     getAutoCreate : function()
20208     {
20209         
20210         var cfg = {
20211             tag: 'div',
20212             cls: 'progress-bar',
20213             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20214         };
20215         
20216         if(this.sr_only){
20217             cfg.cn = {
20218                 tag: 'span',
20219                 cls: 'sr-only',
20220                 html: this.sr_only
20221             }
20222         }
20223         
20224         if(this.role){
20225             cfg.role = this.role;
20226         }
20227         
20228         if(this.aria_valuenow){
20229             cfg['aria-valuenow'] = this.aria_valuenow;
20230         }
20231         
20232         if(this.aria_valuemin){
20233             cfg['aria-valuemin'] = this.aria_valuemin;
20234         }
20235         
20236         if(this.aria_valuemax){
20237             cfg['aria-valuemax'] = this.aria_valuemax;
20238         }
20239         
20240         if(this.label && !this.sr_only){
20241             cfg.html = this.label;
20242         }
20243         
20244         if(this.panel){
20245             cfg.cls += ' progress-bar-' + this.panel;
20246         }
20247         
20248         return cfg;
20249     },
20250     
20251     update : function(aria_valuenow)
20252     {
20253         this.aria_valuenow = aria_valuenow;
20254         
20255         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20256     }
20257    
20258 });
20259
20260  
20261
20262  /*
20263  * - LGPL
20264  *
20265  * column
20266  * 
20267  */
20268
20269 /**
20270  * @class Roo.bootstrap.TabGroup
20271  * @extends Roo.bootstrap.Column
20272  * Bootstrap Column class
20273  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20274  * @cfg {Boolean} carousel true to make the group behave like a carousel
20275  * @cfg {Boolean} bullets show bullets for the panels
20276  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20277  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20278  * @cfg {Boolean} showarrow (true|false) show arrow default true
20279  * 
20280  * @constructor
20281  * Create a new TabGroup
20282  * @param {Object} config The config object
20283  */
20284
20285 Roo.bootstrap.TabGroup = function(config){
20286     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20287     if (!this.navId) {
20288         this.navId = Roo.id();
20289     }
20290     this.tabs = [];
20291     Roo.bootstrap.TabGroup.register(this);
20292     
20293 };
20294
20295 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20296     
20297     carousel : false,
20298     transition : false,
20299     bullets : 0,
20300     timer : 0,
20301     autoslide : false,
20302     slideFn : false,
20303     slideOnTouch : false,
20304     showarrow : true,
20305     
20306     getAutoCreate : function()
20307     {
20308         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20309         
20310         cfg.cls += ' tab-content';
20311         
20312         if (this.carousel) {
20313             cfg.cls += ' carousel slide';
20314             
20315             cfg.cn = [{
20316                cls : 'carousel-inner',
20317                cn : []
20318             }];
20319         
20320             if(this.bullets  && !Roo.isTouch){
20321                 
20322                 var bullets = {
20323                     cls : 'carousel-bullets',
20324                     cn : []
20325                 };
20326                
20327                 if(this.bullets_cls){
20328                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20329                 }
20330                 
20331                 bullets.cn.push({
20332                     cls : 'clear'
20333                 });
20334                 
20335                 cfg.cn[0].cn.push(bullets);
20336             }
20337             
20338             if(this.showarrow){
20339                 cfg.cn[0].cn.push({
20340                     tag : 'div',
20341                     class : 'carousel-arrow',
20342                     cn : [
20343                         {
20344                             tag : 'div',
20345                             class : 'carousel-prev',
20346                             cn : [
20347                                 {
20348                                     tag : 'i',
20349                                     class : 'fa fa-chevron-left'
20350                                 }
20351                             ]
20352                         },
20353                         {
20354                             tag : 'div',
20355                             class : 'carousel-next',
20356                             cn : [
20357                                 {
20358                                     tag : 'i',
20359                                     class : 'fa fa-chevron-right'
20360                                 }
20361                             ]
20362                         }
20363                     ]
20364                 });
20365             }
20366             
20367         }
20368         
20369         return cfg;
20370     },
20371     
20372     initEvents:  function()
20373     {
20374 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20375 //            this.el.on("touchstart", this.onTouchStart, this);
20376 //        }
20377         
20378         if(this.autoslide){
20379             var _this = this;
20380             
20381             this.slideFn = window.setInterval(function() {
20382                 _this.showPanelNext();
20383             }, this.timer);
20384         }
20385         
20386         if(this.showarrow){
20387             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20388             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20389         }
20390         
20391         
20392     },
20393     
20394 //    onTouchStart : function(e, el, o)
20395 //    {
20396 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20397 //            return;
20398 //        }
20399 //        
20400 //        this.showPanelNext();
20401 //    },
20402     
20403     
20404     getChildContainer : function()
20405     {
20406         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20407     },
20408     
20409     /**
20410     * register a Navigation item
20411     * @param {Roo.bootstrap.NavItem} the navitem to add
20412     */
20413     register : function(item)
20414     {
20415         this.tabs.push( item);
20416         item.navId = this.navId; // not really needed..
20417         this.addBullet();
20418     
20419     },
20420     
20421     getActivePanel : function()
20422     {
20423         var r = false;
20424         Roo.each(this.tabs, function(t) {
20425             if (t.active) {
20426                 r = t;
20427                 return false;
20428             }
20429             return null;
20430         });
20431         return r;
20432         
20433     },
20434     getPanelByName : function(n)
20435     {
20436         var r = false;
20437         Roo.each(this.tabs, function(t) {
20438             if (t.tabId == n) {
20439                 r = t;
20440                 return false;
20441             }
20442             return null;
20443         });
20444         return r;
20445     },
20446     indexOfPanel : function(p)
20447     {
20448         var r = false;
20449         Roo.each(this.tabs, function(t,i) {
20450             if (t.tabId == p.tabId) {
20451                 r = i;
20452                 return false;
20453             }
20454             return null;
20455         });
20456         return r;
20457     },
20458     /**
20459      * show a specific panel
20460      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20461      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20462      */
20463     showPanel : function (pan)
20464     {
20465         if(this.transition || typeof(pan) == 'undefined'){
20466             Roo.log("waiting for the transitionend");
20467             return false;
20468         }
20469         
20470         if (typeof(pan) == 'number') {
20471             pan = this.tabs[pan];
20472         }
20473         
20474         if (typeof(pan) == 'string') {
20475             pan = this.getPanelByName(pan);
20476         }
20477         
20478         var cur = this.getActivePanel();
20479         
20480         if(!pan || !cur){
20481             Roo.log('pan or acitve pan is undefined');
20482             return false;
20483         }
20484         
20485         if (pan.tabId == this.getActivePanel().tabId) {
20486             return true;
20487         }
20488         
20489         if (false === cur.fireEvent('beforedeactivate')) {
20490             return false;
20491         }
20492         
20493         if(this.bullets > 0 && !Roo.isTouch){
20494             this.setActiveBullet(this.indexOfPanel(pan));
20495         }
20496         
20497         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20498             
20499             //class="carousel-item carousel-item-next carousel-item-left"
20500             
20501             this.transition = true;
20502             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20503             var lr = dir == 'next' ? 'left' : 'right';
20504             pan.el.addClass(dir); // or prev
20505             pan.el.addClass('carousel-item-' + dir); // or prev
20506             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20507             cur.el.addClass(lr); // or right
20508             pan.el.addClass(lr);
20509             cur.el.addClass('carousel-item-' +lr); // or right
20510             pan.el.addClass('carousel-item-' +lr);
20511             
20512             
20513             var _this = this;
20514             cur.el.on('transitionend', function() {
20515                 Roo.log("trans end?");
20516                 
20517                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20518                 pan.setActive(true);
20519                 
20520                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20521                 cur.setActive(false);
20522                 
20523                 _this.transition = false;
20524                 
20525             }, this, { single:  true } );
20526             
20527             return true;
20528         }
20529         
20530         cur.setActive(false);
20531         pan.setActive(true);
20532         
20533         return true;
20534         
20535     },
20536     showPanelNext : function()
20537     {
20538         var i = this.indexOfPanel(this.getActivePanel());
20539         
20540         if (i >= this.tabs.length - 1 && !this.autoslide) {
20541             return;
20542         }
20543         
20544         if (i >= this.tabs.length - 1 && this.autoslide) {
20545             i = -1;
20546         }
20547         
20548         this.showPanel(this.tabs[i+1]);
20549     },
20550     
20551     showPanelPrev : function()
20552     {
20553         var i = this.indexOfPanel(this.getActivePanel());
20554         
20555         if (i  < 1 && !this.autoslide) {
20556             return;
20557         }
20558         
20559         if (i < 1 && this.autoslide) {
20560             i = this.tabs.length;
20561         }
20562         
20563         this.showPanel(this.tabs[i-1]);
20564     },
20565     
20566     
20567     addBullet: function()
20568     {
20569         if(!this.bullets || Roo.isTouch){
20570             return;
20571         }
20572         var ctr = this.el.select('.carousel-bullets',true).first();
20573         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20574         var bullet = ctr.createChild({
20575             cls : 'bullet bullet-' + i
20576         },ctr.dom.lastChild);
20577         
20578         
20579         var _this = this;
20580         
20581         bullet.on('click', (function(e, el, o, ii, t){
20582
20583             e.preventDefault();
20584
20585             this.showPanel(ii);
20586
20587             if(this.autoslide && this.slideFn){
20588                 clearInterval(this.slideFn);
20589                 this.slideFn = window.setInterval(function() {
20590                     _this.showPanelNext();
20591                 }, this.timer);
20592             }
20593
20594         }).createDelegate(this, [i, bullet], true));
20595                 
20596         
20597     },
20598      
20599     setActiveBullet : function(i)
20600     {
20601         if(Roo.isTouch){
20602             return;
20603         }
20604         
20605         Roo.each(this.el.select('.bullet', true).elements, function(el){
20606             el.removeClass('selected');
20607         });
20608
20609         var bullet = this.el.select('.bullet-' + i, true).first();
20610         
20611         if(!bullet){
20612             return;
20613         }
20614         
20615         bullet.addClass('selected');
20616     }
20617     
20618     
20619   
20620 });
20621
20622  
20623
20624  
20625  
20626 Roo.apply(Roo.bootstrap.TabGroup, {
20627     
20628     groups: {},
20629      /**
20630     * register a Navigation Group
20631     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20632     */
20633     register : function(navgrp)
20634     {
20635         this.groups[navgrp.navId] = navgrp;
20636         
20637     },
20638     /**
20639     * fetch a Navigation Group based on the navigation ID
20640     * if one does not exist , it will get created.
20641     * @param {string} the navgroup to add
20642     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20643     */
20644     get: function(navId) {
20645         if (typeof(this.groups[navId]) == 'undefined') {
20646             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20647         }
20648         return this.groups[navId] ;
20649     }
20650     
20651     
20652     
20653 });
20654
20655  /*
20656  * - LGPL
20657  *
20658  * TabPanel
20659  * 
20660  */
20661
20662 /**
20663  * @class Roo.bootstrap.TabPanel
20664  * @extends Roo.bootstrap.Component
20665  * Bootstrap TabPanel class
20666  * @cfg {Boolean} active panel active
20667  * @cfg {String} html panel content
20668  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20669  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20670  * @cfg {String} href click to link..
20671  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20672  * 
20673  * 
20674  * @constructor
20675  * Create a new TabPanel
20676  * @param {Object} config The config object
20677  */
20678
20679 Roo.bootstrap.TabPanel = function(config){
20680     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20681     this.addEvents({
20682         /**
20683              * @event changed
20684              * Fires when the active status changes
20685              * @param {Roo.bootstrap.TabPanel} this
20686              * @param {Boolean} state the new state
20687             
20688          */
20689         'changed': true,
20690         /**
20691              * @event beforedeactivate
20692              * Fires before a tab is de-activated - can be used to do validation on a form.
20693              * @param {Roo.bootstrap.TabPanel} this
20694              * @return {Boolean} false if there is an error
20695             
20696          */
20697         'beforedeactivate': true
20698      });
20699     
20700     this.tabId = this.tabId || Roo.id();
20701   
20702 };
20703
20704 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20705     
20706     active: false,
20707     html: false,
20708     tabId: false,
20709     navId : false,
20710     href : '',
20711     touchSlide : false,
20712     getAutoCreate : function(){
20713         
20714         
20715         var cfg = {
20716             tag: 'div',
20717             // item is needed for carousel - not sure if it has any effect otherwise
20718             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20719             html: this.html || ''
20720         };
20721         
20722         if(this.active){
20723             cfg.cls += ' active';
20724         }
20725         
20726         if(this.tabId){
20727             cfg.tabId = this.tabId;
20728         }
20729         
20730         
20731         
20732         return cfg;
20733     },
20734     
20735     initEvents:  function()
20736     {
20737         var p = this.parent();
20738         
20739         this.navId = this.navId || p.navId;
20740         
20741         if (typeof(this.navId) != 'undefined') {
20742             // not really needed.. but just in case.. parent should be a NavGroup.
20743             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20744             
20745             tg.register(this);
20746             
20747             var i = tg.tabs.length - 1;
20748             
20749             if(this.active && tg.bullets > 0 && i < tg.bullets){
20750                 tg.setActiveBullet(i);
20751             }
20752         }
20753         
20754         this.el.on('click', this.onClick, this);
20755         
20756         if(Roo.isTouch && this.touchSlide){
20757             this.el.on("touchstart", this.onTouchStart, this);
20758             this.el.on("touchmove", this.onTouchMove, this);
20759             this.el.on("touchend", this.onTouchEnd, this);
20760         }
20761         
20762     },
20763     
20764     onRender : function(ct, position)
20765     {
20766         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20767     },
20768     
20769     setActive : function(state)
20770     {
20771         Roo.log("panel - set active " + this.tabId + "=" + state);
20772         
20773         this.active = state;
20774         if (!state) {
20775             this.el.removeClass('active');
20776             
20777         } else  if (!this.el.hasClass('active')) {
20778             this.el.addClass('active');
20779         }
20780         
20781         this.fireEvent('changed', this, state);
20782     },
20783     
20784     onClick : function(e)
20785     {
20786         e.preventDefault();
20787         
20788         if(!this.href.length){
20789             return;
20790         }
20791         
20792         window.location.href = this.href;
20793     },
20794     
20795     startX : 0,
20796     startY : 0,
20797     endX : 0,
20798     endY : 0,
20799     swiping : false,
20800     
20801     onTouchStart : function(e)
20802     {
20803         this.swiping = false;
20804         
20805         this.startX = e.browserEvent.touches[0].clientX;
20806         this.startY = e.browserEvent.touches[0].clientY;
20807     },
20808     
20809     onTouchMove : function(e)
20810     {
20811         this.swiping = true;
20812         
20813         this.endX = e.browserEvent.touches[0].clientX;
20814         this.endY = e.browserEvent.touches[0].clientY;
20815     },
20816     
20817     onTouchEnd : function(e)
20818     {
20819         if(!this.swiping){
20820             this.onClick(e);
20821             return;
20822         }
20823         
20824         var tabGroup = this.parent();
20825         
20826         if(this.endX > this.startX){ // swiping right
20827             tabGroup.showPanelPrev();
20828             return;
20829         }
20830         
20831         if(this.startX > this.endX){ // swiping left
20832             tabGroup.showPanelNext();
20833             return;
20834         }
20835     }
20836     
20837     
20838 });
20839  
20840
20841  
20842
20843  /*
20844  * - LGPL
20845  *
20846  * DateField
20847  * 
20848  */
20849
20850 /**
20851  * @class Roo.bootstrap.DateField
20852  * @extends Roo.bootstrap.Input
20853  * Bootstrap DateField class
20854  * @cfg {Number} weekStart default 0
20855  * @cfg {String} viewMode default empty, (months|years)
20856  * @cfg {String} minViewMode default empty, (months|years)
20857  * @cfg {Number} startDate default -Infinity
20858  * @cfg {Number} endDate default Infinity
20859  * @cfg {Boolean} todayHighlight default false
20860  * @cfg {Boolean} todayBtn default false
20861  * @cfg {Boolean} calendarWeeks default false
20862  * @cfg {Object} daysOfWeekDisabled default empty
20863  * @cfg {Boolean} singleMode default false (true | false)
20864  * 
20865  * @cfg {Boolean} keyboardNavigation default true
20866  * @cfg {String} language default en
20867  * 
20868  * @constructor
20869  * Create a new DateField
20870  * @param {Object} config The config object
20871  */
20872
20873 Roo.bootstrap.DateField = function(config){
20874     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20875      this.addEvents({
20876             /**
20877              * @event show
20878              * Fires when this field show.
20879              * @param {Roo.bootstrap.DateField} this
20880              * @param {Mixed} date The date value
20881              */
20882             show : true,
20883             /**
20884              * @event show
20885              * Fires when this field hide.
20886              * @param {Roo.bootstrap.DateField} this
20887              * @param {Mixed} date The date value
20888              */
20889             hide : true,
20890             /**
20891              * @event select
20892              * Fires when select a date.
20893              * @param {Roo.bootstrap.DateField} this
20894              * @param {Mixed} date The date value
20895              */
20896             select : true,
20897             /**
20898              * @event beforeselect
20899              * Fires when before select a date.
20900              * @param {Roo.bootstrap.DateField} this
20901              * @param {Mixed} date The date value
20902              */
20903             beforeselect : true
20904         });
20905 };
20906
20907 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20908     
20909     /**
20910      * @cfg {String} format
20911      * The default date format string which can be overriden for localization support.  The format must be
20912      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20913      */
20914     format : "m/d/y",
20915     /**
20916      * @cfg {String} altFormats
20917      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20918      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20919      */
20920     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20921     
20922     weekStart : 0,
20923     
20924     viewMode : '',
20925     
20926     minViewMode : '',
20927     
20928     todayHighlight : false,
20929     
20930     todayBtn: false,
20931     
20932     language: 'en',
20933     
20934     keyboardNavigation: true,
20935     
20936     calendarWeeks: false,
20937     
20938     startDate: -Infinity,
20939     
20940     endDate: Infinity,
20941     
20942     daysOfWeekDisabled: [],
20943     
20944     _events: [],
20945     
20946     singleMode : false,
20947     
20948     UTCDate: function()
20949     {
20950         return new Date(Date.UTC.apply(Date, arguments));
20951     },
20952     
20953     UTCToday: function()
20954     {
20955         var today = new Date();
20956         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20957     },
20958     
20959     getDate: function() {
20960             var d = this.getUTCDate();
20961             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20962     },
20963     
20964     getUTCDate: function() {
20965             return this.date;
20966     },
20967     
20968     setDate: function(d) {
20969             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20970     },
20971     
20972     setUTCDate: function(d) {
20973             this.date = d;
20974             this.setValue(this.formatDate(this.date));
20975     },
20976         
20977     onRender: function(ct, position)
20978     {
20979         
20980         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20981         
20982         this.language = this.language || 'en';
20983         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20984         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20985         
20986         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20987         this.format = this.format || 'm/d/y';
20988         this.isInline = false;
20989         this.isInput = true;
20990         this.component = this.el.select('.add-on', true).first() || false;
20991         this.component = (this.component && this.component.length === 0) ? false : this.component;
20992         this.hasInput = this.component && this.inputEl().length;
20993         
20994         if (typeof(this.minViewMode === 'string')) {
20995             switch (this.minViewMode) {
20996                 case 'months':
20997                     this.minViewMode = 1;
20998                     break;
20999                 case 'years':
21000                     this.minViewMode = 2;
21001                     break;
21002                 default:
21003                     this.minViewMode = 0;
21004                     break;
21005             }
21006         }
21007         
21008         if (typeof(this.viewMode === 'string')) {
21009             switch (this.viewMode) {
21010                 case 'months':
21011                     this.viewMode = 1;
21012                     break;
21013                 case 'years':
21014                     this.viewMode = 2;
21015                     break;
21016                 default:
21017                     this.viewMode = 0;
21018                     break;
21019             }
21020         }
21021                 
21022         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21023         
21024 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21025         
21026         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21027         
21028         this.picker().on('mousedown', this.onMousedown, this);
21029         this.picker().on('click', this.onClick, this);
21030         
21031         this.picker().addClass('datepicker-dropdown');
21032         
21033         this.startViewMode = this.viewMode;
21034         
21035         if(this.singleMode){
21036             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21037                 v.setVisibilityMode(Roo.Element.DISPLAY);
21038                 v.hide();
21039             });
21040             
21041             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21042                 v.setStyle('width', '189px');
21043             });
21044         }
21045         
21046         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21047             if(!this.calendarWeeks){
21048                 v.remove();
21049                 return;
21050             }
21051             
21052             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21053             v.attr('colspan', function(i, val){
21054                 return parseInt(val) + 1;
21055             });
21056         });
21057                         
21058         
21059         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21060         
21061         this.setStartDate(this.startDate);
21062         this.setEndDate(this.endDate);
21063         
21064         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21065         
21066         this.fillDow();
21067         this.fillMonths();
21068         this.update();
21069         this.showMode();
21070         
21071         if(this.isInline) {
21072             this.showPopup();
21073         }
21074     },
21075     
21076     picker : function()
21077     {
21078         return this.pickerEl;
21079 //        return this.el.select('.datepicker', true).first();
21080     },
21081     
21082     fillDow: function()
21083     {
21084         var dowCnt = this.weekStart;
21085         
21086         var dow = {
21087             tag: 'tr',
21088             cn: [
21089                 
21090             ]
21091         };
21092         
21093         if(this.calendarWeeks){
21094             dow.cn.push({
21095                 tag: 'th',
21096                 cls: 'cw',
21097                 html: '&nbsp;'
21098             })
21099         }
21100         
21101         while (dowCnt < this.weekStart + 7) {
21102             dow.cn.push({
21103                 tag: 'th',
21104                 cls: 'dow',
21105                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21106             });
21107         }
21108         
21109         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21110     },
21111     
21112     fillMonths: function()
21113     {    
21114         var i = 0;
21115         var months = this.picker().select('>.datepicker-months td', true).first();
21116         
21117         months.dom.innerHTML = '';
21118         
21119         while (i < 12) {
21120             var month = {
21121                 tag: 'span',
21122                 cls: 'month',
21123                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21124             };
21125             
21126             months.createChild(month);
21127         }
21128         
21129     },
21130     
21131     update: function()
21132     {
21133         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;
21134         
21135         if (this.date < this.startDate) {
21136             this.viewDate = new Date(this.startDate);
21137         } else if (this.date > this.endDate) {
21138             this.viewDate = new Date(this.endDate);
21139         } else {
21140             this.viewDate = new Date(this.date);
21141         }
21142         
21143         this.fill();
21144     },
21145     
21146     fill: function() 
21147     {
21148         var d = new Date(this.viewDate),
21149                 year = d.getUTCFullYear(),
21150                 month = d.getUTCMonth(),
21151                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21152                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21153                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21154                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21155                 currentDate = this.date && this.date.valueOf(),
21156                 today = this.UTCToday();
21157         
21158         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21159         
21160 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21161         
21162 //        this.picker.select('>tfoot th.today').
21163 //                                              .text(dates[this.language].today)
21164 //                                              .toggle(this.todayBtn !== false);
21165     
21166         this.updateNavArrows();
21167         this.fillMonths();
21168                                                 
21169         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21170         
21171         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21172          
21173         prevMonth.setUTCDate(day);
21174         
21175         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21176         
21177         var nextMonth = new Date(prevMonth);
21178         
21179         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21180         
21181         nextMonth = nextMonth.valueOf();
21182         
21183         var fillMonths = false;
21184         
21185         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21186         
21187         while(prevMonth.valueOf() <= nextMonth) {
21188             var clsName = '';
21189             
21190             if (prevMonth.getUTCDay() === this.weekStart) {
21191                 if(fillMonths){
21192                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21193                 }
21194                     
21195                 fillMonths = {
21196                     tag: 'tr',
21197                     cn: []
21198                 };
21199                 
21200                 if(this.calendarWeeks){
21201                     // ISO 8601: First week contains first thursday.
21202                     // ISO also states week starts on Monday, but we can be more abstract here.
21203                     var
21204                     // Start of current week: based on weekstart/current date
21205                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21206                     // Thursday of this week
21207                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21208                     // First Thursday of year, year from thursday
21209                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21210                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21211                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21212                     
21213                     fillMonths.cn.push({
21214                         tag: 'td',
21215                         cls: 'cw',
21216                         html: calWeek
21217                     });
21218                 }
21219             }
21220             
21221             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21222                 clsName += ' old';
21223             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21224                 clsName += ' new';
21225             }
21226             if (this.todayHighlight &&
21227                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21228                 prevMonth.getUTCMonth() == today.getMonth() &&
21229                 prevMonth.getUTCDate() == today.getDate()) {
21230                 clsName += ' today';
21231             }
21232             
21233             if (currentDate && prevMonth.valueOf() === currentDate) {
21234                 clsName += ' active';
21235             }
21236             
21237             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21238                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21239                     clsName += ' disabled';
21240             }
21241             
21242             fillMonths.cn.push({
21243                 tag: 'td',
21244                 cls: 'day ' + clsName,
21245                 html: prevMonth.getDate()
21246             });
21247             
21248             prevMonth.setDate(prevMonth.getDate()+1);
21249         }
21250           
21251         var currentYear = this.date && this.date.getUTCFullYear();
21252         var currentMonth = this.date && this.date.getUTCMonth();
21253         
21254         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21255         
21256         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21257             v.removeClass('active');
21258             
21259             if(currentYear === year && k === currentMonth){
21260                 v.addClass('active');
21261             }
21262             
21263             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21264                 v.addClass('disabled');
21265             }
21266             
21267         });
21268         
21269         
21270         year = parseInt(year/10, 10) * 10;
21271         
21272         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21273         
21274         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21275         
21276         year -= 1;
21277         for (var i = -1; i < 11; i++) {
21278             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21279                 tag: 'span',
21280                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21281                 html: year
21282             });
21283             
21284             year += 1;
21285         }
21286     },
21287     
21288     showMode: function(dir) 
21289     {
21290         if (dir) {
21291             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21292         }
21293         
21294         Roo.each(this.picker().select('>div',true).elements, function(v){
21295             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21296             v.hide();
21297         });
21298         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21299     },
21300     
21301     place: function()
21302     {
21303         if(this.isInline) {
21304             return;
21305         }
21306         
21307         this.picker().removeClass(['bottom', 'top']);
21308         
21309         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21310             /*
21311              * place to the top of element!
21312              *
21313              */
21314             
21315             this.picker().addClass('top');
21316             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21317             
21318             return;
21319         }
21320         
21321         this.picker().addClass('bottom');
21322         
21323         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21324     },
21325     
21326     parseDate : function(value)
21327     {
21328         if(!value || value instanceof Date){
21329             return value;
21330         }
21331         var v = Date.parseDate(value, this.format);
21332         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21333             v = Date.parseDate(value, 'Y-m-d');
21334         }
21335         if(!v && this.altFormats){
21336             if(!this.altFormatsArray){
21337                 this.altFormatsArray = this.altFormats.split("|");
21338             }
21339             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21340                 v = Date.parseDate(value, this.altFormatsArray[i]);
21341             }
21342         }
21343         return v;
21344     },
21345     
21346     formatDate : function(date, fmt)
21347     {   
21348         return (!date || !(date instanceof Date)) ?
21349         date : date.dateFormat(fmt || this.format);
21350     },
21351     
21352     onFocus : function()
21353     {
21354         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21355         this.showPopup();
21356     },
21357     
21358     onBlur : function()
21359     {
21360         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21361         
21362         var d = this.inputEl().getValue();
21363         
21364         this.setValue(d);
21365                 
21366         this.hidePopup();
21367     },
21368     
21369     showPopup : function()
21370     {
21371         this.picker().show();
21372         this.update();
21373         this.place();
21374         
21375         this.fireEvent('showpopup', this, this.date);
21376     },
21377     
21378     hidePopup : function()
21379     {
21380         if(this.isInline) {
21381             return;
21382         }
21383         this.picker().hide();
21384         this.viewMode = this.startViewMode;
21385         this.showMode();
21386         
21387         this.fireEvent('hidepopup', this, this.date);
21388         
21389     },
21390     
21391     onMousedown: function(e)
21392     {
21393         e.stopPropagation();
21394         e.preventDefault();
21395     },
21396     
21397     keyup: function(e)
21398     {
21399         Roo.bootstrap.DateField.superclass.keyup.call(this);
21400         this.update();
21401     },
21402
21403     setValue: function(v)
21404     {
21405         if(this.fireEvent('beforeselect', this, v) !== false){
21406             var d = new Date(this.parseDate(v) ).clearTime();
21407         
21408             if(isNaN(d.getTime())){
21409                 this.date = this.viewDate = '';
21410                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21411                 return;
21412             }
21413
21414             v = this.formatDate(d);
21415
21416             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21417
21418             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21419
21420             this.update();
21421
21422             this.fireEvent('select', this, this.date);
21423         }
21424     },
21425     
21426     getValue: function()
21427     {
21428         return this.formatDate(this.date);
21429     },
21430     
21431     fireKey: function(e)
21432     {
21433         if (!this.picker().isVisible()){
21434             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21435                 this.showPopup();
21436             }
21437             return;
21438         }
21439         
21440         var dateChanged = false,
21441         dir, day, month,
21442         newDate, newViewDate;
21443         
21444         switch(e.keyCode){
21445             case 27: // escape
21446                 this.hidePopup();
21447                 e.preventDefault();
21448                 break;
21449             case 37: // left
21450             case 39: // right
21451                 if (!this.keyboardNavigation) {
21452                     break;
21453                 }
21454                 dir = e.keyCode == 37 ? -1 : 1;
21455                 
21456                 if (e.ctrlKey){
21457                     newDate = this.moveYear(this.date, dir);
21458                     newViewDate = this.moveYear(this.viewDate, dir);
21459                 } else if (e.shiftKey){
21460                     newDate = this.moveMonth(this.date, dir);
21461                     newViewDate = this.moveMonth(this.viewDate, dir);
21462                 } else {
21463                     newDate = new Date(this.date);
21464                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21465                     newViewDate = new Date(this.viewDate);
21466                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21467                 }
21468                 if (this.dateWithinRange(newDate)){
21469                     this.date = newDate;
21470                     this.viewDate = newViewDate;
21471                     this.setValue(this.formatDate(this.date));
21472 //                    this.update();
21473                     e.preventDefault();
21474                     dateChanged = true;
21475                 }
21476                 break;
21477             case 38: // up
21478             case 40: // down
21479                 if (!this.keyboardNavigation) {
21480                     break;
21481                 }
21482                 dir = e.keyCode == 38 ? -1 : 1;
21483                 if (e.ctrlKey){
21484                     newDate = this.moveYear(this.date, dir);
21485                     newViewDate = this.moveYear(this.viewDate, dir);
21486                 } else if (e.shiftKey){
21487                     newDate = this.moveMonth(this.date, dir);
21488                     newViewDate = this.moveMonth(this.viewDate, dir);
21489                 } else {
21490                     newDate = new Date(this.date);
21491                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21492                     newViewDate = new Date(this.viewDate);
21493                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21494                 }
21495                 if (this.dateWithinRange(newDate)){
21496                     this.date = newDate;
21497                     this.viewDate = newViewDate;
21498                     this.setValue(this.formatDate(this.date));
21499 //                    this.update();
21500                     e.preventDefault();
21501                     dateChanged = true;
21502                 }
21503                 break;
21504             case 13: // enter
21505                 this.setValue(this.formatDate(this.date));
21506                 this.hidePopup();
21507                 e.preventDefault();
21508                 break;
21509             case 9: // tab
21510                 this.setValue(this.formatDate(this.date));
21511                 this.hidePopup();
21512                 break;
21513             case 16: // shift
21514             case 17: // ctrl
21515             case 18: // alt
21516                 break;
21517             default :
21518                 this.hidePopup();
21519                 
21520         }
21521     },
21522     
21523     
21524     onClick: function(e) 
21525     {
21526         e.stopPropagation();
21527         e.preventDefault();
21528         
21529         var target = e.getTarget();
21530         
21531         if(target.nodeName.toLowerCase() === 'i'){
21532             target = Roo.get(target).dom.parentNode;
21533         }
21534         
21535         var nodeName = target.nodeName;
21536         var className = target.className;
21537         var html = target.innerHTML;
21538         //Roo.log(nodeName);
21539         
21540         switch(nodeName.toLowerCase()) {
21541             case 'th':
21542                 switch(className) {
21543                     case 'switch':
21544                         this.showMode(1);
21545                         break;
21546                     case 'prev':
21547                     case 'next':
21548                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21549                         switch(this.viewMode){
21550                                 case 0:
21551                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21552                                         break;
21553                                 case 1:
21554                                 case 2:
21555                                         this.viewDate = this.moveYear(this.viewDate, dir);
21556                                         break;
21557                         }
21558                         this.fill();
21559                         break;
21560                     case 'today':
21561                         var date = new Date();
21562                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21563 //                        this.fill()
21564                         this.setValue(this.formatDate(this.date));
21565                         
21566                         this.hidePopup();
21567                         break;
21568                 }
21569                 break;
21570             case 'span':
21571                 if (className.indexOf('disabled') < 0) {
21572                     this.viewDate.setUTCDate(1);
21573                     if (className.indexOf('month') > -1) {
21574                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21575                     } else {
21576                         var year = parseInt(html, 10) || 0;
21577                         this.viewDate.setUTCFullYear(year);
21578                         
21579                     }
21580                     
21581                     if(this.singleMode){
21582                         this.setValue(this.formatDate(this.viewDate));
21583                         this.hidePopup();
21584                         return;
21585                     }
21586                     
21587                     this.showMode(-1);
21588                     this.fill();
21589                 }
21590                 break;
21591                 
21592             case 'td':
21593                 //Roo.log(className);
21594                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21595                     var day = parseInt(html, 10) || 1;
21596                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21597                         month = (this.viewDate || new Date()).getUTCMonth();
21598
21599                     if (className.indexOf('old') > -1) {
21600                         if(month === 0 ){
21601                             month = 11;
21602                             year -= 1;
21603                         }else{
21604                             month -= 1;
21605                         }
21606                     } else if (className.indexOf('new') > -1) {
21607                         if (month == 11) {
21608                             month = 0;
21609                             year += 1;
21610                         } else {
21611                             month += 1;
21612                         }
21613                     }
21614                     //Roo.log([year,month,day]);
21615                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21616                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21617 //                    this.fill();
21618                     //Roo.log(this.formatDate(this.date));
21619                     this.setValue(this.formatDate(this.date));
21620                     this.hidePopup();
21621                 }
21622                 break;
21623         }
21624     },
21625     
21626     setStartDate: function(startDate)
21627     {
21628         this.startDate = startDate || -Infinity;
21629         if (this.startDate !== -Infinity) {
21630             this.startDate = this.parseDate(this.startDate);
21631         }
21632         this.update();
21633         this.updateNavArrows();
21634     },
21635
21636     setEndDate: function(endDate)
21637     {
21638         this.endDate = endDate || Infinity;
21639         if (this.endDate !== Infinity) {
21640             this.endDate = this.parseDate(this.endDate);
21641         }
21642         this.update();
21643         this.updateNavArrows();
21644     },
21645     
21646     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21647     {
21648         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21649         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21650             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21651         }
21652         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21653             return parseInt(d, 10);
21654         });
21655         this.update();
21656         this.updateNavArrows();
21657     },
21658     
21659     updateNavArrows: function() 
21660     {
21661         if(this.singleMode){
21662             return;
21663         }
21664         
21665         var d = new Date(this.viewDate),
21666         year = d.getUTCFullYear(),
21667         month = d.getUTCMonth();
21668         
21669         Roo.each(this.picker().select('.prev', true).elements, function(v){
21670             v.show();
21671             switch (this.viewMode) {
21672                 case 0:
21673
21674                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21675                         v.hide();
21676                     }
21677                     break;
21678                 case 1:
21679                 case 2:
21680                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21681                         v.hide();
21682                     }
21683                     break;
21684             }
21685         });
21686         
21687         Roo.each(this.picker().select('.next', true).elements, function(v){
21688             v.show();
21689             switch (this.viewMode) {
21690                 case 0:
21691
21692                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21693                         v.hide();
21694                     }
21695                     break;
21696                 case 1:
21697                 case 2:
21698                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21699                         v.hide();
21700                     }
21701                     break;
21702             }
21703         })
21704     },
21705     
21706     moveMonth: function(date, dir)
21707     {
21708         if (!dir) {
21709             return date;
21710         }
21711         var new_date = new Date(date.valueOf()),
21712         day = new_date.getUTCDate(),
21713         month = new_date.getUTCMonth(),
21714         mag = Math.abs(dir),
21715         new_month, test;
21716         dir = dir > 0 ? 1 : -1;
21717         if (mag == 1){
21718             test = dir == -1
21719             // If going back one month, make sure month is not current month
21720             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21721             ? function(){
21722                 return new_date.getUTCMonth() == month;
21723             }
21724             // If going forward one month, make sure month is as expected
21725             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21726             : function(){
21727                 return new_date.getUTCMonth() != new_month;
21728             };
21729             new_month = month + dir;
21730             new_date.setUTCMonth(new_month);
21731             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21732             if (new_month < 0 || new_month > 11) {
21733                 new_month = (new_month + 12) % 12;
21734             }
21735         } else {
21736             // For magnitudes >1, move one month at a time...
21737             for (var i=0; i<mag; i++) {
21738                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21739                 new_date = this.moveMonth(new_date, dir);
21740             }
21741             // ...then reset the day, keeping it in the new month
21742             new_month = new_date.getUTCMonth();
21743             new_date.setUTCDate(day);
21744             test = function(){
21745                 return new_month != new_date.getUTCMonth();
21746             };
21747         }
21748         // Common date-resetting loop -- if date is beyond end of month, make it
21749         // end of month
21750         while (test()){
21751             new_date.setUTCDate(--day);
21752             new_date.setUTCMonth(new_month);
21753         }
21754         return new_date;
21755     },
21756
21757     moveYear: function(date, dir)
21758     {
21759         return this.moveMonth(date, dir*12);
21760     },
21761
21762     dateWithinRange: function(date)
21763     {
21764         return date >= this.startDate && date <= this.endDate;
21765     },
21766
21767     
21768     remove: function() 
21769     {
21770         this.picker().remove();
21771     },
21772     
21773     validateValue : function(value)
21774     {
21775         if(this.getVisibilityEl().hasClass('hidden')){
21776             return true;
21777         }
21778         
21779         if(value.length < 1)  {
21780             if(this.allowBlank){
21781                 return true;
21782             }
21783             return false;
21784         }
21785         
21786         if(value.length < this.minLength){
21787             return false;
21788         }
21789         if(value.length > this.maxLength){
21790             return false;
21791         }
21792         if(this.vtype){
21793             var vt = Roo.form.VTypes;
21794             if(!vt[this.vtype](value, this)){
21795                 return false;
21796             }
21797         }
21798         if(typeof this.validator == "function"){
21799             var msg = this.validator(value);
21800             if(msg !== true){
21801                 return false;
21802             }
21803         }
21804         
21805         if(this.regex && !this.regex.test(value)){
21806             return false;
21807         }
21808         
21809         if(typeof(this.parseDate(value)) == 'undefined'){
21810             return false;
21811         }
21812         
21813         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21814             return false;
21815         }      
21816         
21817         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21818             return false;
21819         } 
21820         
21821         
21822         return true;
21823     },
21824     
21825     reset : function()
21826     {
21827         this.date = this.viewDate = '';
21828         
21829         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21830     }
21831    
21832 });
21833
21834 Roo.apply(Roo.bootstrap.DateField,  {
21835     
21836     head : {
21837         tag: 'thead',
21838         cn: [
21839         {
21840             tag: 'tr',
21841             cn: [
21842             {
21843                 tag: 'th',
21844                 cls: 'prev',
21845                 html: '<i class="fa fa-arrow-left"/>'
21846             },
21847             {
21848                 tag: 'th',
21849                 cls: 'switch',
21850                 colspan: '5'
21851             },
21852             {
21853                 tag: 'th',
21854                 cls: 'next',
21855                 html: '<i class="fa fa-arrow-right"/>'
21856             }
21857
21858             ]
21859         }
21860         ]
21861     },
21862     
21863     content : {
21864         tag: 'tbody',
21865         cn: [
21866         {
21867             tag: 'tr',
21868             cn: [
21869             {
21870                 tag: 'td',
21871                 colspan: '7'
21872             }
21873             ]
21874         }
21875         ]
21876     },
21877     
21878     footer : {
21879         tag: 'tfoot',
21880         cn: [
21881         {
21882             tag: 'tr',
21883             cn: [
21884             {
21885                 tag: 'th',
21886                 colspan: '7',
21887                 cls: 'today'
21888             }
21889                     
21890             ]
21891         }
21892         ]
21893     },
21894     
21895     dates:{
21896         en: {
21897             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21898             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21899             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21900             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21901             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21902             today: "Today"
21903         }
21904     },
21905     
21906     modes: [
21907     {
21908         clsName: 'days',
21909         navFnc: 'Month',
21910         navStep: 1
21911     },
21912     {
21913         clsName: 'months',
21914         navFnc: 'FullYear',
21915         navStep: 1
21916     },
21917     {
21918         clsName: 'years',
21919         navFnc: 'FullYear',
21920         navStep: 10
21921     }]
21922 });
21923
21924 Roo.apply(Roo.bootstrap.DateField,  {
21925   
21926     template : {
21927         tag: 'div',
21928         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21929         cn: [
21930         {
21931             tag: 'div',
21932             cls: 'datepicker-days',
21933             cn: [
21934             {
21935                 tag: 'table',
21936                 cls: 'table-condensed',
21937                 cn:[
21938                 Roo.bootstrap.DateField.head,
21939                 {
21940                     tag: 'tbody'
21941                 },
21942                 Roo.bootstrap.DateField.footer
21943                 ]
21944             }
21945             ]
21946         },
21947         {
21948             tag: 'div',
21949             cls: 'datepicker-months',
21950             cn: [
21951             {
21952                 tag: 'table',
21953                 cls: 'table-condensed',
21954                 cn:[
21955                 Roo.bootstrap.DateField.head,
21956                 Roo.bootstrap.DateField.content,
21957                 Roo.bootstrap.DateField.footer
21958                 ]
21959             }
21960             ]
21961         },
21962         {
21963             tag: 'div',
21964             cls: 'datepicker-years',
21965             cn: [
21966             {
21967                 tag: 'table',
21968                 cls: 'table-condensed',
21969                 cn:[
21970                 Roo.bootstrap.DateField.head,
21971                 Roo.bootstrap.DateField.content,
21972                 Roo.bootstrap.DateField.footer
21973                 ]
21974             }
21975             ]
21976         }
21977         ]
21978     }
21979 });
21980
21981  
21982
21983  /*
21984  * - LGPL
21985  *
21986  * TimeField
21987  * 
21988  */
21989
21990 /**
21991  * @class Roo.bootstrap.TimeField
21992  * @extends Roo.bootstrap.Input
21993  * Bootstrap DateField class
21994  * 
21995  * 
21996  * @constructor
21997  * Create a new TimeField
21998  * @param {Object} config The config object
21999  */
22000
22001 Roo.bootstrap.TimeField = function(config){
22002     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22003     this.addEvents({
22004             /**
22005              * @event show
22006              * Fires when this field show.
22007              * @param {Roo.bootstrap.DateField} thisthis
22008              * @param {Mixed} date The date value
22009              */
22010             show : true,
22011             /**
22012              * @event show
22013              * Fires when this field hide.
22014              * @param {Roo.bootstrap.DateField} this
22015              * @param {Mixed} date The date value
22016              */
22017             hide : true,
22018             /**
22019              * @event select
22020              * Fires when select a date.
22021              * @param {Roo.bootstrap.DateField} this
22022              * @param {Mixed} date The date value
22023              */
22024             select : true
22025         });
22026 };
22027
22028 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22029     
22030     /**
22031      * @cfg {String} format
22032      * The default time format string which can be overriden for localization support.  The format must be
22033      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22034      */
22035     format : "H:i",
22036
22037     getAutoCreate : function()
22038     {
22039         this.after = '<i class="fa far fa-clock"></i>';
22040         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22041         
22042          
22043     },
22044     onRender: function(ct, position)
22045     {
22046         
22047         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22048                 
22049         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22050         
22051         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22052         
22053         this.pop = this.picker().select('>.datepicker-time',true).first();
22054         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22055         
22056         this.picker().on('mousedown', this.onMousedown, this);
22057         this.picker().on('click', this.onClick, this);
22058         
22059         this.picker().addClass('datepicker-dropdown');
22060     
22061         this.fillTime();
22062         this.update();
22063             
22064         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22065         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22066         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22067         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22068         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22069         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22070
22071     },
22072     
22073     fireKey: function(e){
22074         if (!this.picker().isVisible()){
22075             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22076                 this.show();
22077             }
22078             return;
22079         }
22080
22081         e.preventDefault();
22082         
22083         switch(e.keyCode){
22084             case 27: // escape
22085                 this.hide();
22086                 break;
22087             case 37: // left
22088             case 39: // right
22089                 this.onTogglePeriod();
22090                 break;
22091             case 38: // up
22092                 this.onIncrementMinutes();
22093                 break;
22094             case 40: // down
22095                 this.onDecrementMinutes();
22096                 break;
22097             case 13: // enter
22098             case 9: // tab
22099                 this.setTime();
22100                 break;
22101         }
22102     },
22103     
22104     onClick: function(e) {
22105         e.stopPropagation();
22106         e.preventDefault();
22107     },
22108     
22109     picker : function()
22110     {
22111         return this.pickerEl;
22112     },
22113     
22114     fillTime: function()
22115     {    
22116         var time = this.pop.select('tbody', true).first();
22117         
22118         time.dom.innerHTML = '';
22119         
22120         time.createChild({
22121             tag: 'tr',
22122             cn: [
22123                 {
22124                     tag: 'td',
22125                     cn: [
22126                         {
22127                             tag: 'a',
22128                             href: '#',
22129                             cls: 'btn',
22130                             cn: [
22131                                 {
22132                                     tag: 'i',
22133                                     cls: 'hours-up fa fas fa-chevron-up'
22134                                 }
22135                             ]
22136                         } 
22137                     ]
22138                 },
22139                 {
22140                     tag: 'td',
22141                     cls: 'separator'
22142                 },
22143                 {
22144                     tag: 'td',
22145                     cn: [
22146                         {
22147                             tag: 'a',
22148                             href: '#',
22149                             cls: 'btn',
22150                             cn: [
22151                                 {
22152                                     tag: 'i',
22153                                     cls: 'minutes-up fa fas fa-chevron-up'
22154                                 }
22155                             ]
22156                         }
22157                     ]
22158                 },
22159                 {
22160                     tag: 'td',
22161                     cls: 'separator'
22162                 }
22163             ]
22164         });
22165         
22166         time.createChild({
22167             tag: 'tr',
22168             cn: [
22169                 {
22170                     tag: 'td',
22171                     cn: [
22172                         {
22173                             tag: 'span',
22174                             cls: 'timepicker-hour',
22175                             html: '00'
22176                         }  
22177                     ]
22178                 },
22179                 {
22180                     tag: 'td',
22181                     cls: 'separator',
22182                     html: ':'
22183                 },
22184                 {
22185                     tag: 'td',
22186                     cn: [
22187                         {
22188                             tag: 'span',
22189                             cls: 'timepicker-minute',
22190                             html: '00'
22191                         }  
22192                     ]
22193                 },
22194                 {
22195                     tag: 'td',
22196                     cls: 'separator'
22197                 },
22198                 {
22199                     tag: 'td',
22200                     cn: [
22201                         {
22202                             tag: 'button',
22203                             type: 'button',
22204                             cls: 'btn btn-primary period',
22205                             html: 'AM'
22206                             
22207                         }
22208                     ]
22209                 }
22210             ]
22211         });
22212         
22213         time.createChild({
22214             tag: 'tr',
22215             cn: [
22216                 {
22217                     tag: 'td',
22218                     cn: [
22219                         {
22220                             tag: 'a',
22221                             href: '#',
22222                             cls: 'btn',
22223                             cn: [
22224                                 {
22225                                     tag: 'span',
22226                                     cls: 'hours-down fa fas fa-chevron-down'
22227                                 }
22228                             ]
22229                         }
22230                     ]
22231                 },
22232                 {
22233                     tag: 'td',
22234                     cls: 'separator'
22235                 },
22236                 {
22237                     tag: 'td',
22238                     cn: [
22239                         {
22240                             tag: 'a',
22241                             href: '#',
22242                             cls: 'btn',
22243                             cn: [
22244                                 {
22245                                     tag: 'span',
22246                                     cls: 'minutes-down fa fas fa-chevron-down'
22247                                 }
22248                             ]
22249                         }
22250                     ]
22251                 },
22252                 {
22253                     tag: 'td',
22254                     cls: 'separator'
22255                 }
22256             ]
22257         });
22258         
22259     },
22260     
22261     update: function()
22262     {
22263         
22264         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22265         
22266         this.fill();
22267     },
22268     
22269     fill: function() 
22270     {
22271         var hours = this.time.getHours();
22272         var minutes = this.time.getMinutes();
22273         var period = 'AM';
22274         
22275         if(hours > 11){
22276             period = 'PM';
22277         }
22278         
22279         if(hours == 0){
22280             hours = 12;
22281         }
22282         
22283         
22284         if(hours > 12){
22285             hours = hours - 12;
22286         }
22287         
22288         if(hours < 10){
22289             hours = '0' + hours;
22290         }
22291         
22292         if(minutes < 10){
22293             minutes = '0' + minutes;
22294         }
22295         
22296         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22297         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22298         this.pop.select('button', true).first().dom.innerHTML = period;
22299         
22300     },
22301     
22302     place: function()
22303     {   
22304         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22305         
22306         var cls = ['bottom'];
22307         
22308         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22309             cls.pop();
22310             cls.push('top');
22311         }
22312         
22313         cls.push('right');
22314         
22315         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22316             cls.pop();
22317             cls.push('left');
22318         }
22319         //this.picker().setXY(20000,20000);
22320         this.picker().addClass(cls.join('-'));
22321         
22322         var _this = this;
22323         
22324         Roo.each(cls, function(c){
22325             if(c == 'bottom'){
22326                 (function() {
22327                  //  
22328                 }).defer(200);
22329                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22330                 //_this.picker().setTop(_this.inputEl().getHeight());
22331                 return;
22332             }
22333             if(c == 'top'){
22334                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22335                 
22336                 //_this.picker().setTop(0 - _this.picker().getHeight());
22337                 return;
22338             }
22339             /*
22340             if(c == 'left'){
22341                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22342                 return;
22343             }
22344             if(c == 'right'){
22345                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22346                 return;
22347             }
22348             */
22349         });
22350         
22351     },
22352   
22353     onFocus : function()
22354     {
22355         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22356         this.show();
22357     },
22358     
22359     onBlur : function()
22360     {
22361         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22362         this.hide();
22363     },
22364     
22365     show : function()
22366     {
22367         this.picker().show();
22368         this.pop.show();
22369         this.update();
22370         this.place();
22371         
22372         this.fireEvent('show', this, this.date);
22373     },
22374     
22375     hide : function()
22376     {
22377         this.picker().hide();
22378         this.pop.hide();
22379         
22380         this.fireEvent('hide', this, this.date);
22381     },
22382     
22383     setTime : function()
22384     {
22385         this.hide();
22386         this.setValue(this.time.format(this.format));
22387         
22388         this.fireEvent('select', this, this.date);
22389         
22390         
22391     },
22392     
22393     onMousedown: function(e){
22394         e.stopPropagation();
22395         e.preventDefault();
22396     },
22397     
22398     onIncrementHours: function()
22399     {
22400         Roo.log('onIncrementHours');
22401         this.time = this.time.add(Date.HOUR, 1);
22402         this.update();
22403         
22404     },
22405     
22406     onDecrementHours: function()
22407     {
22408         Roo.log('onDecrementHours');
22409         this.time = this.time.add(Date.HOUR, -1);
22410         this.update();
22411     },
22412     
22413     onIncrementMinutes: function()
22414     {
22415         Roo.log('onIncrementMinutes');
22416         this.time = this.time.add(Date.MINUTE, 1);
22417         this.update();
22418     },
22419     
22420     onDecrementMinutes: function()
22421     {
22422         Roo.log('onDecrementMinutes');
22423         this.time = this.time.add(Date.MINUTE, -1);
22424         this.update();
22425     },
22426     
22427     onTogglePeriod: function()
22428     {
22429         Roo.log('onTogglePeriod');
22430         this.time = this.time.add(Date.HOUR, 12);
22431         this.update();
22432     }
22433     
22434    
22435 });
22436  
22437
22438 Roo.apply(Roo.bootstrap.TimeField,  {
22439   
22440     template : {
22441         tag: 'div',
22442         cls: 'datepicker dropdown-menu',
22443         cn: [
22444             {
22445                 tag: 'div',
22446                 cls: 'datepicker-time',
22447                 cn: [
22448                 {
22449                     tag: 'table',
22450                     cls: 'table-condensed',
22451                     cn:[
22452                         {
22453                             tag: 'tbody',
22454                             cn: [
22455                                 {
22456                                     tag: 'tr',
22457                                     cn: [
22458                                     {
22459                                         tag: 'td',
22460                                         colspan: '7'
22461                                     }
22462                                     ]
22463                                 }
22464                             ]
22465                         },
22466                         {
22467                             tag: 'tfoot',
22468                             cn: [
22469                                 {
22470                                     tag: 'tr',
22471                                     cn: [
22472                                     {
22473                                         tag: 'th',
22474                                         colspan: '7',
22475                                         cls: '',
22476                                         cn: [
22477                                             {
22478                                                 tag: 'button',
22479                                                 cls: 'btn btn-info ok',
22480                                                 html: 'OK'
22481                                             }
22482                                         ]
22483                                     }
22484                     
22485                                     ]
22486                                 }
22487                             ]
22488                         }
22489                     ]
22490                 }
22491                 ]
22492             }
22493         ]
22494     }
22495 });
22496
22497  
22498
22499  /*
22500  * - LGPL
22501  *
22502  * MonthField
22503  * 
22504  */
22505
22506 /**
22507  * @class Roo.bootstrap.MonthField
22508  * @extends Roo.bootstrap.Input
22509  * Bootstrap MonthField class
22510  * 
22511  * @cfg {String} language default en
22512  * 
22513  * @constructor
22514  * Create a new MonthField
22515  * @param {Object} config The config object
22516  */
22517
22518 Roo.bootstrap.MonthField = function(config){
22519     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22520     
22521     this.addEvents({
22522         /**
22523          * @event show
22524          * Fires when this field show.
22525          * @param {Roo.bootstrap.MonthField} this
22526          * @param {Mixed} date The date value
22527          */
22528         show : true,
22529         /**
22530          * @event show
22531          * Fires when this field hide.
22532          * @param {Roo.bootstrap.MonthField} this
22533          * @param {Mixed} date The date value
22534          */
22535         hide : true,
22536         /**
22537          * @event select
22538          * Fires when select a date.
22539          * @param {Roo.bootstrap.MonthField} this
22540          * @param {String} oldvalue The old value
22541          * @param {String} newvalue The new value
22542          */
22543         select : true
22544     });
22545 };
22546
22547 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22548     
22549     onRender: function(ct, position)
22550     {
22551         
22552         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22553         
22554         this.language = this.language || 'en';
22555         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22556         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22557         
22558         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22559         this.isInline = false;
22560         this.isInput = true;
22561         this.component = this.el.select('.add-on', true).first() || false;
22562         this.component = (this.component && this.component.length === 0) ? false : this.component;
22563         this.hasInput = this.component && this.inputEL().length;
22564         
22565         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22566         
22567         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22568         
22569         this.picker().on('mousedown', this.onMousedown, this);
22570         this.picker().on('click', this.onClick, this);
22571         
22572         this.picker().addClass('datepicker-dropdown');
22573         
22574         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22575             v.setStyle('width', '189px');
22576         });
22577         
22578         this.fillMonths();
22579         
22580         this.update();
22581         
22582         if(this.isInline) {
22583             this.show();
22584         }
22585         
22586     },
22587     
22588     setValue: function(v, suppressEvent)
22589     {   
22590         var o = this.getValue();
22591         
22592         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22593         
22594         this.update();
22595
22596         if(suppressEvent !== true){
22597             this.fireEvent('select', this, o, v);
22598         }
22599         
22600     },
22601     
22602     getValue: function()
22603     {
22604         return this.value;
22605     },
22606     
22607     onClick: function(e) 
22608     {
22609         e.stopPropagation();
22610         e.preventDefault();
22611         
22612         var target = e.getTarget();
22613         
22614         if(target.nodeName.toLowerCase() === 'i'){
22615             target = Roo.get(target).dom.parentNode;
22616         }
22617         
22618         var nodeName = target.nodeName;
22619         var className = target.className;
22620         var html = target.innerHTML;
22621         
22622         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22623             return;
22624         }
22625         
22626         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22627         
22628         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22629         
22630         this.hide();
22631                         
22632     },
22633     
22634     picker : function()
22635     {
22636         return this.pickerEl;
22637     },
22638     
22639     fillMonths: function()
22640     {    
22641         var i = 0;
22642         var months = this.picker().select('>.datepicker-months td', true).first();
22643         
22644         months.dom.innerHTML = '';
22645         
22646         while (i < 12) {
22647             var month = {
22648                 tag: 'span',
22649                 cls: 'month',
22650                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22651             };
22652             
22653             months.createChild(month);
22654         }
22655         
22656     },
22657     
22658     update: function()
22659     {
22660         var _this = this;
22661         
22662         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22663             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22664         }
22665         
22666         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22667             e.removeClass('active');
22668             
22669             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22670                 e.addClass('active');
22671             }
22672         })
22673     },
22674     
22675     place: function()
22676     {
22677         if(this.isInline) {
22678             return;
22679         }
22680         
22681         this.picker().removeClass(['bottom', 'top']);
22682         
22683         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22684             /*
22685              * place to the top of element!
22686              *
22687              */
22688             
22689             this.picker().addClass('top');
22690             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22691             
22692             return;
22693         }
22694         
22695         this.picker().addClass('bottom');
22696         
22697         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22698     },
22699     
22700     onFocus : function()
22701     {
22702         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22703         this.show();
22704     },
22705     
22706     onBlur : function()
22707     {
22708         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22709         
22710         var d = this.inputEl().getValue();
22711         
22712         this.setValue(d);
22713                 
22714         this.hide();
22715     },
22716     
22717     show : function()
22718     {
22719         this.picker().show();
22720         this.picker().select('>.datepicker-months', true).first().show();
22721         this.update();
22722         this.place();
22723         
22724         this.fireEvent('show', this, this.date);
22725     },
22726     
22727     hide : function()
22728     {
22729         if(this.isInline) {
22730             return;
22731         }
22732         this.picker().hide();
22733         this.fireEvent('hide', this, this.date);
22734         
22735     },
22736     
22737     onMousedown: function(e)
22738     {
22739         e.stopPropagation();
22740         e.preventDefault();
22741     },
22742     
22743     keyup: function(e)
22744     {
22745         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22746         this.update();
22747     },
22748
22749     fireKey: function(e)
22750     {
22751         if (!this.picker().isVisible()){
22752             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22753                 this.show();
22754             }
22755             return;
22756         }
22757         
22758         var dir;
22759         
22760         switch(e.keyCode){
22761             case 27: // escape
22762                 this.hide();
22763                 e.preventDefault();
22764                 break;
22765             case 37: // left
22766             case 39: // right
22767                 dir = e.keyCode == 37 ? -1 : 1;
22768                 
22769                 this.vIndex = this.vIndex + dir;
22770                 
22771                 if(this.vIndex < 0){
22772                     this.vIndex = 0;
22773                 }
22774                 
22775                 if(this.vIndex > 11){
22776                     this.vIndex = 11;
22777                 }
22778                 
22779                 if(isNaN(this.vIndex)){
22780                     this.vIndex = 0;
22781                 }
22782                 
22783                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22784                 
22785                 break;
22786             case 38: // up
22787             case 40: // down
22788                 
22789                 dir = e.keyCode == 38 ? -1 : 1;
22790                 
22791                 this.vIndex = this.vIndex + dir * 4;
22792                 
22793                 if(this.vIndex < 0){
22794                     this.vIndex = 0;
22795                 }
22796                 
22797                 if(this.vIndex > 11){
22798                     this.vIndex = 11;
22799                 }
22800                 
22801                 if(isNaN(this.vIndex)){
22802                     this.vIndex = 0;
22803                 }
22804                 
22805                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22806                 break;
22807                 
22808             case 13: // enter
22809                 
22810                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22811                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22812                 }
22813                 
22814                 this.hide();
22815                 e.preventDefault();
22816                 break;
22817             case 9: // tab
22818                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22819                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22820                 }
22821                 this.hide();
22822                 break;
22823             case 16: // shift
22824             case 17: // ctrl
22825             case 18: // alt
22826                 break;
22827             default :
22828                 this.hide();
22829                 
22830         }
22831     },
22832     
22833     remove: function() 
22834     {
22835         this.picker().remove();
22836     }
22837    
22838 });
22839
22840 Roo.apply(Roo.bootstrap.MonthField,  {
22841     
22842     content : {
22843         tag: 'tbody',
22844         cn: [
22845         {
22846             tag: 'tr',
22847             cn: [
22848             {
22849                 tag: 'td',
22850                 colspan: '7'
22851             }
22852             ]
22853         }
22854         ]
22855     },
22856     
22857     dates:{
22858         en: {
22859             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22860             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22861         }
22862     }
22863 });
22864
22865 Roo.apply(Roo.bootstrap.MonthField,  {
22866   
22867     template : {
22868         tag: 'div',
22869         cls: 'datepicker dropdown-menu roo-dynamic',
22870         cn: [
22871             {
22872                 tag: 'div',
22873                 cls: 'datepicker-months',
22874                 cn: [
22875                 {
22876                     tag: 'table',
22877                     cls: 'table-condensed',
22878                     cn:[
22879                         Roo.bootstrap.DateField.content
22880                     ]
22881                 }
22882                 ]
22883             }
22884         ]
22885     }
22886 });
22887
22888  
22889
22890  
22891  /*
22892  * - LGPL
22893  *
22894  * CheckBox
22895  * 
22896  */
22897
22898 /**
22899  * @class Roo.bootstrap.CheckBox
22900  * @extends Roo.bootstrap.Input
22901  * Bootstrap CheckBox class
22902  * 
22903  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22904  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22905  * @cfg {String} boxLabel The text that appears beside the checkbox
22906  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22907  * @cfg {Boolean} checked initnal the element
22908  * @cfg {Boolean} inline inline the element (default false)
22909  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22910  * @cfg {String} tooltip label tooltip
22911  * 
22912  * @constructor
22913  * Create a new CheckBox
22914  * @param {Object} config The config object
22915  */
22916
22917 Roo.bootstrap.CheckBox = function(config){
22918     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22919    
22920     this.addEvents({
22921         /**
22922         * @event check
22923         * Fires when the element is checked or unchecked.
22924         * @param {Roo.bootstrap.CheckBox} this This input
22925         * @param {Boolean} checked The new checked value
22926         */
22927        check : true,
22928        /**
22929         * @event click
22930         * Fires when the element is click.
22931         * @param {Roo.bootstrap.CheckBox} this This input
22932         */
22933        click : true
22934     });
22935     
22936 };
22937
22938 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22939   
22940     inputType: 'checkbox',
22941     inputValue: 1,
22942     valueOff: 0,
22943     boxLabel: false,
22944     checked: false,
22945     weight : false,
22946     inline: false,
22947     tooltip : '',
22948     
22949     // checkbox success does not make any sense really.. 
22950     invalidClass : "",
22951     validClass : "",
22952     
22953     
22954     getAutoCreate : function()
22955     {
22956         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22957         
22958         var id = Roo.id();
22959         
22960         var cfg = {};
22961         
22962         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22963         
22964         if(this.inline){
22965             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22966         }
22967         
22968         var input =  {
22969             tag: 'input',
22970             id : id,
22971             type : this.inputType,
22972             value : this.inputValue,
22973             cls : 'roo-' + this.inputType, //'form-box',
22974             placeholder : this.placeholder || ''
22975             
22976         };
22977         
22978         if(this.inputType != 'radio'){
22979             var hidden =  {
22980                 tag: 'input',
22981                 type : 'hidden',
22982                 cls : 'roo-hidden-value',
22983                 value : this.checked ? this.inputValue : this.valueOff
22984             };
22985         }
22986         
22987             
22988         if (this.weight) { // Validity check?
22989             cfg.cls += " " + this.inputType + "-" + this.weight;
22990         }
22991         
22992         if (this.disabled) {
22993             input.disabled=true;
22994         }
22995         
22996         if(this.checked){
22997             input.checked = this.checked;
22998         }
22999         
23000         if (this.name) {
23001             
23002             input.name = this.name;
23003             
23004             if(this.inputType != 'radio'){
23005                 hidden.name = this.name;
23006                 input.name = '_hidden_' + this.name;
23007             }
23008         }
23009         
23010         if (this.size) {
23011             input.cls += ' input-' + this.size;
23012         }
23013         
23014         var settings=this;
23015         
23016         ['xs','sm','md','lg'].map(function(size){
23017             if (settings[size]) {
23018                 cfg.cls += ' col-' + size + '-' + settings[size];
23019             }
23020         });
23021         
23022         var inputblock = input;
23023          
23024         if (this.before || this.after) {
23025             
23026             inputblock = {
23027                 cls : 'input-group',
23028                 cn :  [] 
23029             };
23030             
23031             if (this.before) {
23032                 inputblock.cn.push({
23033                     tag :'span',
23034                     cls : 'input-group-addon',
23035                     html : this.before
23036                 });
23037             }
23038             
23039             inputblock.cn.push(input);
23040             
23041             if(this.inputType != 'radio'){
23042                 inputblock.cn.push(hidden);
23043             }
23044             
23045             if (this.after) {
23046                 inputblock.cn.push({
23047                     tag :'span',
23048                     cls : 'input-group-addon',
23049                     html : this.after
23050                 });
23051             }
23052             
23053         }
23054         var boxLabelCfg = false;
23055         
23056         if(this.boxLabel){
23057            
23058             boxLabelCfg = {
23059                 tag: 'label',
23060                 //'for': id, // box label is handled by onclick - so no for...
23061                 cls: 'box-label',
23062                 html: this.boxLabel
23063             };
23064             if(this.tooltip){
23065                 boxLabelCfg.tooltip = this.tooltip;
23066             }
23067              
23068         }
23069         
23070         
23071         if (align ==='left' && this.fieldLabel.length) {
23072 //                Roo.log("left and has label");
23073             cfg.cn = [
23074                 {
23075                     tag: 'label',
23076                     'for' :  id,
23077                     cls : 'control-label',
23078                     html : this.fieldLabel
23079                 },
23080                 {
23081                     cls : "", 
23082                     cn: [
23083                         inputblock
23084                     ]
23085                 }
23086             ];
23087             
23088             if (boxLabelCfg) {
23089                 cfg.cn[1].cn.push(boxLabelCfg);
23090             }
23091             
23092             if(this.labelWidth > 12){
23093                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23094             }
23095             
23096             if(this.labelWidth < 13 && this.labelmd == 0){
23097                 this.labelmd = this.labelWidth;
23098             }
23099             
23100             if(this.labellg > 0){
23101                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23102                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23103             }
23104             
23105             if(this.labelmd > 0){
23106                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23107                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23108             }
23109             
23110             if(this.labelsm > 0){
23111                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23112                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23113             }
23114             
23115             if(this.labelxs > 0){
23116                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23117                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23118             }
23119             
23120         } else if ( this.fieldLabel.length) {
23121 //                Roo.log(" label");
23122                 cfg.cn = [
23123                    
23124                     {
23125                         tag: this.boxLabel ? 'span' : 'label',
23126                         'for': id,
23127                         cls: 'control-label box-input-label',
23128                         //cls : 'input-group-addon',
23129                         html : this.fieldLabel
23130                     },
23131                     
23132                     inputblock
23133                     
23134                 ];
23135                 if (boxLabelCfg) {
23136                     cfg.cn.push(boxLabelCfg);
23137                 }
23138
23139         } else {
23140             
23141 //                Roo.log(" no label && no align");
23142                 cfg.cn = [  inputblock ] ;
23143                 if (boxLabelCfg) {
23144                     cfg.cn.push(boxLabelCfg);
23145                 }
23146
23147                 
23148         }
23149         
23150        
23151         
23152         if(this.inputType != 'radio'){
23153             cfg.cn.push(hidden);
23154         }
23155         
23156         return cfg;
23157         
23158     },
23159     
23160     /**
23161      * return the real input element.
23162      */
23163     inputEl: function ()
23164     {
23165         return this.el.select('input.roo-' + this.inputType,true).first();
23166     },
23167     hiddenEl: function ()
23168     {
23169         return this.el.select('input.roo-hidden-value',true).first();
23170     },
23171     
23172     labelEl: function()
23173     {
23174         return this.el.select('label.control-label',true).first();
23175     },
23176     /* depricated... */
23177     
23178     label: function()
23179     {
23180         return this.labelEl();
23181     },
23182     
23183     boxLabelEl: function()
23184     {
23185         return this.el.select('label.box-label',true).first();
23186     },
23187     
23188     initEvents : function()
23189     {
23190 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23191         
23192         this.inputEl().on('click', this.onClick,  this);
23193         
23194         if (this.boxLabel) { 
23195             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23196         }
23197         
23198         this.startValue = this.getValue();
23199         
23200         if(this.groupId){
23201             Roo.bootstrap.CheckBox.register(this);
23202         }
23203     },
23204     
23205     onClick : function(e)
23206     {   
23207         if(this.fireEvent('click', this, e) !== false){
23208             this.setChecked(!this.checked);
23209         }
23210         
23211     },
23212     
23213     setChecked : function(state,suppressEvent)
23214     {
23215         this.startValue = this.getValue();
23216
23217         if(this.inputType == 'radio'){
23218             
23219             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23220                 e.dom.checked = false;
23221             });
23222             
23223             this.inputEl().dom.checked = true;
23224             
23225             this.inputEl().dom.value = this.inputValue;
23226             
23227             if(suppressEvent !== true){
23228                 this.fireEvent('check', this, true);
23229             }
23230             
23231             this.validate();
23232             
23233             return;
23234         }
23235         
23236         this.checked = state;
23237         
23238         this.inputEl().dom.checked = state;
23239         
23240         
23241         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23242         
23243         if(suppressEvent !== true){
23244             this.fireEvent('check', this, state);
23245         }
23246         
23247         this.validate();
23248     },
23249     
23250     getValue : function()
23251     {
23252         if(this.inputType == 'radio'){
23253             return this.getGroupValue();
23254         }
23255         
23256         return this.hiddenEl().dom.value;
23257         
23258     },
23259     
23260     getGroupValue : function()
23261     {
23262         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23263             return '';
23264         }
23265         
23266         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23267     },
23268     
23269     setValue : function(v,suppressEvent)
23270     {
23271         if(this.inputType == 'radio'){
23272             this.setGroupValue(v, suppressEvent);
23273             return;
23274         }
23275         
23276         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23277         
23278         this.validate();
23279     },
23280     
23281     setGroupValue : function(v, suppressEvent)
23282     {
23283         this.startValue = this.getValue();
23284         
23285         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23286             e.dom.checked = false;
23287             
23288             if(e.dom.value == v){
23289                 e.dom.checked = true;
23290             }
23291         });
23292         
23293         if(suppressEvent !== true){
23294             this.fireEvent('check', this, true);
23295         }
23296
23297         this.validate();
23298         
23299         return;
23300     },
23301     
23302     validate : function()
23303     {
23304         if(this.getVisibilityEl().hasClass('hidden')){
23305             return true;
23306         }
23307         
23308         if(
23309                 this.disabled || 
23310                 (this.inputType == 'radio' && this.validateRadio()) ||
23311                 (this.inputType == 'checkbox' && this.validateCheckbox())
23312         ){
23313             this.markValid();
23314             return true;
23315         }
23316         
23317         this.markInvalid();
23318         return false;
23319     },
23320     
23321     validateRadio : function()
23322     {
23323         if(this.getVisibilityEl().hasClass('hidden')){
23324             return true;
23325         }
23326         
23327         if(this.allowBlank){
23328             return true;
23329         }
23330         
23331         var valid = false;
23332         
23333         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23334             if(!e.dom.checked){
23335                 return;
23336             }
23337             
23338             valid = true;
23339             
23340             return false;
23341         });
23342         
23343         return valid;
23344     },
23345     
23346     validateCheckbox : function()
23347     {
23348         if(!this.groupId){
23349             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23350             //return (this.getValue() == this.inputValue) ? true : false;
23351         }
23352         
23353         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23354         
23355         if(!group){
23356             return false;
23357         }
23358         
23359         var r = false;
23360         
23361         for(var i in group){
23362             if(group[i].el.isVisible(true)){
23363                 r = false;
23364                 break;
23365             }
23366             
23367             r = true;
23368         }
23369         
23370         for(var i in group){
23371             if(r){
23372                 break;
23373             }
23374             
23375             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23376         }
23377         
23378         return r;
23379     },
23380     
23381     /**
23382      * Mark this field as valid
23383      */
23384     markValid : function()
23385     {
23386         var _this = this;
23387         
23388         this.fireEvent('valid', this);
23389         
23390         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23391         
23392         if(this.groupId){
23393             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23394         }
23395         
23396         if(label){
23397             label.markValid();
23398         }
23399
23400         if(this.inputType == 'radio'){
23401             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23402                 var fg = e.findParent('.form-group', false, true);
23403                 if (Roo.bootstrap.version == 3) {
23404                     fg.removeClass([_this.invalidClass, _this.validClass]);
23405                     fg.addClass(_this.validClass);
23406                 } else {
23407                     fg.removeClass(['is-valid', 'is-invalid']);
23408                     fg.addClass('is-valid');
23409                 }
23410             });
23411             
23412             return;
23413         }
23414
23415         if(!this.groupId){
23416             var fg = this.el.findParent('.form-group', false, true);
23417             if (Roo.bootstrap.version == 3) {
23418                 fg.removeClass([this.invalidClass, this.validClass]);
23419                 fg.addClass(this.validClass);
23420             } else {
23421                 fg.removeClass(['is-valid', 'is-invalid']);
23422                 fg.addClass('is-valid');
23423             }
23424             return;
23425         }
23426         
23427         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23428         
23429         if(!group){
23430             return;
23431         }
23432         
23433         for(var i in group){
23434             var fg = group[i].el.findParent('.form-group', false, true);
23435             if (Roo.bootstrap.version == 3) {
23436                 fg.removeClass([this.invalidClass, this.validClass]);
23437                 fg.addClass(this.validClass);
23438             } else {
23439                 fg.removeClass(['is-valid', 'is-invalid']);
23440                 fg.addClass('is-valid');
23441             }
23442         }
23443     },
23444     
23445      /**
23446      * Mark this field as invalid
23447      * @param {String} msg The validation message
23448      */
23449     markInvalid : function(msg)
23450     {
23451         if(this.allowBlank){
23452             return;
23453         }
23454         
23455         var _this = this;
23456         
23457         this.fireEvent('invalid', this, msg);
23458         
23459         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23460         
23461         if(this.groupId){
23462             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23463         }
23464         
23465         if(label){
23466             label.markInvalid();
23467         }
23468             
23469         if(this.inputType == 'radio'){
23470             
23471             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23472                 var fg = e.findParent('.form-group', false, true);
23473                 if (Roo.bootstrap.version == 3) {
23474                     fg.removeClass([_this.invalidClass, _this.validClass]);
23475                     fg.addClass(_this.invalidClass);
23476                 } else {
23477                     fg.removeClass(['is-invalid', 'is-valid']);
23478                     fg.addClass('is-invalid');
23479                 }
23480             });
23481             
23482             return;
23483         }
23484         
23485         if(!this.groupId){
23486             var fg = this.el.findParent('.form-group', false, true);
23487             if (Roo.bootstrap.version == 3) {
23488                 fg.removeClass([_this.invalidClass, _this.validClass]);
23489                 fg.addClass(_this.invalidClass);
23490             } else {
23491                 fg.removeClass(['is-invalid', 'is-valid']);
23492                 fg.addClass('is-invalid');
23493             }
23494             return;
23495         }
23496         
23497         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23498         
23499         if(!group){
23500             return;
23501         }
23502         
23503         for(var i in group){
23504             var fg = group[i].el.findParent('.form-group', false, true);
23505             if (Roo.bootstrap.version == 3) {
23506                 fg.removeClass([_this.invalidClass, _this.validClass]);
23507                 fg.addClass(_this.invalidClass);
23508             } else {
23509                 fg.removeClass(['is-invalid', 'is-valid']);
23510                 fg.addClass('is-invalid');
23511             }
23512         }
23513         
23514     },
23515     
23516     clearInvalid : function()
23517     {
23518         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23519         
23520         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23521         
23522         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23523         
23524         if (label && label.iconEl) {
23525             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23526             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23527         }
23528     },
23529     
23530     disable : function()
23531     {
23532         if(this.inputType != 'radio'){
23533             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23534             return;
23535         }
23536         
23537         var _this = this;
23538         
23539         if(this.rendered){
23540             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23541                 _this.getActionEl().addClass(this.disabledClass);
23542                 e.dom.disabled = true;
23543             });
23544         }
23545         
23546         this.disabled = true;
23547         this.fireEvent("disable", this);
23548         return this;
23549     },
23550
23551     enable : function()
23552     {
23553         if(this.inputType != 'radio'){
23554             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23555             return;
23556         }
23557         
23558         var _this = this;
23559         
23560         if(this.rendered){
23561             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23562                 _this.getActionEl().removeClass(this.disabledClass);
23563                 e.dom.disabled = false;
23564             });
23565         }
23566         
23567         this.disabled = false;
23568         this.fireEvent("enable", this);
23569         return this;
23570     },
23571     
23572     setBoxLabel : function(v)
23573     {
23574         this.boxLabel = v;
23575         
23576         if(this.rendered){
23577             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23578         }
23579     }
23580
23581 });
23582
23583 Roo.apply(Roo.bootstrap.CheckBox, {
23584     
23585     groups: {},
23586     
23587      /**
23588     * register a CheckBox Group
23589     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23590     */
23591     register : function(checkbox)
23592     {
23593         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23594             this.groups[checkbox.groupId] = {};
23595         }
23596         
23597         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23598             return;
23599         }
23600         
23601         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23602         
23603     },
23604     /**
23605     * fetch a CheckBox Group based on the group ID
23606     * @param {string} the group ID
23607     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23608     */
23609     get: function(groupId) {
23610         if (typeof(this.groups[groupId]) == 'undefined') {
23611             return false;
23612         }
23613         
23614         return this.groups[groupId] ;
23615     }
23616     
23617     
23618 });
23619 /*
23620  * - LGPL
23621  *
23622  * RadioItem
23623  * 
23624  */
23625
23626 /**
23627  * @class Roo.bootstrap.Radio
23628  * @extends Roo.bootstrap.Component
23629  * Bootstrap Radio class
23630  * @cfg {String} boxLabel - the label associated
23631  * @cfg {String} value - the value of radio
23632  * 
23633  * @constructor
23634  * Create a new Radio
23635  * @param {Object} config The config object
23636  */
23637 Roo.bootstrap.Radio = function(config){
23638     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23639     
23640 };
23641
23642 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23643     
23644     boxLabel : '',
23645     
23646     value : '',
23647     
23648     getAutoCreate : function()
23649     {
23650         var cfg = {
23651             tag : 'div',
23652             cls : 'form-group radio',
23653             cn : [
23654                 {
23655                     tag : 'label',
23656                     cls : 'box-label',
23657                     html : this.boxLabel
23658                 }
23659             ]
23660         };
23661         
23662         return cfg;
23663     },
23664     
23665     initEvents : function() 
23666     {
23667         this.parent().register(this);
23668         
23669         this.el.on('click', this.onClick, this);
23670         
23671     },
23672     
23673     onClick : function(e)
23674     {
23675         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23676             this.setChecked(true);
23677         }
23678     },
23679     
23680     setChecked : function(state, suppressEvent)
23681     {
23682         this.parent().setValue(this.value, suppressEvent);
23683         
23684     },
23685     
23686     setBoxLabel : function(v)
23687     {
23688         this.boxLabel = v;
23689         
23690         if(this.rendered){
23691             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23692         }
23693     }
23694     
23695 });
23696  
23697
23698  /*
23699  * - LGPL
23700  *
23701  * Input
23702  * 
23703  */
23704
23705 /**
23706  * @class Roo.bootstrap.SecurePass
23707  * @extends Roo.bootstrap.Input
23708  * Bootstrap SecurePass class
23709  *
23710  * 
23711  * @constructor
23712  * Create a new SecurePass
23713  * @param {Object} config The config object
23714  */
23715  
23716 Roo.bootstrap.SecurePass = function (config) {
23717     // these go here, so the translation tool can replace them..
23718     this.errors = {
23719         PwdEmpty: "Please type a password, and then retype it to confirm.",
23720         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23721         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23722         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23723         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23724         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23725         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23726         TooWeak: "Your password is Too Weak."
23727     },
23728     this.meterLabel = "Password strength:";
23729     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23730     this.meterClass = [
23731         "roo-password-meter-tooweak", 
23732         "roo-password-meter-weak", 
23733         "roo-password-meter-medium", 
23734         "roo-password-meter-strong", 
23735         "roo-password-meter-grey"
23736     ];
23737     
23738     this.errors = {};
23739     
23740     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23741 }
23742
23743 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23744     /**
23745      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23746      * {
23747      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23748      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23749      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23750      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23751      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23752      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23753      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23754      * })
23755      */
23756     // private
23757     
23758     meterWidth: 300,
23759     errorMsg :'',    
23760     errors: false,
23761     imageRoot: '/',
23762     /**
23763      * @cfg {String/Object} Label for the strength meter (defaults to
23764      * 'Password strength:')
23765      */
23766     // private
23767     meterLabel: '',
23768     /**
23769      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23770      * ['Weak', 'Medium', 'Strong'])
23771      */
23772     // private    
23773     pwdStrengths: false,    
23774     // private
23775     strength: 0,
23776     // private
23777     _lastPwd: null,
23778     // private
23779     kCapitalLetter: 0,
23780     kSmallLetter: 1,
23781     kDigit: 2,
23782     kPunctuation: 3,
23783     
23784     insecure: false,
23785     // private
23786     initEvents: function ()
23787     {
23788         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23789
23790         if (this.el.is('input[type=password]') && Roo.isSafari) {
23791             this.el.on('keydown', this.SafariOnKeyDown, this);
23792         }
23793
23794         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23795     },
23796     // private
23797     onRender: function (ct, position)
23798     {
23799         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23800         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23801         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23802
23803         this.trigger.createChild({
23804                    cn: [
23805                     {
23806                     //id: 'PwdMeter',
23807                     tag: 'div',
23808                     cls: 'roo-password-meter-grey col-xs-12',
23809                     style: {
23810                         //width: 0,
23811                         //width: this.meterWidth + 'px'                                                
23812                         }
23813                     },
23814                     {                            
23815                          cls: 'roo-password-meter-text'                          
23816                     }
23817                 ]            
23818         });
23819
23820          
23821         if (this.hideTrigger) {
23822             this.trigger.setDisplayed(false);
23823         }
23824         this.setSize(this.width || '', this.height || '');
23825     },
23826     // private
23827     onDestroy: function ()
23828     {
23829         if (this.trigger) {
23830             this.trigger.removeAllListeners();
23831             this.trigger.remove();
23832         }
23833         if (this.wrap) {
23834             this.wrap.remove();
23835         }
23836         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23837     },
23838     // private
23839     checkStrength: function ()
23840     {
23841         var pwd = this.inputEl().getValue();
23842         if (pwd == this._lastPwd) {
23843             return;
23844         }
23845
23846         var strength;
23847         if (this.ClientSideStrongPassword(pwd)) {
23848             strength = 3;
23849         } else if (this.ClientSideMediumPassword(pwd)) {
23850             strength = 2;
23851         } else if (this.ClientSideWeakPassword(pwd)) {
23852             strength = 1;
23853         } else {
23854             strength = 0;
23855         }
23856         
23857         Roo.log('strength1: ' + strength);
23858         
23859         //var pm = this.trigger.child('div/div/div').dom;
23860         var pm = this.trigger.child('div/div');
23861         pm.removeClass(this.meterClass);
23862         pm.addClass(this.meterClass[strength]);
23863                 
23864         
23865         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23866                 
23867         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23868         
23869         this._lastPwd = pwd;
23870     },
23871     reset: function ()
23872     {
23873         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23874         
23875         this._lastPwd = '';
23876         
23877         var pm = this.trigger.child('div/div');
23878         pm.removeClass(this.meterClass);
23879         pm.addClass('roo-password-meter-grey');        
23880         
23881         
23882         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23883         
23884         pt.innerHTML = '';
23885         this.inputEl().dom.type='password';
23886     },
23887     // private
23888     validateValue: function (value)
23889     {
23890         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23891             return false;
23892         }
23893         if (value.length == 0) {
23894             if (this.allowBlank) {
23895                 this.clearInvalid();
23896                 return true;
23897             }
23898
23899             this.markInvalid(this.errors.PwdEmpty);
23900             this.errorMsg = this.errors.PwdEmpty;
23901             return false;
23902         }
23903         
23904         if(this.insecure){
23905             return true;
23906         }
23907         
23908         if (!value.match(/[\x21-\x7e]+/)) {
23909             this.markInvalid(this.errors.PwdBadChar);
23910             this.errorMsg = this.errors.PwdBadChar;
23911             return false;
23912         }
23913         if (value.length < 6) {
23914             this.markInvalid(this.errors.PwdShort);
23915             this.errorMsg = this.errors.PwdShort;
23916             return false;
23917         }
23918         if (value.length > 16) {
23919             this.markInvalid(this.errors.PwdLong);
23920             this.errorMsg = this.errors.PwdLong;
23921             return false;
23922         }
23923         var strength;
23924         if (this.ClientSideStrongPassword(value)) {
23925             strength = 3;
23926         } else if (this.ClientSideMediumPassword(value)) {
23927             strength = 2;
23928         } else if (this.ClientSideWeakPassword(value)) {
23929             strength = 1;
23930         } else {
23931             strength = 0;
23932         }
23933
23934         
23935         if (strength < 2) {
23936             //this.markInvalid(this.errors.TooWeak);
23937             this.errorMsg = this.errors.TooWeak;
23938             //return false;
23939         }
23940         
23941         
23942         console.log('strength2: ' + strength);
23943         
23944         //var pm = this.trigger.child('div/div/div').dom;
23945         
23946         var pm = this.trigger.child('div/div');
23947         pm.removeClass(this.meterClass);
23948         pm.addClass(this.meterClass[strength]);
23949                 
23950         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23951                 
23952         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23953         
23954         this.errorMsg = ''; 
23955         return true;
23956     },
23957     // private
23958     CharacterSetChecks: function (type)
23959     {
23960         this.type = type;
23961         this.fResult = false;
23962     },
23963     // private
23964     isctype: function (character, type)
23965     {
23966         switch (type) {  
23967             case this.kCapitalLetter:
23968                 if (character >= 'A' && character <= 'Z') {
23969                     return true;
23970                 }
23971                 break;
23972             
23973             case this.kSmallLetter:
23974                 if (character >= 'a' && character <= 'z') {
23975                     return true;
23976                 }
23977                 break;
23978             
23979             case this.kDigit:
23980                 if (character >= '0' && character <= '9') {
23981                     return true;
23982                 }
23983                 break;
23984             
23985             case this.kPunctuation:
23986                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23987                     return true;
23988                 }
23989                 break;
23990             
23991             default:
23992                 return false;
23993         }
23994
23995     },
23996     // private
23997     IsLongEnough: function (pwd, size)
23998     {
23999         return !(pwd == null || isNaN(size) || pwd.length < size);
24000     },
24001     // private
24002     SpansEnoughCharacterSets: function (word, nb)
24003     {
24004         if (!this.IsLongEnough(word, nb))
24005         {
24006             return false;
24007         }
24008
24009         var characterSetChecks = new Array(
24010             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24011             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24012         );
24013         
24014         for (var index = 0; index < word.length; ++index) {
24015             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24016                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24017                     characterSetChecks[nCharSet].fResult = true;
24018                     break;
24019                 }
24020             }
24021         }
24022
24023         var nCharSets = 0;
24024         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24025             if (characterSetChecks[nCharSet].fResult) {
24026                 ++nCharSets;
24027             }
24028         }
24029
24030         if (nCharSets < nb) {
24031             return false;
24032         }
24033         return true;
24034     },
24035     // private
24036     ClientSideStrongPassword: function (pwd)
24037     {
24038         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24039     },
24040     // private
24041     ClientSideMediumPassword: function (pwd)
24042     {
24043         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24044     },
24045     // private
24046     ClientSideWeakPassword: function (pwd)
24047     {
24048         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24049     }
24050           
24051 })//<script type="text/javascript">
24052
24053 /*
24054  * Based  Ext JS Library 1.1.1
24055  * Copyright(c) 2006-2007, Ext JS, LLC.
24056  * LGPL
24057  *
24058  */
24059  
24060 /**
24061  * @class Roo.HtmlEditorCore
24062  * @extends Roo.Component
24063  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24064  *
24065  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24066  */
24067
24068 Roo.HtmlEditorCore = function(config){
24069     
24070     
24071     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24072     
24073     
24074     this.addEvents({
24075         /**
24076          * @event initialize
24077          * Fires when the editor is fully initialized (including the iframe)
24078          * @param {Roo.HtmlEditorCore} this
24079          */
24080         initialize: true,
24081         /**
24082          * @event activate
24083          * Fires when the editor is first receives the focus. Any insertion must wait
24084          * until after this event.
24085          * @param {Roo.HtmlEditorCore} this
24086          */
24087         activate: true,
24088          /**
24089          * @event beforesync
24090          * Fires before the textarea is updated with content from the editor iframe. Return false
24091          * to cancel the sync.
24092          * @param {Roo.HtmlEditorCore} this
24093          * @param {String} html
24094          */
24095         beforesync: true,
24096          /**
24097          * @event beforepush
24098          * Fires before the iframe editor is updated with content from the textarea. Return false
24099          * to cancel the push.
24100          * @param {Roo.HtmlEditorCore} this
24101          * @param {String} html
24102          */
24103         beforepush: true,
24104          /**
24105          * @event sync
24106          * Fires when the textarea is updated with content from the editor iframe.
24107          * @param {Roo.HtmlEditorCore} this
24108          * @param {String} html
24109          */
24110         sync: true,
24111          /**
24112          * @event push
24113          * Fires when the iframe editor is updated with content from the textarea.
24114          * @param {Roo.HtmlEditorCore} this
24115          * @param {String} html
24116          */
24117         push: true,
24118         
24119         /**
24120          * @event editorevent
24121          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24122          * @param {Roo.HtmlEditorCore} this
24123          */
24124         editorevent: true
24125         
24126     });
24127     
24128     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24129     
24130     // defaults : white / black...
24131     this.applyBlacklists();
24132     
24133     
24134     
24135 };
24136
24137
24138 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24139
24140
24141      /**
24142      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24143      */
24144     
24145     owner : false,
24146     
24147      /**
24148      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24149      *                        Roo.resizable.
24150      */
24151     resizable : false,
24152      /**
24153      * @cfg {Number} height (in pixels)
24154      */   
24155     height: 300,
24156    /**
24157      * @cfg {Number} width (in pixels)
24158      */   
24159     width: 500,
24160     
24161     /**
24162      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24163      * 
24164      */
24165     stylesheets: false,
24166     
24167     // id of frame..
24168     frameId: false,
24169     
24170     // private properties
24171     validationEvent : false,
24172     deferHeight: true,
24173     initialized : false,
24174     activated : false,
24175     sourceEditMode : false,
24176     onFocus : Roo.emptyFn,
24177     iframePad:3,
24178     hideMode:'offsets',
24179     
24180     clearUp: true,
24181     
24182     // blacklist + whitelisted elements..
24183     black: false,
24184     white: false,
24185      
24186     bodyCls : '',
24187
24188     /**
24189      * Protected method that will not generally be called directly. It
24190      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24191      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24192      */
24193     getDocMarkup : function(){
24194         // body styles..
24195         var st = '';
24196         
24197         // inherit styels from page...?? 
24198         if (this.stylesheets === false) {
24199             
24200             Roo.get(document.head).select('style').each(function(node) {
24201                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24202             });
24203             
24204             Roo.get(document.head).select('link').each(function(node) { 
24205                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24206             });
24207             
24208         } else if (!this.stylesheets.length) {
24209                 // simple..
24210                 st = '<style type="text/css">' +
24211                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24212                    '</style>';
24213         } else {
24214             for (var i in this.stylesheets) { 
24215                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24216             }
24217             
24218         }
24219         
24220         st +=  '<style type="text/css">' +
24221             'IMG { cursor: pointer } ' +
24222         '</style>';
24223
24224         var cls = 'roo-htmleditor-body';
24225         
24226         if(this.bodyCls.length){
24227             cls += ' ' + this.bodyCls;
24228         }
24229         
24230         return '<html><head>' + st  +
24231             //<style type="text/css">' +
24232             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24233             //'</style>' +
24234             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24235     },
24236
24237     // private
24238     onRender : function(ct, position)
24239     {
24240         var _t = this;
24241         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24242         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24243         
24244         
24245         this.el.dom.style.border = '0 none';
24246         this.el.dom.setAttribute('tabIndex', -1);
24247         this.el.addClass('x-hidden hide');
24248         
24249         
24250         
24251         if(Roo.isIE){ // fix IE 1px bogus margin
24252             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24253         }
24254        
24255         
24256         this.frameId = Roo.id();
24257         
24258          
24259         
24260         var iframe = this.owner.wrap.createChild({
24261             tag: 'iframe',
24262             cls: 'form-control', // bootstrap..
24263             id: this.frameId,
24264             name: this.frameId,
24265             frameBorder : 'no',
24266             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24267         }, this.el
24268         );
24269         
24270         
24271         this.iframe = iframe.dom;
24272
24273          this.assignDocWin();
24274         
24275         this.doc.designMode = 'on';
24276        
24277         this.doc.open();
24278         this.doc.write(this.getDocMarkup());
24279         this.doc.close();
24280
24281         
24282         var task = { // must defer to wait for browser to be ready
24283             run : function(){
24284                 //console.log("run task?" + this.doc.readyState);
24285                 this.assignDocWin();
24286                 if(this.doc.body || this.doc.readyState == 'complete'){
24287                     try {
24288                         this.doc.designMode="on";
24289                     } catch (e) {
24290                         return;
24291                     }
24292                     Roo.TaskMgr.stop(task);
24293                     this.initEditor.defer(10, this);
24294                 }
24295             },
24296             interval : 10,
24297             duration: 10000,
24298             scope: this
24299         };
24300         Roo.TaskMgr.start(task);
24301
24302     },
24303
24304     // private
24305     onResize : function(w, h)
24306     {
24307          Roo.log('resize: ' +w + ',' + h );
24308         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24309         if(!this.iframe){
24310             return;
24311         }
24312         if(typeof w == 'number'){
24313             
24314             this.iframe.style.width = w + 'px';
24315         }
24316         if(typeof h == 'number'){
24317             
24318             this.iframe.style.height = h + 'px';
24319             if(this.doc){
24320                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24321             }
24322         }
24323         
24324     },
24325
24326     /**
24327      * Toggles the editor between standard and source edit mode.
24328      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24329      */
24330     toggleSourceEdit : function(sourceEditMode){
24331         
24332         this.sourceEditMode = sourceEditMode === true;
24333         
24334         if(this.sourceEditMode){
24335  
24336             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24337             
24338         }else{
24339             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24340             //this.iframe.className = '';
24341             this.deferFocus();
24342         }
24343         //this.setSize(this.owner.wrap.getSize());
24344         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24345     },
24346
24347     
24348   
24349
24350     /**
24351      * Protected method that will not generally be called directly. If you need/want
24352      * custom HTML cleanup, this is the method you should override.
24353      * @param {String} html The HTML to be cleaned
24354      * return {String} The cleaned HTML
24355      */
24356     cleanHtml : function(html){
24357         html = String(html);
24358         if(html.length > 5){
24359             if(Roo.isSafari){ // strip safari nonsense
24360                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24361             }
24362         }
24363         if(html == '&nbsp;'){
24364             html = '';
24365         }
24366         return html;
24367     },
24368
24369     /**
24370      * HTML Editor -> Textarea
24371      * Protected method that will not generally be called directly. Syncs the contents
24372      * of the editor iframe with the textarea.
24373      */
24374     syncValue : function(){
24375         if(this.initialized){
24376             var bd = (this.doc.body || this.doc.documentElement);
24377             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24378             var html = bd.innerHTML;
24379             if(Roo.isSafari){
24380                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24381                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24382                 if(m && m[1]){
24383                     html = '<div style="'+m[0]+'">' + html + '</div>';
24384                 }
24385             }
24386             html = this.cleanHtml(html);
24387             // fix up the special chars.. normaly like back quotes in word...
24388             // however we do not want to do this with chinese..
24389             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24390                 
24391                 var cc = match.charCodeAt();
24392
24393                 // Get the character value, handling surrogate pairs
24394                 if (match.length == 2) {
24395                     // It's a surrogate pair, calculate the Unicode code point
24396                     var high = match.charCodeAt(0) - 0xD800;
24397                     var low  = match.charCodeAt(1) - 0xDC00;
24398                     cc = (high * 0x400) + low + 0x10000;
24399                 }  else if (
24400                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24401                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24402                     (cc >= 0xf900 && cc < 0xfb00 )
24403                 ) {
24404                         return match;
24405                 }  
24406          
24407                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24408                 return "&#" + cc + ";";
24409                 
24410                 
24411             });
24412             
24413             
24414              
24415             if(this.owner.fireEvent('beforesync', this, html) !== false){
24416                 this.el.dom.value = html;
24417                 this.owner.fireEvent('sync', this, html);
24418             }
24419         }
24420     },
24421
24422     /**
24423      * Protected method that will not generally be called directly. Pushes the value of the textarea
24424      * into the iframe editor.
24425      */
24426     pushValue : function(){
24427         if(this.initialized){
24428             var v = this.el.dom.value.trim();
24429             
24430 //            if(v.length < 1){
24431 //                v = '&#160;';
24432 //            }
24433             
24434             if(this.owner.fireEvent('beforepush', this, v) !== false){
24435                 var d = (this.doc.body || this.doc.documentElement);
24436                 d.innerHTML = v;
24437                 this.cleanUpPaste();
24438                 this.el.dom.value = d.innerHTML;
24439                 this.owner.fireEvent('push', this, v);
24440             }
24441         }
24442     },
24443
24444     // private
24445     deferFocus : function(){
24446         this.focus.defer(10, this);
24447     },
24448
24449     // doc'ed in Field
24450     focus : function(){
24451         if(this.win && !this.sourceEditMode){
24452             this.win.focus();
24453         }else{
24454             this.el.focus();
24455         }
24456     },
24457     
24458     assignDocWin: function()
24459     {
24460         var iframe = this.iframe;
24461         
24462          if(Roo.isIE){
24463             this.doc = iframe.contentWindow.document;
24464             this.win = iframe.contentWindow;
24465         } else {
24466 //            if (!Roo.get(this.frameId)) {
24467 //                return;
24468 //            }
24469 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24470 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24471             
24472             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24473                 return;
24474             }
24475             
24476             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24477             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24478         }
24479     },
24480     
24481     // private
24482     initEditor : function(){
24483         //console.log("INIT EDITOR");
24484         this.assignDocWin();
24485         
24486         
24487         
24488         this.doc.designMode="on";
24489         this.doc.open();
24490         this.doc.write(this.getDocMarkup());
24491         this.doc.close();
24492         
24493         var dbody = (this.doc.body || this.doc.documentElement);
24494         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24495         // this copies styles from the containing element into thsi one..
24496         // not sure why we need all of this..
24497         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24498         
24499         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24500         //ss['background-attachment'] = 'fixed'; // w3c
24501         dbody.bgProperties = 'fixed'; // ie
24502         //Roo.DomHelper.applyStyles(dbody, ss);
24503         Roo.EventManager.on(this.doc, {
24504             //'mousedown': this.onEditorEvent,
24505             'mouseup': this.onEditorEvent,
24506             'dblclick': this.onEditorEvent,
24507             'click': this.onEditorEvent,
24508             'keyup': this.onEditorEvent,
24509             buffer:100,
24510             scope: this
24511         });
24512         if(Roo.isGecko){
24513             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24514         }
24515         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24516             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24517         }
24518         this.initialized = true;
24519
24520         this.owner.fireEvent('initialize', this);
24521         this.pushValue();
24522     },
24523
24524     // private
24525     onDestroy : function(){
24526         
24527         
24528         
24529         if(this.rendered){
24530             
24531             //for (var i =0; i < this.toolbars.length;i++) {
24532             //    // fixme - ask toolbars for heights?
24533             //    this.toolbars[i].onDestroy();
24534            // }
24535             
24536             //this.wrap.dom.innerHTML = '';
24537             //this.wrap.remove();
24538         }
24539     },
24540
24541     // private
24542     onFirstFocus : function(){
24543         
24544         this.assignDocWin();
24545         
24546         
24547         this.activated = true;
24548          
24549     
24550         if(Roo.isGecko){ // prevent silly gecko errors
24551             this.win.focus();
24552             var s = this.win.getSelection();
24553             if(!s.focusNode || s.focusNode.nodeType != 3){
24554                 var r = s.getRangeAt(0);
24555                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24556                 r.collapse(true);
24557                 this.deferFocus();
24558             }
24559             try{
24560                 this.execCmd('useCSS', true);
24561                 this.execCmd('styleWithCSS', false);
24562             }catch(e){}
24563         }
24564         this.owner.fireEvent('activate', this);
24565     },
24566
24567     // private
24568     adjustFont: function(btn){
24569         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24570         //if(Roo.isSafari){ // safari
24571         //    adjust *= 2;
24572        // }
24573         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24574         if(Roo.isSafari){ // safari
24575             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24576             v =  (v < 10) ? 10 : v;
24577             v =  (v > 48) ? 48 : v;
24578             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24579             
24580         }
24581         
24582         
24583         v = Math.max(1, v+adjust);
24584         
24585         this.execCmd('FontSize', v  );
24586     },
24587
24588     onEditorEvent : function(e)
24589     {
24590         this.owner.fireEvent('editorevent', this, e);
24591       //  this.updateToolbar();
24592         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24593     },
24594
24595     insertTag : function(tg)
24596     {
24597         // could be a bit smarter... -> wrap the current selected tRoo..
24598         if (tg.toLowerCase() == 'span' ||
24599             tg.toLowerCase() == 'code' ||
24600             tg.toLowerCase() == 'sup' ||
24601             tg.toLowerCase() == 'sub' 
24602             ) {
24603             
24604             range = this.createRange(this.getSelection());
24605             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24606             wrappingNode.appendChild(range.extractContents());
24607             range.insertNode(wrappingNode);
24608
24609             return;
24610             
24611             
24612             
24613         }
24614         this.execCmd("formatblock",   tg);
24615         
24616     },
24617     
24618     insertText : function(txt)
24619     {
24620         
24621         
24622         var range = this.createRange();
24623         range.deleteContents();
24624                //alert(Sender.getAttribute('label'));
24625                
24626         range.insertNode(this.doc.createTextNode(txt));
24627     } ,
24628     
24629      
24630
24631     /**
24632      * Executes a Midas editor command on the editor document and performs necessary focus and
24633      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24634      * @param {String} cmd The Midas command
24635      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24636      */
24637     relayCmd : function(cmd, value){
24638         this.win.focus();
24639         this.execCmd(cmd, value);
24640         this.owner.fireEvent('editorevent', this);
24641         //this.updateToolbar();
24642         this.owner.deferFocus();
24643     },
24644
24645     /**
24646      * Executes a Midas editor command directly on the editor document.
24647      * For visual commands, you should use {@link #relayCmd} instead.
24648      * <b>This should only be called after the editor is initialized.</b>
24649      * @param {String} cmd The Midas command
24650      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24651      */
24652     execCmd : function(cmd, value){
24653         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24654         this.syncValue();
24655     },
24656  
24657  
24658    
24659     /**
24660      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24661      * to insert tRoo.
24662      * @param {String} text | dom node.. 
24663      */
24664     insertAtCursor : function(text)
24665     {
24666         
24667         if(!this.activated){
24668             return;
24669         }
24670         /*
24671         if(Roo.isIE){
24672             this.win.focus();
24673             var r = this.doc.selection.createRange();
24674             if(r){
24675                 r.collapse(true);
24676                 r.pasteHTML(text);
24677                 this.syncValue();
24678                 this.deferFocus();
24679             
24680             }
24681             return;
24682         }
24683         */
24684         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24685             this.win.focus();
24686             
24687             
24688             // from jquery ui (MIT licenced)
24689             var range, node;
24690             var win = this.win;
24691             
24692             if (win.getSelection && win.getSelection().getRangeAt) {
24693                 range = win.getSelection().getRangeAt(0);
24694                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24695                 range.insertNode(node);
24696             } else if (win.document.selection && win.document.selection.createRange) {
24697                 // no firefox support
24698                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24699                 win.document.selection.createRange().pasteHTML(txt);
24700             } else {
24701                 // no firefox support
24702                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24703                 this.execCmd('InsertHTML', txt);
24704             } 
24705             
24706             this.syncValue();
24707             
24708             this.deferFocus();
24709         }
24710     },
24711  // private
24712     mozKeyPress : function(e){
24713         if(e.ctrlKey){
24714             var c = e.getCharCode(), cmd;
24715           
24716             if(c > 0){
24717                 c = String.fromCharCode(c).toLowerCase();
24718                 switch(c){
24719                     case 'b':
24720                         cmd = 'bold';
24721                         break;
24722                     case 'i':
24723                         cmd = 'italic';
24724                         break;
24725                     
24726                     case 'u':
24727                         cmd = 'underline';
24728                         break;
24729                     
24730                     case 'v':
24731                         this.cleanUpPaste.defer(100, this);
24732                         return;
24733                         
24734                 }
24735                 if(cmd){
24736                     this.win.focus();
24737                     this.execCmd(cmd);
24738                     this.deferFocus();
24739                     e.preventDefault();
24740                 }
24741                 
24742             }
24743         }
24744     },
24745
24746     // private
24747     fixKeys : function(){ // load time branching for fastest keydown performance
24748         if(Roo.isIE){
24749             return function(e){
24750                 var k = e.getKey(), r;
24751                 if(k == e.TAB){
24752                     e.stopEvent();
24753                     r = this.doc.selection.createRange();
24754                     if(r){
24755                         r.collapse(true);
24756                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24757                         this.deferFocus();
24758                     }
24759                     return;
24760                 }
24761                 
24762                 if(k == e.ENTER){
24763                     r = this.doc.selection.createRange();
24764                     if(r){
24765                         var target = r.parentElement();
24766                         if(!target || target.tagName.toLowerCase() != 'li'){
24767                             e.stopEvent();
24768                             r.pasteHTML('<br />');
24769                             r.collapse(false);
24770                             r.select();
24771                         }
24772                     }
24773                 }
24774                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24775                     this.cleanUpPaste.defer(100, this);
24776                     return;
24777                 }
24778                 
24779                 
24780             };
24781         }else if(Roo.isOpera){
24782             return function(e){
24783                 var k = e.getKey();
24784                 if(k == e.TAB){
24785                     e.stopEvent();
24786                     this.win.focus();
24787                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24788                     this.deferFocus();
24789                 }
24790                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24791                     this.cleanUpPaste.defer(100, this);
24792                     return;
24793                 }
24794                 
24795             };
24796         }else if(Roo.isSafari){
24797             return function(e){
24798                 var k = e.getKey();
24799                 
24800                 if(k == e.TAB){
24801                     e.stopEvent();
24802                     this.execCmd('InsertText','\t');
24803                     this.deferFocus();
24804                     return;
24805                 }
24806                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24807                     this.cleanUpPaste.defer(100, this);
24808                     return;
24809                 }
24810                 
24811              };
24812         }
24813     }(),
24814     
24815     getAllAncestors: function()
24816     {
24817         var p = this.getSelectedNode();
24818         var a = [];
24819         if (!p) {
24820             a.push(p); // push blank onto stack..
24821             p = this.getParentElement();
24822         }
24823         
24824         
24825         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24826             a.push(p);
24827             p = p.parentNode;
24828         }
24829         a.push(this.doc.body);
24830         return a;
24831     },
24832     lastSel : false,
24833     lastSelNode : false,
24834     
24835     
24836     getSelection : function() 
24837     {
24838         this.assignDocWin();
24839         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24840     },
24841     
24842     getSelectedNode: function() 
24843     {
24844         // this may only work on Gecko!!!
24845         
24846         // should we cache this!!!!
24847         
24848         
24849         
24850          
24851         var range = this.createRange(this.getSelection()).cloneRange();
24852         
24853         if (Roo.isIE) {
24854             var parent = range.parentElement();
24855             while (true) {
24856                 var testRange = range.duplicate();
24857                 testRange.moveToElementText(parent);
24858                 if (testRange.inRange(range)) {
24859                     break;
24860                 }
24861                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24862                     break;
24863                 }
24864                 parent = parent.parentElement;
24865             }
24866             return parent;
24867         }
24868         
24869         // is ancestor a text element.
24870         var ac =  range.commonAncestorContainer;
24871         if (ac.nodeType == 3) {
24872             ac = ac.parentNode;
24873         }
24874         
24875         var ar = ac.childNodes;
24876          
24877         var nodes = [];
24878         var other_nodes = [];
24879         var has_other_nodes = false;
24880         for (var i=0;i<ar.length;i++) {
24881             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24882                 continue;
24883             }
24884             // fullly contained node.
24885             
24886             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24887                 nodes.push(ar[i]);
24888                 continue;
24889             }
24890             
24891             // probably selected..
24892             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24893                 other_nodes.push(ar[i]);
24894                 continue;
24895             }
24896             // outer..
24897             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24898                 continue;
24899             }
24900             
24901             
24902             has_other_nodes = true;
24903         }
24904         if (!nodes.length && other_nodes.length) {
24905             nodes= other_nodes;
24906         }
24907         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24908             return false;
24909         }
24910         
24911         return nodes[0];
24912     },
24913     createRange: function(sel)
24914     {
24915         // this has strange effects when using with 
24916         // top toolbar - not sure if it's a great idea.
24917         //this.editor.contentWindow.focus();
24918         if (typeof sel != "undefined") {
24919             try {
24920                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24921             } catch(e) {
24922                 return this.doc.createRange();
24923             }
24924         } else {
24925             return this.doc.createRange();
24926         }
24927     },
24928     getParentElement: function()
24929     {
24930         
24931         this.assignDocWin();
24932         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24933         
24934         var range = this.createRange(sel);
24935          
24936         try {
24937             var p = range.commonAncestorContainer;
24938             while (p.nodeType == 3) { // text node
24939                 p = p.parentNode;
24940             }
24941             return p;
24942         } catch (e) {
24943             return null;
24944         }
24945     
24946     },
24947     /***
24948      *
24949      * Range intersection.. the hard stuff...
24950      *  '-1' = before
24951      *  '0' = hits..
24952      *  '1' = after.
24953      *         [ -- selected range --- ]
24954      *   [fail]                        [fail]
24955      *
24956      *    basically..
24957      *      if end is before start or  hits it. fail.
24958      *      if start is after end or hits it fail.
24959      *
24960      *   if either hits (but other is outside. - then it's not 
24961      *   
24962      *    
24963      **/
24964     
24965     
24966     // @see http://www.thismuchiknow.co.uk/?p=64.
24967     rangeIntersectsNode : function(range, node)
24968     {
24969         var nodeRange = node.ownerDocument.createRange();
24970         try {
24971             nodeRange.selectNode(node);
24972         } catch (e) {
24973             nodeRange.selectNodeContents(node);
24974         }
24975     
24976         var rangeStartRange = range.cloneRange();
24977         rangeStartRange.collapse(true);
24978     
24979         var rangeEndRange = range.cloneRange();
24980         rangeEndRange.collapse(false);
24981     
24982         var nodeStartRange = nodeRange.cloneRange();
24983         nodeStartRange.collapse(true);
24984     
24985         var nodeEndRange = nodeRange.cloneRange();
24986         nodeEndRange.collapse(false);
24987     
24988         return rangeStartRange.compareBoundaryPoints(
24989                  Range.START_TO_START, nodeEndRange) == -1 &&
24990                rangeEndRange.compareBoundaryPoints(
24991                  Range.START_TO_START, nodeStartRange) == 1;
24992         
24993          
24994     },
24995     rangeCompareNode : function(range, node)
24996     {
24997         var nodeRange = node.ownerDocument.createRange();
24998         try {
24999             nodeRange.selectNode(node);
25000         } catch (e) {
25001             nodeRange.selectNodeContents(node);
25002         }
25003         
25004         
25005         range.collapse(true);
25006     
25007         nodeRange.collapse(true);
25008      
25009         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25010         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25011          
25012         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25013         
25014         var nodeIsBefore   =  ss == 1;
25015         var nodeIsAfter    = ee == -1;
25016         
25017         if (nodeIsBefore && nodeIsAfter) {
25018             return 0; // outer
25019         }
25020         if (!nodeIsBefore && nodeIsAfter) {
25021             return 1; //right trailed.
25022         }
25023         
25024         if (nodeIsBefore && !nodeIsAfter) {
25025             return 2;  // left trailed.
25026         }
25027         // fully contined.
25028         return 3;
25029     },
25030
25031     // private? - in a new class?
25032     cleanUpPaste :  function()
25033     {
25034         // cleans up the whole document..
25035         Roo.log('cleanuppaste');
25036         
25037         this.cleanUpChildren(this.doc.body);
25038         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25039         if (clean != this.doc.body.innerHTML) {
25040             this.doc.body.innerHTML = clean;
25041         }
25042         
25043     },
25044     
25045     cleanWordChars : function(input) {// change the chars to hex code
25046         var he = Roo.HtmlEditorCore;
25047         
25048         var output = input;
25049         Roo.each(he.swapCodes, function(sw) { 
25050             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25051             
25052             output = output.replace(swapper, sw[1]);
25053         });
25054         
25055         return output;
25056     },
25057     
25058     
25059     cleanUpChildren : function (n)
25060     {
25061         if (!n.childNodes.length) {
25062             return;
25063         }
25064         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25065            this.cleanUpChild(n.childNodes[i]);
25066         }
25067     },
25068     
25069     
25070         
25071     
25072     cleanUpChild : function (node)
25073     {
25074         var ed = this;
25075         //console.log(node);
25076         if (node.nodeName == "#text") {
25077             // clean up silly Windows -- stuff?
25078             return; 
25079         }
25080         if (node.nodeName == "#comment") {
25081             node.parentNode.removeChild(node);
25082             // clean up silly Windows -- stuff?
25083             return; 
25084         }
25085         var lcname = node.tagName.toLowerCase();
25086         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25087         // whitelist of tags..
25088         
25089         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25090             // remove node.
25091             node.parentNode.removeChild(node);
25092             return;
25093             
25094         }
25095         
25096         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25097         
25098         // spans with no attributes - just remove them..
25099         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25100             remove_keep_children = true;
25101         }
25102         
25103         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25104         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25105         
25106         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25107         //    remove_keep_children = true;
25108         //}
25109         
25110         if (remove_keep_children) {
25111             this.cleanUpChildren(node);
25112             // inserts everything just before this node...
25113             while (node.childNodes.length) {
25114                 var cn = node.childNodes[0];
25115                 node.removeChild(cn);
25116                 node.parentNode.insertBefore(cn, node);
25117             }
25118             node.parentNode.removeChild(node);
25119             return;
25120         }
25121         
25122         if (!node.attributes || !node.attributes.length) {
25123             
25124           
25125             
25126             
25127             this.cleanUpChildren(node);
25128             return;
25129         }
25130         
25131         function cleanAttr(n,v)
25132         {
25133             
25134             if (v.match(/^\./) || v.match(/^\//)) {
25135                 return;
25136             }
25137             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25138                 return;
25139             }
25140             if (v.match(/^#/)) {
25141                 return;
25142             }
25143             if (v.match(/^\{/)) { // allow template editing.
25144                 return;
25145             }
25146 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25147             node.removeAttribute(n);
25148             
25149         }
25150         
25151         var cwhite = this.cwhite;
25152         var cblack = this.cblack;
25153             
25154         function cleanStyle(n,v)
25155         {
25156             if (v.match(/expression/)) { //XSS?? should we even bother..
25157                 node.removeAttribute(n);
25158                 return;
25159             }
25160             
25161             var parts = v.split(/;/);
25162             var clean = [];
25163             
25164             Roo.each(parts, function(p) {
25165                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25166                 if (!p.length) {
25167                     return true;
25168                 }
25169                 var l = p.split(':').shift().replace(/\s+/g,'');
25170                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25171                 
25172                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25173 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25174                     //node.removeAttribute(n);
25175                     return true;
25176                 }
25177                 //Roo.log()
25178                 // only allow 'c whitelisted system attributes'
25179                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25181                     //node.removeAttribute(n);
25182                     return true;
25183                 }
25184                 
25185                 
25186                  
25187                 
25188                 clean.push(p);
25189                 return true;
25190             });
25191             if (clean.length) { 
25192                 node.setAttribute(n, clean.join(';'));
25193             } else {
25194                 node.removeAttribute(n);
25195             }
25196             
25197         }
25198         
25199         
25200         for (var i = node.attributes.length-1; i > -1 ; i--) {
25201             var a = node.attributes[i];
25202             //console.log(a);
25203             
25204             if (a.name.toLowerCase().substr(0,2)=='on')  {
25205                 node.removeAttribute(a.name);
25206                 continue;
25207             }
25208             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25209                 node.removeAttribute(a.name);
25210                 continue;
25211             }
25212             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25213                 cleanAttr(a.name,a.value); // fixme..
25214                 continue;
25215             }
25216             if (a.name == 'style') {
25217                 cleanStyle(a.name,a.value);
25218                 continue;
25219             }
25220             /// clean up MS crap..
25221             // tecnically this should be a list of valid class'es..
25222             
25223             
25224             if (a.name == 'class') {
25225                 if (a.value.match(/^Mso/)) {
25226                     node.removeAttribute('class');
25227                 }
25228                 
25229                 if (a.value.match(/^body$/)) {
25230                     node.removeAttribute('class');
25231                 }
25232                 continue;
25233             }
25234             
25235             // style cleanup!?
25236             // class cleanup?
25237             
25238         }
25239         
25240         
25241         this.cleanUpChildren(node);
25242         
25243         
25244     },
25245     
25246     /**
25247      * Clean up MS wordisms...
25248      */
25249     cleanWord : function(node)
25250     {
25251         if (!node) {
25252             this.cleanWord(this.doc.body);
25253             return;
25254         }
25255         
25256         if(
25257                 node.nodeName == 'SPAN' &&
25258                 !node.hasAttributes() &&
25259                 node.childNodes.length == 1 &&
25260                 node.firstChild.nodeName == "#text"  
25261         ) {
25262             var textNode = node.firstChild;
25263             node.removeChild(textNode);
25264             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25265                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25266             }
25267             node.parentNode.insertBefore(textNode, node);
25268             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25269                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25270             }
25271             node.parentNode.removeChild(node);
25272         }
25273         
25274         if (node.nodeName == "#text") {
25275             // clean up silly Windows -- stuff?
25276             return; 
25277         }
25278         if (node.nodeName == "#comment") {
25279             node.parentNode.removeChild(node);
25280             // clean up silly Windows -- stuff?
25281             return; 
25282         }
25283         
25284         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25285             node.parentNode.removeChild(node);
25286             return;
25287         }
25288         //Roo.log(node.tagName);
25289         // remove - but keep children..
25290         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25291             //Roo.log('-- removed');
25292             while (node.childNodes.length) {
25293                 var cn = node.childNodes[0];
25294                 node.removeChild(cn);
25295                 node.parentNode.insertBefore(cn, node);
25296                 // move node to parent - and clean it..
25297                 this.cleanWord(cn);
25298             }
25299             node.parentNode.removeChild(node);
25300             /// no need to iterate chidlren = it's got none..
25301             //this.iterateChildren(node, this.cleanWord);
25302             return;
25303         }
25304         // clean styles
25305         if (node.className.length) {
25306             
25307             var cn = node.className.split(/\W+/);
25308             var cna = [];
25309             Roo.each(cn, function(cls) {
25310                 if (cls.match(/Mso[a-zA-Z]+/)) {
25311                     return;
25312                 }
25313                 cna.push(cls);
25314             });
25315             node.className = cna.length ? cna.join(' ') : '';
25316             if (!cna.length) {
25317                 node.removeAttribute("class");
25318             }
25319         }
25320         
25321         if (node.hasAttribute("lang")) {
25322             node.removeAttribute("lang");
25323         }
25324         
25325         if (node.hasAttribute("style")) {
25326             
25327             var styles = node.getAttribute("style").split(";");
25328             var nstyle = [];
25329             Roo.each(styles, function(s) {
25330                 if (!s.match(/:/)) {
25331                     return;
25332                 }
25333                 var kv = s.split(":");
25334                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25335                     return;
25336                 }
25337                 // what ever is left... we allow.
25338                 nstyle.push(s);
25339             });
25340             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25341             if (!nstyle.length) {
25342                 node.removeAttribute('style');
25343             }
25344         }
25345         this.iterateChildren(node, this.cleanWord);
25346         
25347         
25348         
25349     },
25350     /**
25351      * iterateChildren of a Node, calling fn each time, using this as the scole..
25352      * @param {DomNode} node node to iterate children of.
25353      * @param {Function} fn method of this class to call on each item.
25354      */
25355     iterateChildren : function(node, fn)
25356     {
25357         if (!node.childNodes.length) {
25358                 return;
25359         }
25360         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25361            fn.call(this, node.childNodes[i])
25362         }
25363     },
25364     
25365     
25366     /**
25367      * cleanTableWidths.
25368      *
25369      * Quite often pasting from word etc.. results in tables with column and widths.
25370      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25371      *
25372      */
25373     cleanTableWidths : function(node)
25374     {
25375          
25376          
25377         if (!node) {
25378             this.cleanTableWidths(this.doc.body);
25379             return;
25380         }
25381         
25382         // ignore list...
25383         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25384             return; 
25385         }
25386         Roo.log(node.tagName);
25387         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25388             this.iterateChildren(node, this.cleanTableWidths);
25389             return;
25390         }
25391         if (node.hasAttribute('width')) {
25392             node.removeAttribute('width');
25393         }
25394         
25395          
25396         if (node.hasAttribute("style")) {
25397             // pretty basic...
25398             
25399             var styles = node.getAttribute("style").split(";");
25400             var nstyle = [];
25401             Roo.each(styles, function(s) {
25402                 if (!s.match(/:/)) {
25403                     return;
25404                 }
25405                 var kv = s.split(":");
25406                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25407                     return;
25408                 }
25409                 // what ever is left... we allow.
25410                 nstyle.push(s);
25411             });
25412             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25413             if (!nstyle.length) {
25414                 node.removeAttribute('style');
25415             }
25416         }
25417         
25418         this.iterateChildren(node, this.cleanTableWidths);
25419         
25420         
25421     },
25422     
25423     
25424     
25425     
25426     domToHTML : function(currentElement, depth, nopadtext) {
25427         
25428         depth = depth || 0;
25429         nopadtext = nopadtext || false;
25430     
25431         if (!currentElement) {
25432             return this.domToHTML(this.doc.body);
25433         }
25434         
25435         //Roo.log(currentElement);
25436         var j;
25437         var allText = false;
25438         var nodeName = currentElement.nodeName;
25439         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25440         
25441         if  (nodeName == '#text') {
25442             
25443             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25444         }
25445         
25446         
25447         var ret = '';
25448         if (nodeName != 'BODY') {
25449              
25450             var i = 0;
25451             // Prints the node tagName, such as <A>, <IMG>, etc
25452             if (tagName) {
25453                 var attr = [];
25454                 for(i = 0; i < currentElement.attributes.length;i++) {
25455                     // quoting?
25456                     var aname = currentElement.attributes.item(i).name;
25457                     if (!currentElement.attributes.item(i).value.length) {
25458                         continue;
25459                     }
25460                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25461                 }
25462                 
25463                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25464             } 
25465             else {
25466                 
25467                 // eack
25468             }
25469         } else {
25470             tagName = false;
25471         }
25472         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25473             return ret;
25474         }
25475         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25476             nopadtext = true;
25477         }
25478         
25479         
25480         // Traverse the tree
25481         i = 0;
25482         var currentElementChild = currentElement.childNodes.item(i);
25483         var allText = true;
25484         var innerHTML  = '';
25485         lastnode = '';
25486         while (currentElementChild) {
25487             // Formatting code (indent the tree so it looks nice on the screen)
25488             var nopad = nopadtext;
25489             if (lastnode == 'SPAN') {
25490                 nopad  = true;
25491             }
25492             // text
25493             if  (currentElementChild.nodeName == '#text') {
25494                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25495                 toadd = nopadtext ? toadd : toadd.trim();
25496                 if (!nopad && toadd.length > 80) {
25497                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25498                 }
25499                 innerHTML  += toadd;
25500                 
25501                 i++;
25502                 currentElementChild = currentElement.childNodes.item(i);
25503                 lastNode = '';
25504                 continue;
25505             }
25506             allText = false;
25507             
25508             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25509                 
25510             // Recursively traverse the tree structure of the child node
25511             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25512             lastnode = currentElementChild.nodeName;
25513             i++;
25514             currentElementChild=currentElement.childNodes.item(i);
25515         }
25516         
25517         ret += innerHTML;
25518         
25519         if (!allText) {
25520                 // The remaining code is mostly for formatting the tree
25521             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25522         }
25523         
25524         
25525         if (tagName) {
25526             ret+= "</"+tagName+">";
25527         }
25528         return ret;
25529         
25530     },
25531         
25532     applyBlacklists : function()
25533     {
25534         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25535         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25536         
25537         this.white = [];
25538         this.black = [];
25539         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25540             if (b.indexOf(tag) > -1) {
25541                 return;
25542             }
25543             this.white.push(tag);
25544             
25545         }, this);
25546         
25547         Roo.each(w, function(tag) {
25548             if (b.indexOf(tag) > -1) {
25549                 return;
25550             }
25551             if (this.white.indexOf(tag) > -1) {
25552                 return;
25553             }
25554             this.white.push(tag);
25555             
25556         }, this);
25557         
25558         
25559         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25560             if (w.indexOf(tag) > -1) {
25561                 return;
25562             }
25563             this.black.push(tag);
25564             
25565         }, this);
25566         
25567         Roo.each(b, function(tag) {
25568             if (w.indexOf(tag) > -1) {
25569                 return;
25570             }
25571             if (this.black.indexOf(tag) > -1) {
25572                 return;
25573             }
25574             this.black.push(tag);
25575             
25576         }, this);
25577         
25578         
25579         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25580         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25581         
25582         this.cwhite = [];
25583         this.cblack = [];
25584         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25585             if (b.indexOf(tag) > -1) {
25586                 return;
25587             }
25588             this.cwhite.push(tag);
25589             
25590         }, this);
25591         
25592         Roo.each(w, function(tag) {
25593             if (b.indexOf(tag) > -1) {
25594                 return;
25595             }
25596             if (this.cwhite.indexOf(tag) > -1) {
25597                 return;
25598             }
25599             this.cwhite.push(tag);
25600             
25601         }, this);
25602         
25603         
25604         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25605             if (w.indexOf(tag) > -1) {
25606                 return;
25607             }
25608             this.cblack.push(tag);
25609             
25610         }, this);
25611         
25612         Roo.each(b, function(tag) {
25613             if (w.indexOf(tag) > -1) {
25614                 return;
25615             }
25616             if (this.cblack.indexOf(tag) > -1) {
25617                 return;
25618             }
25619             this.cblack.push(tag);
25620             
25621         }, this);
25622     },
25623     
25624     setStylesheets : function(stylesheets)
25625     {
25626         if(typeof(stylesheets) == 'string'){
25627             Roo.get(this.iframe.contentDocument.head).createChild({
25628                 tag : 'link',
25629                 rel : 'stylesheet',
25630                 type : 'text/css',
25631                 href : stylesheets
25632             });
25633             
25634             return;
25635         }
25636         var _this = this;
25637      
25638         Roo.each(stylesheets, function(s) {
25639             if(!s.length){
25640                 return;
25641             }
25642             
25643             Roo.get(_this.iframe.contentDocument.head).createChild({
25644                 tag : 'link',
25645                 rel : 'stylesheet',
25646                 type : 'text/css',
25647                 href : s
25648             });
25649         });
25650
25651         
25652     },
25653     
25654     removeStylesheets : function()
25655     {
25656         var _this = this;
25657         
25658         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25659             s.remove();
25660         });
25661     },
25662     
25663     setStyle : function(style)
25664     {
25665         Roo.get(this.iframe.contentDocument.head).createChild({
25666             tag : 'style',
25667             type : 'text/css',
25668             html : style
25669         });
25670
25671         return;
25672     }
25673     
25674     // hide stuff that is not compatible
25675     /**
25676      * @event blur
25677      * @hide
25678      */
25679     /**
25680      * @event change
25681      * @hide
25682      */
25683     /**
25684      * @event focus
25685      * @hide
25686      */
25687     /**
25688      * @event specialkey
25689      * @hide
25690      */
25691     /**
25692      * @cfg {String} fieldClass @hide
25693      */
25694     /**
25695      * @cfg {String} focusClass @hide
25696      */
25697     /**
25698      * @cfg {String} autoCreate @hide
25699      */
25700     /**
25701      * @cfg {String} inputType @hide
25702      */
25703     /**
25704      * @cfg {String} invalidClass @hide
25705      */
25706     /**
25707      * @cfg {String} invalidText @hide
25708      */
25709     /**
25710      * @cfg {String} msgFx @hide
25711      */
25712     /**
25713      * @cfg {String} validateOnBlur @hide
25714      */
25715 });
25716
25717 Roo.HtmlEditorCore.white = [
25718         'area', 'br', 'img', 'input', 'hr', 'wbr',
25719         
25720        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25721        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25722        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25723        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25724        'table',   'ul',         'xmp', 
25725        
25726        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25727       'thead',   'tr', 
25728      
25729       'dir', 'menu', 'ol', 'ul', 'dl',
25730        
25731       'embed',  'object'
25732 ];
25733
25734
25735 Roo.HtmlEditorCore.black = [
25736     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25737         'applet', // 
25738         'base',   'basefont', 'bgsound', 'blink',  'body', 
25739         'frame',  'frameset', 'head',    'html',   'ilayer', 
25740         'iframe', 'layer',  'link',     'meta',    'object',   
25741         'script', 'style' ,'title',  'xml' // clean later..
25742 ];
25743 Roo.HtmlEditorCore.clean = [
25744     'script', 'style', 'title', 'xml'
25745 ];
25746 Roo.HtmlEditorCore.remove = [
25747     'font'
25748 ];
25749 // attributes..
25750
25751 Roo.HtmlEditorCore.ablack = [
25752     'on'
25753 ];
25754     
25755 Roo.HtmlEditorCore.aclean = [ 
25756     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25757 ];
25758
25759 // protocols..
25760 Roo.HtmlEditorCore.pwhite= [
25761         'http',  'https',  'mailto'
25762 ];
25763
25764 // white listed style attributes.
25765 Roo.HtmlEditorCore.cwhite= [
25766       //  'text-align', /// default is to allow most things..
25767       
25768          
25769 //        'font-size'//??
25770 ];
25771
25772 // black listed style attributes.
25773 Roo.HtmlEditorCore.cblack= [
25774       //  'font-size' -- this can be set by the project 
25775 ];
25776
25777
25778 Roo.HtmlEditorCore.swapCodes   =[ 
25779     [    8211, "--" ], 
25780     [    8212, "--" ], 
25781     [    8216,  "'" ],  
25782     [    8217, "'" ],  
25783     [    8220, '"' ],  
25784     [    8221, '"' ],  
25785     [    8226, "*" ],  
25786     [    8230, "..." ]
25787 ]; 
25788
25789     /*
25790  * - LGPL
25791  *
25792  * HtmlEditor
25793  * 
25794  */
25795
25796 /**
25797  * @class Roo.bootstrap.HtmlEditor
25798  * @extends Roo.bootstrap.TextArea
25799  * Bootstrap HtmlEditor class
25800
25801  * @constructor
25802  * Create a new HtmlEditor
25803  * @param {Object} config The config object
25804  */
25805
25806 Roo.bootstrap.HtmlEditor = function(config){
25807     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25808     if (!this.toolbars) {
25809         this.toolbars = [];
25810     }
25811     
25812     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25813     this.addEvents({
25814             /**
25815              * @event initialize
25816              * Fires when the editor is fully initialized (including the iframe)
25817              * @param {HtmlEditor} this
25818              */
25819             initialize: true,
25820             /**
25821              * @event activate
25822              * Fires when the editor is first receives the focus. Any insertion must wait
25823              * until after this event.
25824              * @param {HtmlEditor} this
25825              */
25826             activate: true,
25827              /**
25828              * @event beforesync
25829              * Fires before the textarea is updated with content from the editor iframe. Return false
25830              * to cancel the sync.
25831              * @param {HtmlEditor} this
25832              * @param {String} html
25833              */
25834             beforesync: true,
25835              /**
25836              * @event beforepush
25837              * Fires before the iframe editor is updated with content from the textarea. Return false
25838              * to cancel the push.
25839              * @param {HtmlEditor} this
25840              * @param {String} html
25841              */
25842             beforepush: true,
25843              /**
25844              * @event sync
25845              * Fires when the textarea is updated with content from the editor iframe.
25846              * @param {HtmlEditor} this
25847              * @param {String} html
25848              */
25849             sync: true,
25850              /**
25851              * @event push
25852              * Fires when the iframe editor is updated with content from the textarea.
25853              * @param {HtmlEditor} this
25854              * @param {String} html
25855              */
25856             push: true,
25857              /**
25858              * @event editmodechange
25859              * Fires when the editor switches edit modes
25860              * @param {HtmlEditor} this
25861              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25862              */
25863             editmodechange: true,
25864             /**
25865              * @event editorevent
25866              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25867              * @param {HtmlEditor} this
25868              */
25869             editorevent: true,
25870             /**
25871              * @event firstfocus
25872              * Fires when on first focus - needed by toolbars..
25873              * @param {HtmlEditor} this
25874              */
25875             firstfocus: true,
25876             /**
25877              * @event autosave
25878              * Auto save the htmlEditor value as a file into Events
25879              * @param {HtmlEditor} this
25880              */
25881             autosave: true,
25882             /**
25883              * @event savedpreview
25884              * preview the saved version of htmlEditor
25885              * @param {HtmlEditor} this
25886              */
25887             savedpreview: true
25888         });
25889 };
25890
25891
25892 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25893     
25894     
25895       /**
25896      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25897      */
25898     toolbars : false,
25899     
25900      /**
25901     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25902     */
25903     btns : [],
25904    
25905      /**
25906      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25907      *                        Roo.resizable.
25908      */
25909     resizable : false,
25910      /**
25911      * @cfg {Number} height (in pixels)
25912      */   
25913     height: 300,
25914    /**
25915      * @cfg {Number} width (in pixels)
25916      */   
25917     width: false,
25918     
25919     /**
25920      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25921      * 
25922      */
25923     stylesheets: false,
25924     
25925     // id of frame..
25926     frameId: false,
25927     
25928     // private properties
25929     validationEvent : false,
25930     deferHeight: true,
25931     initialized : false,
25932     activated : false,
25933     
25934     onFocus : Roo.emptyFn,
25935     iframePad:3,
25936     hideMode:'offsets',
25937     
25938     tbContainer : false,
25939     
25940     bodyCls : '',
25941     
25942     toolbarContainer :function() {
25943         return this.wrap.select('.x-html-editor-tb',true).first();
25944     },
25945
25946     /**
25947      * Protected method that will not generally be called directly. It
25948      * is called when the editor creates its toolbar. Override this method if you need to
25949      * add custom toolbar buttons.
25950      * @param {HtmlEditor} editor
25951      */
25952     createToolbar : function(){
25953         Roo.log('renewing');
25954         Roo.log("create toolbars");
25955         
25956         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25957         this.toolbars[0].render(this.toolbarContainer());
25958         
25959         return;
25960         
25961 //        if (!editor.toolbars || !editor.toolbars.length) {
25962 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25963 //        }
25964 //        
25965 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25966 //            editor.toolbars[i] = Roo.factory(
25967 //                    typeof(editor.toolbars[i]) == 'string' ?
25968 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25969 //                Roo.bootstrap.HtmlEditor);
25970 //            editor.toolbars[i].init(editor);
25971 //        }
25972     },
25973
25974      
25975     // private
25976     onRender : function(ct, position)
25977     {
25978        // Roo.log("Call onRender: " + this.xtype);
25979         var _t = this;
25980         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25981       
25982         this.wrap = this.inputEl().wrap({
25983             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25984         });
25985         
25986         this.editorcore.onRender(ct, position);
25987          
25988         if (this.resizable) {
25989             this.resizeEl = new Roo.Resizable(this.wrap, {
25990                 pinned : true,
25991                 wrap: true,
25992                 dynamic : true,
25993                 minHeight : this.height,
25994                 height: this.height,
25995                 handles : this.resizable,
25996                 width: this.width,
25997                 listeners : {
25998                     resize : function(r, w, h) {
25999                         _t.onResize(w,h); // -something
26000                     }
26001                 }
26002             });
26003             
26004         }
26005         this.createToolbar(this);
26006        
26007         
26008         if(!this.width && this.resizable){
26009             this.setSize(this.wrap.getSize());
26010         }
26011         if (this.resizeEl) {
26012             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26013             // should trigger onReize..
26014         }
26015         
26016     },
26017
26018     // private
26019     onResize : function(w, h)
26020     {
26021         Roo.log('resize: ' +w + ',' + h );
26022         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26023         var ew = false;
26024         var eh = false;
26025         
26026         if(this.inputEl() ){
26027             if(typeof w == 'number'){
26028                 var aw = w - this.wrap.getFrameWidth('lr');
26029                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26030                 ew = aw;
26031             }
26032             if(typeof h == 'number'){
26033                  var tbh = -11;  // fixme it needs to tool bar size!
26034                 for (var i =0; i < this.toolbars.length;i++) {
26035                     // fixme - ask toolbars for heights?
26036                     tbh += this.toolbars[i].el.getHeight();
26037                     //if (this.toolbars[i].footer) {
26038                     //    tbh += this.toolbars[i].footer.el.getHeight();
26039                     //}
26040                 }
26041               
26042                 
26043                 
26044                 
26045                 
26046                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26047                 ah -= 5; // knock a few pixes off for look..
26048                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26049                 var eh = ah;
26050             }
26051         }
26052         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26053         this.editorcore.onResize(ew,eh);
26054         
26055     },
26056
26057     /**
26058      * Toggles the editor between standard and source edit mode.
26059      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26060      */
26061     toggleSourceEdit : function(sourceEditMode)
26062     {
26063         this.editorcore.toggleSourceEdit(sourceEditMode);
26064         
26065         if(this.editorcore.sourceEditMode){
26066             Roo.log('editor - showing textarea');
26067             
26068 //            Roo.log('in');
26069 //            Roo.log(this.syncValue());
26070             this.syncValue();
26071             this.inputEl().removeClass(['hide', 'x-hidden']);
26072             this.inputEl().dom.removeAttribute('tabIndex');
26073             this.inputEl().focus();
26074         }else{
26075             Roo.log('editor - hiding textarea');
26076 //            Roo.log('out')
26077 //            Roo.log(this.pushValue()); 
26078             this.pushValue();
26079             
26080             this.inputEl().addClass(['hide', 'x-hidden']);
26081             this.inputEl().dom.setAttribute('tabIndex', -1);
26082             //this.deferFocus();
26083         }
26084          
26085         if(this.resizable){
26086             this.setSize(this.wrap.getSize());
26087         }
26088         
26089         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26090     },
26091  
26092     // private (for BoxComponent)
26093     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26094
26095     // private (for BoxComponent)
26096     getResizeEl : function(){
26097         return this.wrap;
26098     },
26099
26100     // private (for BoxComponent)
26101     getPositionEl : function(){
26102         return this.wrap;
26103     },
26104
26105     // private
26106     initEvents : function(){
26107         this.originalValue = this.getValue();
26108     },
26109
26110 //    /**
26111 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26112 //     * @method
26113 //     */
26114 //    markInvalid : Roo.emptyFn,
26115 //    /**
26116 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26117 //     * @method
26118 //     */
26119 //    clearInvalid : Roo.emptyFn,
26120
26121     setValue : function(v){
26122         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26123         this.editorcore.pushValue();
26124     },
26125
26126      
26127     // private
26128     deferFocus : function(){
26129         this.focus.defer(10, this);
26130     },
26131
26132     // doc'ed in Field
26133     focus : function(){
26134         this.editorcore.focus();
26135         
26136     },
26137       
26138
26139     // private
26140     onDestroy : function(){
26141         
26142         
26143         
26144         if(this.rendered){
26145             
26146             for (var i =0; i < this.toolbars.length;i++) {
26147                 // fixme - ask toolbars for heights?
26148                 this.toolbars[i].onDestroy();
26149             }
26150             
26151             this.wrap.dom.innerHTML = '';
26152             this.wrap.remove();
26153         }
26154     },
26155
26156     // private
26157     onFirstFocus : function(){
26158         //Roo.log("onFirstFocus");
26159         this.editorcore.onFirstFocus();
26160          for (var i =0; i < this.toolbars.length;i++) {
26161             this.toolbars[i].onFirstFocus();
26162         }
26163         
26164     },
26165     
26166     // private
26167     syncValue : function()
26168     {   
26169         this.editorcore.syncValue();
26170     },
26171     
26172     pushValue : function()
26173     {   
26174         this.editorcore.pushValue();
26175     }
26176      
26177     
26178     // hide stuff that is not compatible
26179     /**
26180      * @event blur
26181      * @hide
26182      */
26183     /**
26184      * @event change
26185      * @hide
26186      */
26187     /**
26188      * @event focus
26189      * @hide
26190      */
26191     /**
26192      * @event specialkey
26193      * @hide
26194      */
26195     /**
26196      * @cfg {String} fieldClass @hide
26197      */
26198     /**
26199      * @cfg {String} focusClass @hide
26200      */
26201     /**
26202      * @cfg {String} autoCreate @hide
26203      */
26204     /**
26205      * @cfg {String} inputType @hide
26206      */
26207      
26208     /**
26209      * @cfg {String} invalidText @hide
26210      */
26211     /**
26212      * @cfg {String} msgFx @hide
26213      */
26214     /**
26215      * @cfg {String} validateOnBlur @hide
26216      */
26217 });
26218  
26219     
26220    
26221    
26222    
26223       
26224 Roo.namespace('Roo.bootstrap.htmleditor');
26225 /**
26226  * @class Roo.bootstrap.HtmlEditorToolbar1
26227  * Basic Toolbar
26228  * 
26229  * @example
26230  * Usage:
26231  *
26232  new Roo.bootstrap.HtmlEditor({
26233     ....
26234     toolbars : [
26235         new Roo.bootstrap.HtmlEditorToolbar1({
26236             disable : { fonts: 1 , format: 1, ..., ... , ...],
26237             btns : [ .... ]
26238         })
26239     }
26240      
26241  * 
26242  * @cfg {Object} disable List of elements to disable..
26243  * @cfg {Array} btns List of additional buttons.
26244  * 
26245  * 
26246  * NEEDS Extra CSS? 
26247  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26248  */
26249  
26250 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26251 {
26252     
26253     Roo.apply(this, config);
26254     
26255     // default disabled, based on 'good practice'..
26256     this.disable = this.disable || {};
26257     Roo.applyIf(this.disable, {
26258         fontSize : true,
26259         colors : true,
26260         specialElements : true
26261     });
26262     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26263     
26264     this.editor = config.editor;
26265     this.editorcore = config.editor.editorcore;
26266     
26267     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26268     
26269     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26270     // dont call parent... till later.
26271 }
26272 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26273      
26274     bar : true,
26275     
26276     editor : false,
26277     editorcore : false,
26278     
26279     
26280     formats : [
26281         "p" ,  
26282         "h1","h2","h3","h4","h5","h6", 
26283         "pre", "code", 
26284         "abbr", "acronym", "address", "cite", "samp", "var",
26285         'div','span'
26286     ],
26287     
26288     onRender : function(ct, position)
26289     {
26290        // Roo.log("Call onRender: " + this.xtype);
26291         
26292        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26293        Roo.log(this.el);
26294        this.el.dom.style.marginBottom = '0';
26295        var _this = this;
26296        var editorcore = this.editorcore;
26297        var editor= this.editor;
26298        
26299        var children = [];
26300        var btn = function(id,cmd , toggle, handler, html){
26301        
26302             var  event = toggle ? 'toggle' : 'click';
26303        
26304             var a = {
26305                 size : 'sm',
26306                 xtype: 'Button',
26307                 xns: Roo.bootstrap,
26308                 //glyphicon : id,
26309                 fa: id,
26310                 cmd : id || cmd,
26311                 enableToggle:toggle !== false,
26312                 html : html || '',
26313                 pressed : toggle ? false : null,
26314                 listeners : {}
26315             };
26316             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26317                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26318             };
26319             children.push(a);
26320             return a;
26321        }
26322        
26323     //    var cb_box = function...
26324         
26325         var style = {
26326                 xtype: 'Button',
26327                 size : 'sm',
26328                 xns: Roo.bootstrap,
26329                 fa : 'font',
26330                 //html : 'submit'
26331                 menu : {
26332                     xtype: 'Menu',
26333                     xns: Roo.bootstrap,
26334                     items:  []
26335                 }
26336         };
26337         Roo.each(this.formats, function(f) {
26338             style.menu.items.push({
26339                 xtype :'MenuItem',
26340                 xns: Roo.bootstrap,
26341                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26342                 tagname : f,
26343                 listeners : {
26344                     click : function()
26345                     {
26346                         editorcore.insertTag(this.tagname);
26347                         editor.focus();
26348                     }
26349                 }
26350                 
26351             });
26352         });
26353         children.push(style);   
26354         
26355         btn('bold',false,true);
26356         btn('italic',false,true);
26357         btn('align-left', 'justifyleft',true);
26358         btn('align-center', 'justifycenter',true);
26359         btn('align-right' , 'justifyright',true);
26360         btn('link', false, false, function(btn) {
26361             //Roo.log("create link?");
26362             var url = prompt(this.createLinkText, this.defaultLinkValue);
26363             if(url && url != 'http:/'+'/'){
26364                 this.editorcore.relayCmd('createlink', url);
26365             }
26366         }),
26367         btn('list','insertunorderedlist',true);
26368         btn('pencil', false,true, function(btn){
26369                 Roo.log(this);
26370                 this.toggleSourceEdit(btn.pressed);
26371         });
26372         
26373         if (this.editor.btns.length > 0) {
26374             for (var i = 0; i<this.editor.btns.length; i++) {
26375                 children.push(this.editor.btns[i]);
26376             }
26377         }
26378         
26379         /*
26380         var cog = {
26381                 xtype: 'Button',
26382                 size : 'sm',
26383                 xns: Roo.bootstrap,
26384                 glyphicon : 'cog',
26385                 //html : 'submit'
26386                 menu : {
26387                     xtype: 'Menu',
26388                     xns: Roo.bootstrap,
26389                     items:  []
26390                 }
26391         };
26392         
26393         cog.menu.items.push({
26394             xtype :'MenuItem',
26395             xns: Roo.bootstrap,
26396             html : Clean styles,
26397             tagname : f,
26398             listeners : {
26399                 click : function()
26400                 {
26401                     editorcore.insertTag(this.tagname);
26402                     editor.focus();
26403                 }
26404             }
26405             
26406         });
26407        */
26408         
26409          
26410        this.xtype = 'NavSimplebar';
26411         
26412         for(var i=0;i< children.length;i++) {
26413             
26414             this.buttons.add(this.addxtypeChild(children[i]));
26415             
26416         }
26417         
26418         editor.on('editorevent', this.updateToolbar, this);
26419     },
26420     onBtnClick : function(id)
26421     {
26422        this.editorcore.relayCmd(id);
26423        this.editorcore.focus();
26424     },
26425     
26426     /**
26427      * Protected method that will not generally be called directly. It triggers
26428      * a toolbar update by reading the markup state of the current selection in the editor.
26429      */
26430     updateToolbar: function(){
26431
26432         if(!this.editorcore.activated){
26433             this.editor.onFirstFocus(); // is this neeed?
26434             return;
26435         }
26436
26437         var btns = this.buttons; 
26438         var doc = this.editorcore.doc;
26439         btns.get('bold').setActive(doc.queryCommandState('bold'));
26440         btns.get('italic').setActive(doc.queryCommandState('italic'));
26441         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26442         
26443         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26444         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26445         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26446         
26447         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26448         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26449          /*
26450         
26451         var ans = this.editorcore.getAllAncestors();
26452         if (this.formatCombo) {
26453             
26454             
26455             var store = this.formatCombo.store;
26456             this.formatCombo.setValue("");
26457             for (var i =0; i < ans.length;i++) {
26458                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26459                     // select it..
26460                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26461                     break;
26462                 }
26463             }
26464         }
26465         
26466         
26467         
26468         // hides menus... - so this cant be on a menu...
26469         Roo.bootstrap.MenuMgr.hideAll();
26470         */
26471         Roo.bootstrap.MenuMgr.hideAll();
26472         //this.editorsyncValue();
26473     },
26474     onFirstFocus: function() {
26475         this.buttons.each(function(item){
26476            item.enable();
26477         });
26478     },
26479     toggleSourceEdit : function(sourceEditMode){
26480         
26481           
26482         if(sourceEditMode){
26483             Roo.log("disabling buttons");
26484            this.buttons.each( function(item){
26485                 if(item.cmd != 'pencil'){
26486                     item.disable();
26487                 }
26488             });
26489           
26490         }else{
26491             Roo.log("enabling buttons");
26492             if(this.editorcore.initialized){
26493                 this.buttons.each( function(item){
26494                     item.enable();
26495                 });
26496             }
26497             
26498         }
26499         Roo.log("calling toggole on editor");
26500         // tell the editor that it's been pressed..
26501         this.editor.toggleSourceEdit(sourceEditMode);
26502        
26503     }
26504 });
26505
26506
26507
26508
26509  
26510 /*
26511  * - LGPL
26512  */
26513
26514 /**
26515  * @class Roo.bootstrap.Markdown
26516  * @extends Roo.bootstrap.TextArea
26517  * Bootstrap Showdown editable area
26518  * @cfg {string} content
26519  * 
26520  * @constructor
26521  * Create a new Showdown
26522  */
26523
26524 Roo.bootstrap.Markdown = function(config){
26525     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26526    
26527 };
26528
26529 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26530     
26531     editing :false,
26532     
26533     initEvents : function()
26534     {
26535         
26536         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26537         this.markdownEl = this.el.createChild({
26538             cls : 'roo-markdown-area'
26539         });
26540         this.inputEl().addClass('d-none');
26541         if (this.getValue() == '') {
26542             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26543             
26544         } else {
26545             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26546         }
26547         this.markdownEl.on('click', this.toggleTextEdit, this);
26548         this.on('blur', this.toggleTextEdit, this);
26549         this.on('specialkey', this.resizeTextArea, this);
26550     },
26551     
26552     toggleTextEdit : function()
26553     {
26554         var sh = this.markdownEl.getHeight();
26555         this.inputEl().addClass('d-none');
26556         this.markdownEl.addClass('d-none');
26557         if (!this.editing) {
26558             // show editor?
26559             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26560             this.inputEl().removeClass('d-none');
26561             this.inputEl().focus();
26562             this.editing = true;
26563             return;
26564         }
26565         // show showdown...
26566         this.updateMarkdown();
26567         this.markdownEl.removeClass('d-none');
26568         this.editing = false;
26569         return;
26570     },
26571     updateMarkdown : function()
26572     {
26573         if (this.getValue() == '') {
26574             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26575             return;
26576         }
26577  
26578         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26579     },
26580     
26581     resizeTextArea: function () {
26582         
26583         var sh = 100;
26584         Roo.log([sh, this.getValue().split("\n").length * 30]);
26585         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26586     },
26587     setValue : function(val)
26588     {
26589         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26590         if (!this.editing) {
26591             this.updateMarkdown();
26592         }
26593         
26594     },
26595     focus : function()
26596     {
26597         if (!this.editing) {
26598             this.toggleTextEdit();
26599         }
26600         
26601     }
26602
26603
26604 });
26605 /**
26606  * @class Roo.bootstrap.Table.AbstractSelectionModel
26607  * @extends Roo.util.Observable
26608  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26609  * implemented by descendant classes.  This class should not be directly instantiated.
26610  * @constructor
26611  */
26612 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26613     this.locked = false;
26614     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26615 };
26616
26617
26618 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26619     /** @ignore Called by the grid automatically. Do not call directly. */
26620     init : function(grid){
26621         this.grid = grid;
26622         this.initEvents();
26623     },
26624
26625     /**
26626      * Locks the selections.
26627      */
26628     lock : function(){
26629         this.locked = true;
26630     },
26631
26632     /**
26633      * Unlocks the selections.
26634      */
26635     unlock : function(){
26636         this.locked = false;
26637     },
26638
26639     /**
26640      * Returns true if the selections are locked.
26641      * @return {Boolean}
26642      */
26643     isLocked : function(){
26644         return this.locked;
26645     },
26646     
26647     
26648     initEvents : function ()
26649     {
26650         
26651     }
26652 });
26653 /**
26654  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26655  * @class Roo.bootstrap.Table.RowSelectionModel
26656  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26657  * It supports multiple selections and keyboard selection/navigation. 
26658  * @constructor
26659  * @param {Object} config
26660  */
26661
26662 Roo.bootstrap.Table.RowSelectionModel = function(config){
26663     Roo.apply(this, config);
26664     this.selections = new Roo.util.MixedCollection(false, function(o){
26665         return o.id;
26666     });
26667
26668     this.last = false;
26669     this.lastActive = false;
26670
26671     this.addEvents({
26672         /**
26673              * @event selectionchange
26674              * Fires when the selection changes
26675              * @param {SelectionModel} this
26676              */
26677             "selectionchange" : true,
26678         /**
26679              * @event afterselectionchange
26680              * Fires after the selection changes (eg. by key press or clicking)
26681              * @param {SelectionModel} this
26682              */
26683             "afterselectionchange" : true,
26684         /**
26685              * @event beforerowselect
26686              * Fires when a row is selected being selected, return false to cancel.
26687              * @param {SelectionModel} this
26688              * @param {Number} rowIndex The selected index
26689              * @param {Boolean} keepExisting False if other selections will be cleared
26690              */
26691             "beforerowselect" : true,
26692         /**
26693              * @event rowselect
26694              * Fires when a row is selected.
26695              * @param {SelectionModel} this
26696              * @param {Number} rowIndex The selected index
26697              * @param {Roo.data.Record} r The record
26698              */
26699             "rowselect" : true,
26700         /**
26701              * @event rowdeselect
26702              * Fires when a row is deselected.
26703              * @param {SelectionModel} this
26704              * @param {Number} rowIndex The selected index
26705              */
26706         "rowdeselect" : true
26707     });
26708     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26709     this.locked = false;
26710  };
26711
26712 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26713     /**
26714      * @cfg {Boolean} singleSelect
26715      * True to allow selection of only one row at a time (defaults to false)
26716      */
26717     singleSelect : false,
26718
26719     // private
26720     initEvents : function()
26721     {
26722
26723         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26724         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26725         //}else{ // allow click to work like normal
26726          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26727         //}
26728         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26729         this.grid.on("rowclick", this.handleMouseDown, this);
26730         
26731         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26732             "up" : function(e){
26733                 if(!e.shiftKey){
26734                     this.selectPrevious(e.shiftKey);
26735                 }else if(this.last !== false && this.lastActive !== false){
26736                     var last = this.last;
26737                     this.selectRange(this.last,  this.lastActive-1);
26738                     this.grid.getView().focusRow(this.lastActive);
26739                     if(last !== false){
26740                         this.last = last;
26741                     }
26742                 }else{
26743                     this.selectFirstRow();
26744                 }
26745                 this.fireEvent("afterselectionchange", this);
26746             },
26747             "down" : function(e){
26748                 if(!e.shiftKey){
26749                     this.selectNext(e.shiftKey);
26750                 }else if(this.last !== false && this.lastActive !== false){
26751                     var last = this.last;
26752                     this.selectRange(this.last,  this.lastActive+1);
26753                     this.grid.getView().focusRow(this.lastActive);
26754                     if(last !== false){
26755                         this.last = last;
26756                     }
26757                 }else{
26758                     this.selectFirstRow();
26759                 }
26760                 this.fireEvent("afterselectionchange", this);
26761             },
26762             scope: this
26763         });
26764         this.grid.store.on('load', function(){
26765             this.selections.clear();
26766         },this);
26767         /*
26768         var view = this.grid.view;
26769         view.on("refresh", this.onRefresh, this);
26770         view.on("rowupdated", this.onRowUpdated, this);
26771         view.on("rowremoved", this.onRemove, this);
26772         */
26773     },
26774
26775     // private
26776     onRefresh : function()
26777     {
26778         var ds = this.grid.store, i, v = this.grid.view;
26779         var s = this.selections;
26780         s.each(function(r){
26781             if((i = ds.indexOfId(r.id)) != -1){
26782                 v.onRowSelect(i);
26783             }else{
26784                 s.remove(r);
26785             }
26786         });
26787     },
26788
26789     // private
26790     onRemove : function(v, index, r){
26791         this.selections.remove(r);
26792     },
26793
26794     // private
26795     onRowUpdated : function(v, index, r){
26796         if(this.isSelected(r)){
26797             v.onRowSelect(index);
26798         }
26799     },
26800
26801     /**
26802      * Select records.
26803      * @param {Array} records The records to select
26804      * @param {Boolean} keepExisting (optional) True to keep existing selections
26805      */
26806     selectRecords : function(records, keepExisting)
26807     {
26808         if(!keepExisting){
26809             this.clearSelections();
26810         }
26811             var ds = this.grid.store;
26812         for(var i = 0, len = records.length; i < len; i++){
26813             this.selectRow(ds.indexOf(records[i]), true);
26814         }
26815     },
26816
26817     /**
26818      * Gets the number of selected rows.
26819      * @return {Number}
26820      */
26821     getCount : function(){
26822         return this.selections.length;
26823     },
26824
26825     /**
26826      * Selects the first row in the grid.
26827      */
26828     selectFirstRow : function(){
26829         this.selectRow(0);
26830     },
26831
26832     /**
26833      * Select the last row.
26834      * @param {Boolean} keepExisting (optional) True to keep existing selections
26835      */
26836     selectLastRow : function(keepExisting){
26837         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26838         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26839     },
26840
26841     /**
26842      * Selects the row immediately following the last selected row.
26843      * @param {Boolean} keepExisting (optional) True to keep existing selections
26844      */
26845     selectNext : function(keepExisting)
26846     {
26847             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26848             this.selectRow(this.last+1, keepExisting);
26849             this.grid.getView().focusRow(this.last);
26850         }
26851     },
26852
26853     /**
26854      * Selects the row that precedes the last selected row.
26855      * @param {Boolean} keepExisting (optional) True to keep existing selections
26856      */
26857     selectPrevious : function(keepExisting){
26858         if(this.last){
26859             this.selectRow(this.last-1, keepExisting);
26860             this.grid.getView().focusRow(this.last);
26861         }
26862     },
26863
26864     /**
26865      * Returns the selected records
26866      * @return {Array} Array of selected records
26867      */
26868     getSelections : function(){
26869         return [].concat(this.selections.items);
26870     },
26871
26872     /**
26873      * Returns the first selected record.
26874      * @return {Record}
26875      */
26876     getSelected : function(){
26877         return this.selections.itemAt(0);
26878     },
26879
26880
26881     /**
26882      * Clears all selections.
26883      */
26884     clearSelections : function(fast)
26885     {
26886         if(this.locked) {
26887             return;
26888         }
26889         if(fast !== true){
26890                 var ds = this.grid.store;
26891             var s = this.selections;
26892             s.each(function(r){
26893                 this.deselectRow(ds.indexOfId(r.id));
26894             }, this);
26895             s.clear();
26896         }else{
26897             this.selections.clear();
26898         }
26899         this.last = false;
26900     },
26901
26902
26903     /**
26904      * Selects all rows.
26905      */
26906     selectAll : function(){
26907         if(this.locked) {
26908             return;
26909         }
26910         this.selections.clear();
26911         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26912             this.selectRow(i, true);
26913         }
26914     },
26915
26916     /**
26917      * Returns True if there is a selection.
26918      * @return {Boolean}
26919      */
26920     hasSelection : function(){
26921         return this.selections.length > 0;
26922     },
26923
26924     /**
26925      * Returns True if the specified row is selected.
26926      * @param {Number/Record} record The record or index of the record to check
26927      * @return {Boolean}
26928      */
26929     isSelected : function(index){
26930             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26931         return (r && this.selections.key(r.id) ? true : false);
26932     },
26933
26934     /**
26935      * Returns True if the specified record id is selected.
26936      * @param {String} id The id of record to check
26937      * @return {Boolean}
26938      */
26939     isIdSelected : function(id){
26940         return (this.selections.key(id) ? true : false);
26941     },
26942
26943
26944     // private
26945     handleMouseDBClick : function(e, t){
26946         
26947     },
26948     // private
26949     handleMouseDown : function(e, t)
26950     {
26951             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26952         if(this.isLocked() || rowIndex < 0 ){
26953             return;
26954         };
26955         if(e.shiftKey && this.last !== false){
26956             var last = this.last;
26957             this.selectRange(last, rowIndex, e.ctrlKey);
26958             this.last = last; // reset the last
26959             t.focus();
26960     
26961         }else{
26962             var isSelected = this.isSelected(rowIndex);
26963             //Roo.log("select row:" + rowIndex);
26964             if(isSelected){
26965                 this.deselectRow(rowIndex);
26966             } else {
26967                         this.selectRow(rowIndex, true);
26968             }
26969     
26970             /*
26971                 if(e.button !== 0 && isSelected){
26972                 alert('rowIndex 2: ' + rowIndex);
26973                     view.focusRow(rowIndex);
26974                 }else if(e.ctrlKey && isSelected){
26975                     this.deselectRow(rowIndex);
26976                 }else if(!isSelected){
26977                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26978                     view.focusRow(rowIndex);
26979                 }
26980             */
26981         }
26982         this.fireEvent("afterselectionchange", this);
26983     },
26984     // private
26985     handleDragableRowClick :  function(grid, rowIndex, e) 
26986     {
26987         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26988             this.selectRow(rowIndex, false);
26989             grid.view.focusRow(rowIndex);
26990              this.fireEvent("afterselectionchange", this);
26991         }
26992     },
26993     
26994     /**
26995      * Selects multiple rows.
26996      * @param {Array} rows Array of the indexes of the row to select
26997      * @param {Boolean} keepExisting (optional) True to keep existing selections
26998      */
26999     selectRows : function(rows, keepExisting){
27000         if(!keepExisting){
27001             this.clearSelections();
27002         }
27003         for(var i = 0, len = rows.length; i < len; i++){
27004             this.selectRow(rows[i], true);
27005         }
27006     },
27007
27008     /**
27009      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27010      * @param {Number} startRow The index of the first row in the range
27011      * @param {Number} endRow The index of the last row in the range
27012      * @param {Boolean} keepExisting (optional) True to retain existing selections
27013      */
27014     selectRange : function(startRow, endRow, keepExisting){
27015         if(this.locked) {
27016             return;
27017         }
27018         if(!keepExisting){
27019             this.clearSelections();
27020         }
27021         if(startRow <= endRow){
27022             for(var i = startRow; i <= endRow; i++){
27023                 this.selectRow(i, true);
27024             }
27025         }else{
27026             for(var i = startRow; i >= endRow; i--){
27027                 this.selectRow(i, true);
27028             }
27029         }
27030     },
27031
27032     /**
27033      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27034      * @param {Number} startRow The index of the first row in the range
27035      * @param {Number} endRow The index of the last row in the range
27036      */
27037     deselectRange : function(startRow, endRow, preventViewNotify){
27038         if(this.locked) {
27039             return;
27040         }
27041         for(var i = startRow; i <= endRow; i++){
27042             this.deselectRow(i, preventViewNotify);
27043         }
27044     },
27045
27046     /**
27047      * Selects a row.
27048      * @param {Number} row The index of the row to select
27049      * @param {Boolean} keepExisting (optional) True to keep existing selections
27050      */
27051     selectRow : function(index, keepExisting, preventViewNotify)
27052     {
27053             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27054             return;
27055         }
27056         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27057             if(!keepExisting || this.singleSelect){
27058                 this.clearSelections();
27059             }
27060             
27061             var r = this.grid.store.getAt(index);
27062             //console.log('selectRow - record id :' + r.id);
27063             
27064             this.selections.add(r);
27065             this.last = this.lastActive = index;
27066             if(!preventViewNotify){
27067                 var proxy = new Roo.Element(
27068                                 this.grid.getRowDom(index)
27069                 );
27070                 proxy.addClass('bg-info info');
27071             }
27072             this.fireEvent("rowselect", this, index, r);
27073             this.fireEvent("selectionchange", this);
27074         }
27075     },
27076
27077     /**
27078      * Deselects a row.
27079      * @param {Number} row The index of the row to deselect
27080      */
27081     deselectRow : function(index, preventViewNotify)
27082     {
27083         if(this.locked) {
27084             return;
27085         }
27086         if(this.last == index){
27087             this.last = false;
27088         }
27089         if(this.lastActive == index){
27090             this.lastActive = false;
27091         }
27092         
27093         var r = this.grid.store.getAt(index);
27094         if (!r) {
27095             return;
27096         }
27097         
27098         this.selections.remove(r);
27099         //.console.log('deselectRow - record id :' + r.id);
27100         if(!preventViewNotify){
27101         
27102             var proxy = new Roo.Element(
27103                 this.grid.getRowDom(index)
27104             );
27105             proxy.removeClass('bg-info info');
27106         }
27107         this.fireEvent("rowdeselect", this, index);
27108         this.fireEvent("selectionchange", this);
27109     },
27110
27111     // private
27112     restoreLast : function(){
27113         if(this._last){
27114             this.last = this._last;
27115         }
27116     },
27117
27118     // private
27119     acceptsNav : function(row, col, cm){
27120         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27121     },
27122
27123     // private
27124     onEditorKey : function(field, e){
27125         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27126         if(k == e.TAB){
27127             e.stopEvent();
27128             ed.completeEdit();
27129             if(e.shiftKey){
27130                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27131             }else{
27132                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27133             }
27134         }else if(k == e.ENTER && !e.ctrlKey){
27135             e.stopEvent();
27136             ed.completeEdit();
27137             if(e.shiftKey){
27138                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27139             }else{
27140                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27141             }
27142         }else if(k == e.ESC){
27143             ed.cancelEdit();
27144         }
27145         if(newCell){
27146             g.startEditing(newCell[0], newCell[1]);
27147         }
27148     }
27149 });
27150 /*
27151  * Based on:
27152  * Ext JS Library 1.1.1
27153  * Copyright(c) 2006-2007, Ext JS, LLC.
27154  *
27155  * Originally Released Under LGPL - original licence link has changed is not relivant.
27156  *
27157  * Fork - LGPL
27158  * <script type="text/javascript">
27159  */
27160  
27161 /**
27162  * @class Roo.bootstrap.PagingToolbar
27163  * @extends Roo.bootstrap.NavSimplebar
27164  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27165  * @constructor
27166  * Create a new PagingToolbar
27167  * @param {Object} config The config object
27168  * @param {Roo.data.Store} store
27169  */
27170 Roo.bootstrap.PagingToolbar = function(config)
27171 {
27172     // old args format still supported... - xtype is prefered..
27173         // created from xtype...
27174     
27175     this.ds = config.dataSource;
27176     
27177     if (config.store && !this.ds) {
27178         this.store= Roo.factory(config.store, Roo.data);
27179         this.ds = this.store;
27180         this.ds.xmodule = this.xmodule || false;
27181     }
27182     
27183     this.toolbarItems = [];
27184     if (config.items) {
27185         this.toolbarItems = config.items;
27186     }
27187     
27188     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27189     
27190     this.cursor = 0;
27191     
27192     if (this.ds) { 
27193         this.bind(this.ds);
27194     }
27195     
27196     if (Roo.bootstrap.version == 4) {
27197         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27198     } else {
27199         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27200     }
27201     
27202 };
27203
27204 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27205     /**
27206      * @cfg {Roo.data.Store} dataSource
27207      * The underlying data store providing the paged data
27208      */
27209     /**
27210      * @cfg {String/HTMLElement/Element} container
27211      * container The id or element that will contain the toolbar
27212      */
27213     /**
27214      * @cfg {Boolean} displayInfo
27215      * True to display the displayMsg (defaults to false)
27216      */
27217     /**
27218      * @cfg {Number} pageSize
27219      * The number of records to display per page (defaults to 20)
27220      */
27221     pageSize: 20,
27222     /**
27223      * @cfg {String} displayMsg
27224      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27225      */
27226     displayMsg : 'Displaying {0} - {1} of {2}',
27227     /**
27228      * @cfg {String} emptyMsg
27229      * The message to display when no records are found (defaults to "No data to display")
27230      */
27231     emptyMsg : 'No data to display',
27232     /**
27233      * Customizable piece of the default paging text (defaults to "Page")
27234      * @type String
27235      */
27236     beforePageText : "Page",
27237     /**
27238      * Customizable piece of the default paging text (defaults to "of %0")
27239      * @type String
27240      */
27241     afterPageText : "of {0}",
27242     /**
27243      * Customizable piece of the default paging text (defaults to "First Page")
27244      * @type String
27245      */
27246     firstText : "First Page",
27247     /**
27248      * Customizable piece of the default paging text (defaults to "Previous Page")
27249      * @type String
27250      */
27251     prevText : "Previous Page",
27252     /**
27253      * Customizable piece of the default paging text (defaults to "Next Page")
27254      * @type String
27255      */
27256     nextText : "Next Page",
27257     /**
27258      * Customizable piece of the default paging text (defaults to "Last Page")
27259      * @type String
27260      */
27261     lastText : "Last Page",
27262     /**
27263      * Customizable piece of the default paging text (defaults to "Refresh")
27264      * @type String
27265      */
27266     refreshText : "Refresh",
27267
27268     buttons : false,
27269     // private
27270     onRender : function(ct, position) 
27271     {
27272         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27273         this.navgroup.parentId = this.id;
27274         this.navgroup.onRender(this.el, null);
27275         // add the buttons to the navgroup
27276         
27277         if(this.displayInfo){
27278             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27279             this.displayEl = this.el.select('.x-paging-info', true).first();
27280 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27281 //            this.displayEl = navel.el.select('span',true).first();
27282         }
27283         
27284         var _this = this;
27285         
27286         if(this.buttons){
27287             Roo.each(_this.buttons, function(e){ // this might need to use render????
27288                Roo.factory(e).render(_this.el);
27289             });
27290         }
27291             
27292         Roo.each(_this.toolbarItems, function(e) {
27293             _this.navgroup.addItem(e);
27294         });
27295         
27296         
27297         this.first = this.navgroup.addItem({
27298             tooltip: this.firstText,
27299             cls: "prev btn-outline-secondary",
27300             html : ' <i class="fa fa-step-backward"></i>',
27301             disabled: true,
27302             preventDefault: true,
27303             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27304         });
27305         
27306         this.prev =  this.navgroup.addItem({
27307             tooltip: this.prevText,
27308             cls: "prev btn-outline-secondary",
27309             html : ' <i class="fa fa-backward"></i>',
27310             disabled: true,
27311             preventDefault: true,
27312             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27313         });
27314     //this.addSeparator();
27315         
27316         
27317         var field = this.navgroup.addItem( {
27318             tagtype : 'span',
27319             cls : 'x-paging-position  btn-outline-secondary',
27320              disabled: true,
27321             html : this.beforePageText  +
27322                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27323                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27324          } ); //?? escaped?
27325         
27326         this.field = field.el.select('input', true).first();
27327         this.field.on("keydown", this.onPagingKeydown, this);
27328         this.field.on("focus", function(){this.dom.select();});
27329     
27330     
27331         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27332         //this.field.setHeight(18);
27333         //this.addSeparator();
27334         this.next = this.navgroup.addItem({
27335             tooltip: this.nextText,
27336             cls: "next btn-outline-secondary",
27337             html : ' <i class="fa fa-forward"></i>',
27338             disabled: true,
27339             preventDefault: true,
27340             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27341         });
27342         this.last = this.navgroup.addItem({
27343             tooltip: this.lastText,
27344             html : ' <i class="fa fa-step-forward"></i>',
27345             cls: "next btn-outline-secondary",
27346             disabled: true,
27347             preventDefault: true,
27348             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27349         });
27350     //this.addSeparator();
27351         this.loading = this.navgroup.addItem({
27352             tooltip: this.refreshText,
27353             cls: "btn-outline-secondary",
27354             html : ' <i class="fa fa-refresh"></i>',
27355             preventDefault: true,
27356             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27357         });
27358         
27359     },
27360
27361     // private
27362     updateInfo : function(){
27363         if(this.displayEl){
27364             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27365             var msg = count == 0 ?
27366                 this.emptyMsg :
27367                 String.format(
27368                     this.displayMsg,
27369                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27370                 );
27371             this.displayEl.update(msg);
27372         }
27373     },
27374
27375     // private
27376     onLoad : function(ds, r, o)
27377     {
27378         this.cursor = o.params && o.params.start ? o.params.start : 0;
27379         
27380         var d = this.getPageData(),
27381             ap = d.activePage,
27382             ps = d.pages;
27383         
27384         
27385         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27386         this.field.dom.value = ap;
27387         this.first.setDisabled(ap == 1);
27388         this.prev.setDisabled(ap == 1);
27389         this.next.setDisabled(ap == ps);
27390         this.last.setDisabled(ap == ps);
27391         this.loading.enable();
27392         this.updateInfo();
27393     },
27394
27395     // private
27396     getPageData : function(){
27397         var total = this.ds.getTotalCount();
27398         return {
27399             total : total,
27400             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27401             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27402         };
27403     },
27404
27405     // private
27406     onLoadError : function(){
27407         this.loading.enable();
27408     },
27409
27410     // private
27411     onPagingKeydown : function(e){
27412         var k = e.getKey();
27413         var d = this.getPageData();
27414         if(k == e.RETURN){
27415             var v = this.field.dom.value, pageNum;
27416             if(!v || isNaN(pageNum = parseInt(v, 10))){
27417                 this.field.dom.value = d.activePage;
27418                 return;
27419             }
27420             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27421             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27422             e.stopEvent();
27423         }
27424         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))
27425         {
27426           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27427           this.field.dom.value = pageNum;
27428           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27429           e.stopEvent();
27430         }
27431         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27432         {
27433           var v = this.field.dom.value, pageNum; 
27434           var increment = (e.shiftKey) ? 10 : 1;
27435           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27436                 increment *= -1;
27437           }
27438           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27439             this.field.dom.value = d.activePage;
27440             return;
27441           }
27442           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27443           {
27444             this.field.dom.value = parseInt(v, 10) + increment;
27445             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27446             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27447           }
27448           e.stopEvent();
27449         }
27450     },
27451
27452     // private
27453     beforeLoad : function(){
27454         if(this.loading){
27455             this.loading.disable();
27456         }
27457     },
27458
27459     // private
27460     onClick : function(which){
27461         
27462         var ds = this.ds;
27463         if (!ds) {
27464             return;
27465         }
27466         
27467         switch(which){
27468             case "first":
27469                 ds.load({params:{start: 0, limit: this.pageSize}});
27470             break;
27471             case "prev":
27472                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27473             break;
27474             case "next":
27475                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27476             break;
27477             case "last":
27478                 var total = ds.getTotalCount();
27479                 var extra = total % this.pageSize;
27480                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27481                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27482             break;
27483             case "refresh":
27484                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27485             break;
27486         }
27487     },
27488
27489     /**
27490      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27491      * @param {Roo.data.Store} store The data store to unbind
27492      */
27493     unbind : function(ds){
27494         ds.un("beforeload", this.beforeLoad, this);
27495         ds.un("load", this.onLoad, this);
27496         ds.un("loadexception", this.onLoadError, this);
27497         ds.un("remove", this.updateInfo, this);
27498         ds.un("add", this.updateInfo, this);
27499         this.ds = undefined;
27500     },
27501
27502     /**
27503      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27504      * @param {Roo.data.Store} store The data store to bind
27505      */
27506     bind : function(ds){
27507         ds.on("beforeload", this.beforeLoad, this);
27508         ds.on("load", this.onLoad, this);
27509         ds.on("loadexception", this.onLoadError, this);
27510         ds.on("remove", this.updateInfo, this);
27511         ds.on("add", this.updateInfo, this);
27512         this.ds = ds;
27513     }
27514 });/*
27515  * - LGPL
27516  *
27517  * element
27518  * 
27519  */
27520
27521 /**
27522  * @class Roo.bootstrap.MessageBar
27523  * @extends Roo.bootstrap.Component
27524  * Bootstrap MessageBar class
27525  * @cfg {String} html contents of the MessageBar
27526  * @cfg {String} weight (info | success | warning | danger) default info
27527  * @cfg {String} beforeClass insert the bar before the given class
27528  * @cfg {Boolean} closable (true | false) default false
27529  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27530  * 
27531  * @constructor
27532  * Create a new Element
27533  * @param {Object} config The config object
27534  */
27535
27536 Roo.bootstrap.MessageBar = function(config){
27537     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27538 };
27539
27540 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27541     
27542     html: '',
27543     weight: 'info',
27544     closable: false,
27545     fixed: false,
27546     beforeClass: 'bootstrap-sticky-wrap',
27547     
27548     getAutoCreate : function(){
27549         
27550         var cfg = {
27551             tag: 'div',
27552             cls: 'alert alert-dismissable alert-' + this.weight,
27553             cn: [
27554                 {
27555                     tag: 'span',
27556                     cls: 'message',
27557                     html: this.html || ''
27558                 }
27559             ]
27560         };
27561         
27562         if(this.fixed){
27563             cfg.cls += ' alert-messages-fixed';
27564         }
27565         
27566         if(this.closable){
27567             cfg.cn.push({
27568                 tag: 'button',
27569                 cls: 'close',
27570                 html: 'x'
27571             });
27572         }
27573         
27574         return cfg;
27575     },
27576     
27577     onRender : function(ct, position)
27578     {
27579         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27580         
27581         if(!this.el){
27582             var cfg = Roo.apply({},  this.getAutoCreate());
27583             cfg.id = Roo.id();
27584             
27585             if (this.cls) {
27586                 cfg.cls += ' ' + this.cls;
27587             }
27588             if (this.style) {
27589                 cfg.style = this.style;
27590             }
27591             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27592             
27593             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27594         }
27595         
27596         this.el.select('>button.close').on('click', this.hide, this);
27597         
27598     },
27599     
27600     show : function()
27601     {
27602         if (!this.rendered) {
27603             this.render();
27604         }
27605         
27606         this.el.show();
27607         
27608         this.fireEvent('show', this);
27609         
27610     },
27611     
27612     hide : function()
27613     {
27614         if (!this.rendered) {
27615             this.render();
27616         }
27617         
27618         this.el.hide();
27619         
27620         this.fireEvent('hide', this);
27621     },
27622     
27623     update : function()
27624     {
27625 //        var e = this.el.dom.firstChild;
27626 //        
27627 //        if(this.closable){
27628 //            e = e.nextSibling;
27629 //        }
27630 //        
27631 //        e.data = this.html || '';
27632
27633         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27634     }
27635    
27636 });
27637
27638  
27639
27640      /*
27641  * - LGPL
27642  *
27643  * Graph
27644  * 
27645  */
27646
27647
27648 /**
27649  * @class Roo.bootstrap.Graph
27650  * @extends Roo.bootstrap.Component
27651  * Bootstrap Graph class
27652 > Prameters
27653  -sm {number} sm 4
27654  -md {number} md 5
27655  @cfg {String} graphtype  bar | vbar | pie
27656  @cfg {number} g_x coodinator | centre x (pie)
27657  @cfg {number} g_y coodinator | centre y (pie)
27658  @cfg {number} g_r radius (pie)
27659  @cfg {number} g_height height of the chart (respected by all elements in the set)
27660  @cfg {number} g_width width of the chart (respected by all elements in the set)
27661  @cfg {Object} title The title of the chart
27662     
27663  -{Array}  values
27664  -opts (object) options for the chart 
27665      o {
27666      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27667      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27668      o vgutter (number)
27669      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.
27670      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27671      o to
27672      o stretch (boolean)
27673      o }
27674  -opts (object) options for the pie
27675      o{
27676      o cut
27677      o startAngle (number)
27678      o endAngle (number)
27679      } 
27680  *
27681  * @constructor
27682  * Create a new Input
27683  * @param {Object} config The config object
27684  */
27685
27686 Roo.bootstrap.Graph = function(config){
27687     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27688     
27689     this.addEvents({
27690         // img events
27691         /**
27692          * @event click
27693          * The img click event for the img.
27694          * @param {Roo.EventObject} e
27695          */
27696         "click" : true
27697     });
27698 };
27699
27700 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27701     
27702     sm: 4,
27703     md: 5,
27704     graphtype: 'bar',
27705     g_height: 250,
27706     g_width: 400,
27707     g_x: 50,
27708     g_y: 50,
27709     g_r: 30,
27710     opts:{
27711         //g_colors: this.colors,
27712         g_type: 'soft',
27713         g_gutter: '20%'
27714
27715     },
27716     title : false,
27717
27718     getAutoCreate : function(){
27719         
27720         var cfg = {
27721             tag: 'div',
27722             html : null
27723         };
27724         
27725         
27726         return  cfg;
27727     },
27728
27729     onRender : function(ct,position){
27730         
27731         
27732         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27733         
27734         if (typeof(Raphael) == 'undefined') {
27735             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27736             return;
27737         }
27738         
27739         this.raphael = Raphael(this.el.dom);
27740         
27741                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27742                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27743                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27744                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27745                 /*
27746                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27747                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27748                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27749                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27750                 
27751                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27752                 r.barchart(330, 10, 300, 220, data1);
27753                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27754                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27755                 */
27756                 
27757                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27758                 // r.barchart(30, 30, 560, 250,  xdata, {
27759                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27760                 //     axis : "0 0 1 1",
27761                 //     axisxlabels :  xdata
27762                 //     //yvalues : cols,
27763                    
27764                 // });
27765 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27766 //        
27767 //        this.load(null,xdata,{
27768 //                axis : "0 0 1 1",
27769 //                axisxlabels :  xdata
27770 //                });
27771
27772     },
27773
27774     load : function(graphtype,xdata,opts)
27775     {
27776         this.raphael.clear();
27777         if(!graphtype) {
27778             graphtype = this.graphtype;
27779         }
27780         if(!opts){
27781             opts = this.opts;
27782         }
27783         var r = this.raphael,
27784             fin = function () {
27785                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27786             },
27787             fout = function () {
27788                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27789             },
27790             pfin = function() {
27791                 this.sector.stop();
27792                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27793
27794                 if (this.label) {
27795                     this.label[0].stop();
27796                     this.label[0].attr({ r: 7.5 });
27797                     this.label[1].attr({ "font-weight": 800 });
27798                 }
27799             },
27800             pfout = function() {
27801                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27802
27803                 if (this.label) {
27804                     this.label[0].animate({ r: 5 }, 500, "bounce");
27805                     this.label[1].attr({ "font-weight": 400 });
27806                 }
27807             };
27808
27809         switch(graphtype){
27810             case 'bar':
27811                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27812                 break;
27813             case 'hbar':
27814                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27815                 break;
27816             case 'pie':
27817 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27818 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27819 //            
27820                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27821                 
27822                 break;
27823
27824         }
27825         
27826         if(this.title){
27827             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27828         }
27829         
27830     },
27831     
27832     setTitle: function(o)
27833     {
27834         this.title = o;
27835     },
27836     
27837     initEvents: function() {
27838         
27839         if(!this.href){
27840             this.el.on('click', this.onClick, this);
27841         }
27842     },
27843     
27844     onClick : function(e)
27845     {
27846         Roo.log('img onclick');
27847         this.fireEvent('click', this, e);
27848     }
27849    
27850 });
27851
27852  
27853 /*
27854  * - LGPL
27855  *
27856  * numberBox
27857  * 
27858  */
27859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27860
27861 /**
27862  * @class Roo.bootstrap.dash.NumberBox
27863  * @extends Roo.bootstrap.Component
27864  * Bootstrap NumberBox class
27865  * @cfg {String} headline Box headline
27866  * @cfg {String} content Box content
27867  * @cfg {String} icon Box icon
27868  * @cfg {String} footer Footer text
27869  * @cfg {String} fhref Footer href
27870  * 
27871  * @constructor
27872  * Create a new NumberBox
27873  * @param {Object} config The config object
27874  */
27875
27876
27877 Roo.bootstrap.dash.NumberBox = function(config){
27878     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27879     
27880 };
27881
27882 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27883     
27884     headline : '',
27885     content : '',
27886     icon : '',
27887     footer : '',
27888     fhref : '',
27889     ficon : '',
27890     
27891     getAutoCreate : function(){
27892         
27893         var cfg = {
27894             tag : 'div',
27895             cls : 'small-box ',
27896             cn : [
27897                 {
27898                     tag : 'div',
27899                     cls : 'inner',
27900                     cn :[
27901                         {
27902                             tag : 'h3',
27903                             cls : 'roo-headline',
27904                             html : this.headline
27905                         },
27906                         {
27907                             tag : 'p',
27908                             cls : 'roo-content',
27909                             html : this.content
27910                         }
27911                     ]
27912                 }
27913             ]
27914         };
27915         
27916         if(this.icon){
27917             cfg.cn.push({
27918                 tag : 'div',
27919                 cls : 'icon',
27920                 cn :[
27921                     {
27922                         tag : 'i',
27923                         cls : 'ion ' + this.icon
27924                     }
27925                 ]
27926             });
27927         }
27928         
27929         if(this.footer){
27930             var footer = {
27931                 tag : 'a',
27932                 cls : 'small-box-footer',
27933                 href : this.fhref || '#',
27934                 html : this.footer
27935             };
27936             
27937             cfg.cn.push(footer);
27938             
27939         }
27940         
27941         return  cfg;
27942     },
27943
27944     onRender : function(ct,position){
27945         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27946
27947
27948        
27949                 
27950     },
27951
27952     setHeadline: function (value)
27953     {
27954         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27955     },
27956     
27957     setFooter: function (value, href)
27958     {
27959         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27960         
27961         if(href){
27962             this.el.select('a.small-box-footer',true).first().attr('href', href);
27963         }
27964         
27965     },
27966
27967     setContent: function (value)
27968     {
27969         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27970     },
27971
27972     initEvents: function() 
27973     {   
27974         
27975     }
27976     
27977 });
27978
27979  
27980 /*
27981  * - LGPL
27982  *
27983  * TabBox
27984  * 
27985  */
27986 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27987
27988 /**
27989  * @class Roo.bootstrap.dash.TabBox
27990  * @extends Roo.bootstrap.Component
27991  * Bootstrap TabBox class
27992  * @cfg {String} title Title of the TabBox
27993  * @cfg {String} icon Icon of the TabBox
27994  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27995  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27996  * 
27997  * @constructor
27998  * Create a new TabBox
27999  * @param {Object} config The config object
28000  */
28001
28002
28003 Roo.bootstrap.dash.TabBox = function(config){
28004     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28005     this.addEvents({
28006         // raw events
28007         /**
28008          * @event addpane
28009          * When a pane is added
28010          * @param {Roo.bootstrap.dash.TabPane} pane
28011          */
28012         "addpane" : true,
28013         /**
28014          * @event activatepane
28015          * When a pane is activated
28016          * @param {Roo.bootstrap.dash.TabPane} pane
28017          */
28018         "activatepane" : true
28019         
28020          
28021     });
28022     
28023     this.panes = [];
28024 };
28025
28026 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28027
28028     title : '',
28029     icon : false,
28030     showtabs : true,
28031     tabScrollable : false,
28032     
28033     getChildContainer : function()
28034     {
28035         return this.el.select('.tab-content', true).first();
28036     },
28037     
28038     getAutoCreate : function(){
28039         
28040         var header = {
28041             tag: 'li',
28042             cls: 'pull-left header',
28043             html: this.title,
28044             cn : []
28045         };
28046         
28047         if(this.icon){
28048             header.cn.push({
28049                 tag: 'i',
28050                 cls: 'fa ' + this.icon
28051             });
28052         }
28053         
28054         var h = {
28055             tag: 'ul',
28056             cls: 'nav nav-tabs pull-right',
28057             cn: [
28058                 header
28059             ]
28060         };
28061         
28062         if(this.tabScrollable){
28063             h = {
28064                 tag: 'div',
28065                 cls: 'tab-header',
28066                 cn: [
28067                     {
28068                         tag: 'ul',
28069                         cls: 'nav nav-tabs pull-right',
28070                         cn: [
28071                             header
28072                         ]
28073                     }
28074                 ]
28075             };
28076         }
28077         
28078         var cfg = {
28079             tag: 'div',
28080             cls: 'nav-tabs-custom',
28081             cn: [
28082                 h,
28083                 {
28084                     tag: 'div',
28085                     cls: 'tab-content no-padding',
28086                     cn: []
28087                 }
28088             ]
28089         };
28090
28091         return  cfg;
28092     },
28093     initEvents : function()
28094     {
28095         //Roo.log('add add pane handler');
28096         this.on('addpane', this.onAddPane, this);
28097     },
28098      /**
28099      * Updates the box title
28100      * @param {String} html to set the title to.
28101      */
28102     setTitle : function(value)
28103     {
28104         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28105     },
28106     onAddPane : function(pane)
28107     {
28108         this.panes.push(pane);
28109         //Roo.log('addpane');
28110         //Roo.log(pane);
28111         // tabs are rendere left to right..
28112         if(!this.showtabs){
28113             return;
28114         }
28115         
28116         var ctr = this.el.select('.nav-tabs', true).first();
28117          
28118          
28119         var existing = ctr.select('.nav-tab',true);
28120         var qty = existing.getCount();;
28121         
28122         
28123         var tab = ctr.createChild({
28124             tag : 'li',
28125             cls : 'nav-tab' + (qty ? '' : ' active'),
28126             cn : [
28127                 {
28128                     tag : 'a',
28129                     href:'#',
28130                     html : pane.title
28131                 }
28132             ]
28133         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28134         pane.tab = tab;
28135         
28136         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28137         if (!qty) {
28138             pane.el.addClass('active');
28139         }
28140         
28141                 
28142     },
28143     onTabClick : function(ev,un,ob,pane)
28144     {
28145         //Roo.log('tab - prev default');
28146         ev.preventDefault();
28147         
28148         
28149         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28150         pane.tab.addClass('active');
28151         //Roo.log(pane.title);
28152         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28153         // technically we should have a deactivate event.. but maybe add later.
28154         // and it should not de-activate the selected tab...
28155         this.fireEvent('activatepane', pane);
28156         pane.el.addClass('active');
28157         pane.fireEvent('activate');
28158         
28159         
28160     },
28161     
28162     getActivePane : function()
28163     {
28164         var r = false;
28165         Roo.each(this.panes, function(p) {
28166             if(p.el.hasClass('active')){
28167                 r = p;
28168                 return false;
28169             }
28170             
28171             return;
28172         });
28173         
28174         return r;
28175     }
28176     
28177     
28178 });
28179
28180  
28181 /*
28182  * - LGPL
28183  *
28184  * Tab pane
28185  * 
28186  */
28187 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28188 /**
28189  * @class Roo.bootstrap.TabPane
28190  * @extends Roo.bootstrap.Component
28191  * Bootstrap TabPane class
28192  * @cfg {Boolean} active (false | true) Default false
28193  * @cfg {String} title title of panel
28194
28195  * 
28196  * @constructor
28197  * Create a new TabPane
28198  * @param {Object} config The config object
28199  */
28200
28201 Roo.bootstrap.dash.TabPane = function(config){
28202     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28203     
28204     this.addEvents({
28205         // raw events
28206         /**
28207          * @event activate
28208          * When a pane is activated
28209          * @param {Roo.bootstrap.dash.TabPane} pane
28210          */
28211         "activate" : true
28212          
28213     });
28214 };
28215
28216 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28217     
28218     active : false,
28219     title : '',
28220     
28221     // the tabBox that this is attached to.
28222     tab : false,
28223      
28224     getAutoCreate : function() 
28225     {
28226         var cfg = {
28227             tag: 'div',
28228             cls: 'tab-pane'
28229         };
28230         
28231         if(this.active){
28232             cfg.cls += ' active';
28233         }
28234         
28235         return cfg;
28236     },
28237     initEvents  : function()
28238     {
28239         //Roo.log('trigger add pane handler');
28240         this.parent().fireEvent('addpane', this)
28241     },
28242     
28243      /**
28244      * Updates the tab title 
28245      * @param {String} html to set the title to.
28246      */
28247     setTitle: function(str)
28248     {
28249         if (!this.tab) {
28250             return;
28251         }
28252         this.title = str;
28253         this.tab.select('a', true).first().dom.innerHTML = str;
28254         
28255     }
28256     
28257     
28258     
28259 });
28260
28261  
28262
28263
28264  /*
28265  * - LGPL
28266  *
28267  * menu
28268  * 
28269  */
28270 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28271
28272 /**
28273  * @class Roo.bootstrap.menu.Menu
28274  * @extends Roo.bootstrap.Component
28275  * Bootstrap Menu class - container for Menu
28276  * @cfg {String} html Text of the menu
28277  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28278  * @cfg {String} icon Font awesome icon
28279  * @cfg {String} pos Menu align to (top | bottom) default bottom
28280  * 
28281  * 
28282  * @constructor
28283  * Create a new Menu
28284  * @param {Object} config The config object
28285  */
28286
28287
28288 Roo.bootstrap.menu.Menu = function(config){
28289     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28290     
28291     this.addEvents({
28292         /**
28293          * @event beforeshow
28294          * Fires before this menu is displayed
28295          * @param {Roo.bootstrap.menu.Menu} this
28296          */
28297         beforeshow : true,
28298         /**
28299          * @event beforehide
28300          * Fires before this menu is hidden
28301          * @param {Roo.bootstrap.menu.Menu} this
28302          */
28303         beforehide : true,
28304         /**
28305          * @event show
28306          * Fires after this menu is displayed
28307          * @param {Roo.bootstrap.menu.Menu} this
28308          */
28309         show : true,
28310         /**
28311          * @event hide
28312          * Fires after this menu is hidden
28313          * @param {Roo.bootstrap.menu.Menu} this
28314          */
28315         hide : true,
28316         /**
28317          * @event click
28318          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28319          * @param {Roo.bootstrap.menu.Menu} this
28320          * @param {Roo.EventObject} e
28321          */
28322         click : true
28323     });
28324     
28325 };
28326
28327 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28328     
28329     submenu : false,
28330     html : '',
28331     weight : 'default',
28332     icon : false,
28333     pos : 'bottom',
28334     
28335     
28336     getChildContainer : function() {
28337         if(this.isSubMenu){
28338             return this.el;
28339         }
28340         
28341         return this.el.select('ul.dropdown-menu', true).first();  
28342     },
28343     
28344     getAutoCreate : function()
28345     {
28346         var text = [
28347             {
28348                 tag : 'span',
28349                 cls : 'roo-menu-text',
28350                 html : this.html
28351             }
28352         ];
28353         
28354         if(this.icon){
28355             text.unshift({
28356                 tag : 'i',
28357                 cls : 'fa ' + this.icon
28358             })
28359         }
28360         
28361         
28362         var cfg = {
28363             tag : 'div',
28364             cls : 'btn-group',
28365             cn : [
28366                 {
28367                     tag : 'button',
28368                     cls : 'dropdown-button btn btn-' + this.weight,
28369                     cn : text
28370                 },
28371                 {
28372                     tag : 'button',
28373                     cls : 'dropdown-toggle btn btn-' + this.weight,
28374                     cn : [
28375                         {
28376                             tag : 'span',
28377                             cls : 'caret'
28378                         }
28379                     ]
28380                 },
28381                 {
28382                     tag : 'ul',
28383                     cls : 'dropdown-menu'
28384                 }
28385             ]
28386             
28387         };
28388         
28389         if(this.pos == 'top'){
28390             cfg.cls += ' dropup';
28391         }
28392         
28393         if(this.isSubMenu){
28394             cfg = {
28395                 tag : 'ul',
28396                 cls : 'dropdown-menu'
28397             }
28398         }
28399         
28400         return cfg;
28401     },
28402     
28403     onRender : function(ct, position)
28404     {
28405         this.isSubMenu = ct.hasClass('dropdown-submenu');
28406         
28407         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28408     },
28409     
28410     initEvents : function() 
28411     {
28412         if(this.isSubMenu){
28413             return;
28414         }
28415         
28416         this.hidden = true;
28417         
28418         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28419         this.triggerEl.on('click', this.onTriggerPress, this);
28420         
28421         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28422         this.buttonEl.on('click', this.onClick, this);
28423         
28424     },
28425     
28426     list : function()
28427     {
28428         if(this.isSubMenu){
28429             return this.el;
28430         }
28431         
28432         return this.el.select('ul.dropdown-menu', true).first();
28433     },
28434     
28435     onClick : function(e)
28436     {
28437         this.fireEvent("click", this, e);
28438     },
28439     
28440     onTriggerPress  : function(e)
28441     {   
28442         if (this.isVisible()) {
28443             this.hide();
28444         } else {
28445             this.show();
28446         }
28447     },
28448     
28449     isVisible : function(){
28450         return !this.hidden;
28451     },
28452     
28453     show : function()
28454     {
28455         this.fireEvent("beforeshow", this);
28456         
28457         this.hidden = false;
28458         this.el.addClass('open');
28459         
28460         Roo.get(document).on("mouseup", this.onMouseUp, this);
28461         
28462         this.fireEvent("show", this);
28463         
28464         
28465     },
28466     
28467     hide : function()
28468     {
28469         this.fireEvent("beforehide", this);
28470         
28471         this.hidden = true;
28472         this.el.removeClass('open');
28473         
28474         Roo.get(document).un("mouseup", this.onMouseUp);
28475         
28476         this.fireEvent("hide", this);
28477     },
28478     
28479     onMouseUp : function()
28480     {
28481         this.hide();
28482     }
28483     
28484 });
28485
28486  
28487  /*
28488  * - LGPL
28489  *
28490  * menu item
28491  * 
28492  */
28493 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28494
28495 /**
28496  * @class Roo.bootstrap.menu.Item
28497  * @extends Roo.bootstrap.Component
28498  * Bootstrap MenuItem class
28499  * @cfg {Boolean} submenu (true | false) default false
28500  * @cfg {String} html text of the item
28501  * @cfg {String} href the link
28502  * @cfg {Boolean} disable (true | false) default false
28503  * @cfg {Boolean} preventDefault (true | false) default true
28504  * @cfg {String} icon Font awesome icon
28505  * @cfg {String} pos Submenu align to (left | right) default right 
28506  * 
28507  * 
28508  * @constructor
28509  * Create a new Item
28510  * @param {Object} config The config object
28511  */
28512
28513
28514 Roo.bootstrap.menu.Item = function(config){
28515     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28516     this.addEvents({
28517         /**
28518          * @event mouseover
28519          * Fires when the mouse is hovering over this menu
28520          * @param {Roo.bootstrap.menu.Item} this
28521          * @param {Roo.EventObject} e
28522          */
28523         mouseover : true,
28524         /**
28525          * @event mouseout
28526          * Fires when the mouse exits this menu
28527          * @param {Roo.bootstrap.menu.Item} this
28528          * @param {Roo.EventObject} e
28529          */
28530         mouseout : true,
28531         // raw events
28532         /**
28533          * @event click
28534          * The raw click event for the entire grid.
28535          * @param {Roo.EventObject} e
28536          */
28537         click : true
28538     });
28539 };
28540
28541 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28542     
28543     submenu : false,
28544     href : '',
28545     html : '',
28546     preventDefault: true,
28547     disable : false,
28548     icon : false,
28549     pos : 'right',
28550     
28551     getAutoCreate : function()
28552     {
28553         var text = [
28554             {
28555                 tag : 'span',
28556                 cls : 'roo-menu-item-text',
28557                 html : this.html
28558             }
28559         ];
28560         
28561         if(this.icon){
28562             text.unshift({
28563                 tag : 'i',
28564                 cls : 'fa ' + this.icon
28565             })
28566         }
28567         
28568         var cfg = {
28569             tag : 'li',
28570             cn : [
28571                 {
28572                     tag : 'a',
28573                     href : this.href || '#',
28574                     cn : text
28575                 }
28576             ]
28577         };
28578         
28579         if(this.disable){
28580             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28581         }
28582         
28583         if(this.submenu){
28584             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28585             
28586             if(this.pos == 'left'){
28587                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28588             }
28589         }
28590         
28591         return cfg;
28592     },
28593     
28594     initEvents : function() 
28595     {
28596         this.el.on('mouseover', this.onMouseOver, this);
28597         this.el.on('mouseout', this.onMouseOut, this);
28598         
28599         this.el.select('a', true).first().on('click', this.onClick, this);
28600         
28601     },
28602     
28603     onClick : function(e)
28604     {
28605         if(this.preventDefault){
28606             e.preventDefault();
28607         }
28608         
28609         this.fireEvent("click", this, e);
28610     },
28611     
28612     onMouseOver : function(e)
28613     {
28614         if(this.submenu && this.pos == 'left'){
28615             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28616         }
28617         
28618         this.fireEvent("mouseover", this, e);
28619     },
28620     
28621     onMouseOut : function(e)
28622     {
28623         this.fireEvent("mouseout", this, e);
28624     }
28625 });
28626
28627  
28628
28629  /*
28630  * - LGPL
28631  *
28632  * menu separator
28633  * 
28634  */
28635 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28636
28637 /**
28638  * @class Roo.bootstrap.menu.Separator
28639  * @extends Roo.bootstrap.Component
28640  * Bootstrap Separator class
28641  * 
28642  * @constructor
28643  * Create a new Separator
28644  * @param {Object} config The config object
28645  */
28646
28647
28648 Roo.bootstrap.menu.Separator = function(config){
28649     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28650 };
28651
28652 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28653     
28654     getAutoCreate : function(){
28655         var cfg = {
28656             tag : 'li',
28657             cls: 'divider'
28658         };
28659         
28660         return cfg;
28661     }
28662    
28663 });
28664
28665  
28666
28667  /*
28668  * - LGPL
28669  *
28670  * Tooltip
28671  * 
28672  */
28673
28674 /**
28675  * @class Roo.bootstrap.Tooltip
28676  * Bootstrap Tooltip class
28677  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28678  * to determine which dom element triggers the tooltip.
28679  * 
28680  * It needs to add support for additional attributes like tooltip-position
28681  * 
28682  * @constructor
28683  * Create a new Toolti
28684  * @param {Object} config The config object
28685  */
28686
28687 Roo.bootstrap.Tooltip = function(config){
28688     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28689     
28690     this.alignment = Roo.bootstrap.Tooltip.alignment;
28691     
28692     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28693         this.alignment = config.alignment;
28694     }
28695     
28696 };
28697
28698 Roo.apply(Roo.bootstrap.Tooltip, {
28699     /**
28700      * @function init initialize tooltip monitoring.
28701      * @static
28702      */
28703     currentEl : false,
28704     currentTip : false,
28705     currentRegion : false,
28706     
28707     //  init : delay?
28708     
28709     init : function()
28710     {
28711         Roo.get(document).on('mouseover', this.enter ,this);
28712         Roo.get(document).on('mouseout', this.leave, this);
28713          
28714         
28715         this.currentTip = new Roo.bootstrap.Tooltip();
28716     },
28717     
28718     enter : function(ev)
28719     {
28720         var dom = ev.getTarget();
28721         
28722         //Roo.log(['enter',dom]);
28723         var el = Roo.fly(dom);
28724         if (this.currentEl) {
28725             //Roo.log(dom);
28726             //Roo.log(this.currentEl);
28727             //Roo.log(this.currentEl.contains(dom));
28728             if (this.currentEl == el) {
28729                 return;
28730             }
28731             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28732                 return;
28733             }
28734
28735         }
28736         
28737         if (this.currentTip.el) {
28738             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28739         }    
28740         //Roo.log(ev);
28741         
28742         if(!el || el.dom == document){
28743             return;
28744         }
28745         
28746         var bindEl = el;
28747         
28748         // you can not look for children, as if el is the body.. then everythign is the child..
28749         if (!el.attr('tooltip')) { //
28750             if (!el.select("[tooltip]").elements.length) {
28751                 return;
28752             }
28753             // is the mouse over this child...?
28754             bindEl = el.select("[tooltip]").first();
28755             var xy = ev.getXY();
28756             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28757                 //Roo.log("not in region.");
28758                 return;
28759             }
28760             //Roo.log("child element over..");
28761             
28762         }
28763         this.currentEl = bindEl;
28764         this.currentTip.bind(bindEl);
28765         this.currentRegion = Roo.lib.Region.getRegion(dom);
28766         this.currentTip.enter();
28767         
28768     },
28769     leave : function(ev)
28770     {
28771         var dom = ev.getTarget();
28772         //Roo.log(['leave',dom]);
28773         if (!this.currentEl) {
28774             return;
28775         }
28776         
28777         
28778         if (dom != this.currentEl.dom) {
28779             return;
28780         }
28781         var xy = ev.getXY();
28782         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28783             return;
28784         }
28785         // only activate leave if mouse cursor is outside... bounding box..
28786         
28787         
28788         
28789         
28790         if (this.currentTip) {
28791             this.currentTip.leave();
28792         }
28793         //Roo.log('clear currentEl');
28794         this.currentEl = false;
28795         
28796         
28797     },
28798     alignment : {
28799         'left' : ['r-l', [-2,0], 'right'],
28800         'right' : ['l-r', [2,0], 'left'],
28801         'bottom' : ['t-b', [0,2], 'top'],
28802         'top' : [ 'b-t', [0,-2], 'bottom']
28803     }
28804     
28805 });
28806
28807
28808 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28809     
28810     
28811     bindEl : false,
28812     
28813     delay : null, // can be { show : 300 , hide: 500}
28814     
28815     timeout : null,
28816     
28817     hoverState : null, //???
28818     
28819     placement : 'bottom', 
28820     
28821     alignment : false,
28822     
28823     getAutoCreate : function(){
28824     
28825         var cfg = {
28826            cls : 'tooltip',   
28827            role : 'tooltip',
28828            cn : [
28829                 {
28830                     cls : 'tooltip-arrow arrow'
28831                 },
28832                 {
28833                     cls : 'tooltip-inner'
28834                 }
28835            ]
28836         };
28837         
28838         return cfg;
28839     },
28840     bind : function(el)
28841     {
28842         this.bindEl = el;
28843     },
28844     
28845     initEvents : function()
28846     {
28847         this.arrowEl = this.el.select('.arrow', true).first();
28848         this.innerEl = this.el.select('.tooltip-inner', true).first();
28849     },
28850     
28851     enter : function () {
28852        
28853         if (this.timeout != null) {
28854             clearTimeout(this.timeout);
28855         }
28856         
28857         this.hoverState = 'in';
28858          //Roo.log("enter - show");
28859         if (!this.delay || !this.delay.show) {
28860             this.show();
28861             return;
28862         }
28863         var _t = this;
28864         this.timeout = setTimeout(function () {
28865             if (_t.hoverState == 'in') {
28866                 _t.show();
28867             }
28868         }, this.delay.show);
28869     },
28870     leave : function()
28871     {
28872         clearTimeout(this.timeout);
28873     
28874         this.hoverState = 'out';
28875          if (!this.delay || !this.delay.hide) {
28876             this.hide();
28877             return;
28878         }
28879        
28880         var _t = this;
28881         this.timeout = setTimeout(function () {
28882             //Roo.log("leave - timeout");
28883             
28884             if (_t.hoverState == 'out') {
28885                 _t.hide();
28886                 Roo.bootstrap.Tooltip.currentEl = false;
28887             }
28888         }, delay);
28889     },
28890     
28891     show : function (msg)
28892     {
28893         if (!this.el) {
28894             this.render(document.body);
28895         }
28896         // set content.
28897         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28898         
28899         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28900         
28901         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28902         
28903         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28904                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28905         
28906         var placement = typeof this.placement == 'function' ?
28907             this.placement.call(this, this.el, on_el) :
28908             this.placement;
28909             
28910         var autoToken = /\s?auto?\s?/i;
28911         var autoPlace = autoToken.test(placement);
28912         if (autoPlace) {
28913             placement = placement.replace(autoToken, '') || 'top';
28914         }
28915         
28916         //this.el.detach()
28917         //this.el.setXY([0,0]);
28918         this.el.show();
28919         //this.el.dom.style.display='block';
28920         
28921         //this.el.appendTo(on_el);
28922         
28923         var p = this.getPosition();
28924         var box = this.el.getBox();
28925         
28926         if (autoPlace) {
28927             // fixme..
28928         }
28929         
28930         var align = this.alignment[placement];
28931         
28932         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28933         
28934         if(placement == 'top' || placement == 'bottom'){
28935             if(xy[0] < 0){
28936                 placement = 'right';
28937             }
28938             
28939             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28940                 placement = 'left';
28941             }
28942             
28943             var scroll = Roo.select('body', true).first().getScroll();
28944             
28945             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28946                 placement = 'top';
28947             }
28948             
28949             align = this.alignment[placement];
28950             
28951             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28952             
28953         }
28954         
28955         this.el.alignTo(this.bindEl, align[0],align[1]);
28956         //var arrow = this.el.select('.arrow',true).first();
28957         //arrow.set(align[2], 
28958         
28959         this.el.addClass(placement);
28960         this.el.addClass("bs-tooltip-"+ placement);
28961         
28962         this.el.addClass('in fade show');
28963         
28964         this.hoverState = null;
28965         
28966         if (this.el.hasClass('fade')) {
28967             // fade it?
28968         }
28969         
28970         
28971         
28972         
28973         
28974     },
28975     hide : function()
28976     {
28977          
28978         if (!this.el) {
28979             return;
28980         }
28981         //this.el.setXY([0,0]);
28982         this.el.removeClass(['show', 'in']);
28983         //this.el.hide();
28984         
28985     }
28986     
28987 });
28988  
28989
28990  /*
28991  * - LGPL
28992  *
28993  * Location Picker
28994  * 
28995  */
28996
28997 /**
28998  * @class Roo.bootstrap.LocationPicker
28999  * @extends Roo.bootstrap.Component
29000  * Bootstrap LocationPicker class
29001  * @cfg {Number} latitude Position when init default 0
29002  * @cfg {Number} longitude Position when init default 0
29003  * @cfg {Number} zoom default 15
29004  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29005  * @cfg {Boolean} mapTypeControl default false
29006  * @cfg {Boolean} disableDoubleClickZoom default false
29007  * @cfg {Boolean} scrollwheel default true
29008  * @cfg {Boolean} streetViewControl default false
29009  * @cfg {Number} radius default 0
29010  * @cfg {String} locationName
29011  * @cfg {Boolean} draggable default true
29012  * @cfg {Boolean} enableAutocomplete default false
29013  * @cfg {Boolean} enableReverseGeocode default true
29014  * @cfg {String} markerTitle
29015  * 
29016  * @constructor
29017  * Create a new LocationPicker
29018  * @param {Object} config The config object
29019  */
29020
29021
29022 Roo.bootstrap.LocationPicker = function(config){
29023     
29024     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29025     
29026     this.addEvents({
29027         /**
29028          * @event initial
29029          * Fires when the picker initialized.
29030          * @param {Roo.bootstrap.LocationPicker} this
29031          * @param {Google Location} location
29032          */
29033         initial : true,
29034         /**
29035          * @event positionchanged
29036          * Fires when the picker position changed.
29037          * @param {Roo.bootstrap.LocationPicker} this
29038          * @param {Google Location} location
29039          */
29040         positionchanged : true,
29041         /**
29042          * @event resize
29043          * Fires when the map resize.
29044          * @param {Roo.bootstrap.LocationPicker} this
29045          */
29046         resize : true,
29047         /**
29048          * @event show
29049          * Fires when the map show.
29050          * @param {Roo.bootstrap.LocationPicker} this
29051          */
29052         show : true,
29053         /**
29054          * @event hide
29055          * Fires when the map hide.
29056          * @param {Roo.bootstrap.LocationPicker} this
29057          */
29058         hide : true,
29059         /**
29060          * @event mapClick
29061          * Fires when click the map.
29062          * @param {Roo.bootstrap.LocationPicker} this
29063          * @param {Map event} e
29064          */
29065         mapClick : true,
29066         /**
29067          * @event mapRightClick
29068          * Fires when right click the map.
29069          * @param {Roo.bootstrap.LocationPicker} this
29070          * @param {Map event} e
29071          */
29072         mapRightClick : true,
29073         /**
29074          * @event markerClick
29075          * Fires when click the marker.
29076          * @param {Roo.bootstrap.LocationPicker} this
29077          * @param {Map event} e
29078          */
29079         markerClick : true,
29080         /**
29081          * @event markerRightClick
29082          * Fires when right click the marker.
29083          * @param {Roo.bootstrap.LocationPicker} this
29084          * @param {Map event} e
29085          */
29086         markerRightClick : true,
29087         /**
29088          * @event OverlayViewDraw
29089          * Fires when OverlayView Draw
29090          * @param {Roo.bootstrap.LocationPicker} this
29091          */
29092         OverlayViewDraw : true,
29093         /**
29094          * @event OverlayViewOnAdd
29095          * Fires when OverlayView Draw
29096          * @param {Roo.bootstrap.LocationPicker} this
29097          */
29098         OverlayViewOnAdd : true,
29099         /**
29100          * @event OverlayViewOnRemove
29101          * Fires when OverlayView Draw
29102          * @param {Roo.bootstrap.LocationPicker} this
29103          */
29104         OverlayViewOnRemove : true,
29105         /**
29106          * @event OverlayViewShow
29107          * Fires when OverlayView Draw
29108          * @param {Roo.bootstrap.LocationPicker} this
29109          * @param {Pixel} cpx
29110          */
29111         OverlayViewShow : true,
29112         /**
29113          * @event OverlayViewHide
29114          * Fires when OverlayView Draw
29115          * @param {Roo.bootstrap.LocationPicker} this
29116          */
29117         OverlayViewHide : true,
29118         /**
29119          * @event loadexception
29120          * Fires when load google lib failed.
29121          * @param {Roo.bootstrap.LocationPicker} this
29122          */
29123         loadexception : true
29124     });
29125         
29126 };
29127
29128 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29129     
29130     gMapContext: false,
29131     
29132     latitude: 0,
29133     longitude: 0,
29134     zoom: 15,
29135     mapTypeId: false,
29136     mapTypeControl: false,
29137     disableDoubleClickZoom: false,
29138     scrollwheel: true,
29139     streetViewControl: false,
29140     radius: 0,
29141     locationName: '',
29142     draggable: true,
29143     enableAutocomplete: false,
29144     enableReverseGeocode: true,
29145     markerTitle: '',
29146     
29147     getAutoCreate: function()
29148     {
29149
29150         var cfg = {
29151             tag: 'div',
29152             cls: 'roo-location-picker'
29153         };
29154         
29155         return cfg
29156     },
29157     
29158     initEvents: function(ct, position)
29159     {       
29160         if(!this.el.getWidth() || this.isApplied()){
29161             return;
29162         }
29163         
29164         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29165         
29166         this.initial();
29167     },
29168     
29169     initial: function()
29170     {
29171         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29172             this.fireEvent('loadexception', this);
29173             return;
29174         }
29175         
29176         if(!this.mapTypeId){
29177             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29178         }
29179         
29180         this.gMapContext = this.GMapContext();
29181         
29182         this.initOverlayView();
29183         
29184         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29185         
29186         var _this = this;
29187                 
29188         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29189             _this.setPosition(_this.gMapContext.marker.position);
29190         });
29191         
29192         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29193             _this.fireEvent('mapClick', this, event);
29194             
29195         });
29196
29197         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29198             _this.fireEvent('mapRightClick', this, event);
29199             
29200         });
29201         
29202         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29203             _this.fireEvent('markerClick', this, event);
29204             
29205         });
29206
29207         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29208             _this.fireEvent('markerRightClick', this, event);
29209             
29210         });
29211         
29212         this.setPosition(this.gMapContext.location);
29213         
29214         this.fireEvent('initial', this, this.gMapContext.location);
29215     },
29216     
29217     initOverlayView: function()
29218     {
29219         var _this = this;
29220         
29221         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29222             
29223             draw: function()
29224             {
29225                 _this.fireEvent('OverlayViewDraw', _this);
29226             },
29227             
29228             onAdd: function()
29229             {
29230                 _this.fireEvent('OverlayViewOnAdd', _this);
29231             },
29232             
29233             onRemove: function()
29234             {
29235                 _this.fireEvent('OverlayViewOnRemove', _this);
29236             },
29237             
29238             show: function(cpx)
29239             {
29240                 _this.fireEvent('OverlayViewShow', _this, cpx);
29241             },
29242             
29243             hide: function()
29244             {
29245                 _this.fireEvent('OverlayViewHide', _this);
29246             }
29247             
29248         });
29249     },
29250     
29251     fromLatLngToContainerPixel: function(event)
29252     {
29253         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29254     },
29255     
29256     isApplied: function() 
29257     {
29258         return this.getGmapContext() == false ? false : true;
29259     },
29260     
29261     getGmapContext: function() 
29262     {
29263         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29264     },
29265     
29266     GMapContext: function() 
29267     {
29268         var position = new google.maps.LatLng(this.latitude, this.longitude);
29269         
29270         var _map = new google.maps.Map(this.el.dom, {
29271             center: position,
29272             zoom: this.zoom,
29273             mapTypeId: this.mapTypeId,
29274             mapTypeControl: this.mapTypeControl,
29275             disableDoubleClickZoom: this.disableDoubleClickZoom,
29276             scrollwheel: this.scrollwheel,
29277             streetViewControl: this.streetViewControl,
29278             locationName: this.locationName,
29279             draggable: this.draggable,
29280             enableAutocomplete: this.enableAutocomplete,
29281             enableReverseGeocode: this.enableReverseGeocode
29282         });
29283         
29284         var _marker = new google.maps.Marker({
29285             position: position,
29286             map: _map,
29287             title: this.markerTitle,
29288             draggable: this.draggable
29289         });
29290         
29291         return {
29292             map: _map,
29293             marker: _marker,
29294             circle: null,
29295             location: position,
29296             radius: this.radius,
29297             locationName: this.locationName,
29298             addressComponents: {
29299                 formatted_address: null,
29300                 addressLine1: null,
29301                 addressLine2: null,
29302                 streetName: null,
29303                 streetNumber: null,
29304                 city: null,
29305                 district: null,
29306                 state: null,
29307                 stateOrProvince: null
29308             },
29309             settings: this,
29310             domContainer: this.el.dom,
29311             geodecoder: new google.maps.Geocoder()
29312         };
29313     },
29314     
29315     drawCircle: function(center, radius, options) 
29316     {
29317         if (this.gMapContext.circle != null) {
29318             this.gMapContext.circle.setMap(null);
29319         }
29320         if (radius > 0) {
29321             radius *= 1;
29322             options = Roo.apply({}, options, {
29323                 strokeColor: "#0000FF",
29324                 strokeOpacity: .35,
29325                 strokeWeight: 2,
29326                 fillColor: "#0000FF",
29327                 fillOpacity: .2
29328             });
29329             
29330             options.map = this.gMapContext.map;
29331             options.radius = radius;
29332             options.center = center;
29333             this.gMapContext.circle = new google.maps.Circle(options);
29334             return this.gMapContext.circle;
29335         }
29336         
29337         return null;
29338     },
29339     
29340     setPosition: function(location) 
29341     {
29342         this.gMapContext.location = location;
29343         this.gMapContext.marker.setPosition(location);
29344         this.gMapContext.map.panTo(location);
29345         this.drawCircle(location, this.gMapContext.radius, {});
29346         
29347         var _this = this;
29348         
29349         if (this.gMapContext.settings.enableReverseGeocode) {
29350             this.gMapContext.geodecoder.geocode({
29351                 latLng: this.gMapContext.location
29352             }, function(results, status) {
29353                 
29354                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29355                     _this.gMapContext.locationName = results[0].formatted_address;
29356                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29357                     
29358                     _this.fireEvent('positionchanged', this, location);
29359                 }
29360             });
29361             
29362             return;
29363         }
29364         
29365         this.fireEvent('positionchanged', this, location);
29366     },
29367     
29368     resize: function()
29369     {
29370         google.maps.event.trigger(this.gMapContext.map, "resize");
29371         
29372         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29373         
29374         this.fireEvent('resize', this);
29375     },
29376     
29377     setPositionByLatLng: function(latitude, longitude)
29378     {
29379         this.setPosition(new google.maps.LatLng(latitude, longitude));
29380     },
29381     
29382     getCurrentPosition: function() 
29383     {
29384         return {
29385             latitude: this.gMapContext.location.lat(),
29386             longitude: this.gMapContext.location.lng()
29387         };
29388     },
29389     
29390     getAddressName: function() 
29391     {
29392         return this.gMapContext.locationName;
29393     },
29394     
29395     getAddressComponents: function() 
29396     {
29397         return this.gMapContext.addressComponents;
29398     },
29399     
29400     address_component_from_google_geocode: function(address_components) 
29401     {
29402         var result = {};
29403         
29404         for (var i = 0; i < address_components.length; i++) {
29405             var component = address_components[i];
29406             if (component.types.indexOf("postal_code") >= 0) {
29407                 result.postalCode = component.short_name;
29408             } else if (component.types.indexOf("street_number") >= 0) {
29409                 result.streetNumber = component.short_name;
29410             } else if (component.types.indexOf("route") >= 0) {
29411                 result.streetName = component.short_name;
29412             } else if (component.types.indexOf("neighborhood") >= 0) {
29413                 result.city = component.short_name;
29414             } else if (component.types.indexOf("locality") >= 0) {
29415                 result.city = component.short_name;
29416             } else if (component.types.indexOf("sublocality") >= 0) {
29417                 result.district = component.short_name;
29418             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29419                 result.stateOrProvince = component.short_name;
29420             } else if (component.types.indexOf("country") >= 0) {
29421                 result.country = component.short_name;
29422             }
29423         }
29424         
29425         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29426         result.addressLine2 = "";
29427         return result;
29428     },
29429     
29430     setZoomLevel: function(zoom)
29431     {
29432         this.gMapContext.map.setZoom(zoom);
29433     },
29434     
29435     show: function()
29436     {
29437         if(!this.el){
29438             return;
29439         }
29440         
29441         this.el.show();
29442         
29443         this.resize();
29444         
29445         this.fireEvent('show', this);
29446     },
29447     
29448     hide: function()
29449     {
29450         if(!this.el){
29451             return;
29452         }
29453         
29454         this.el.hide();
29455         
29456         this.fireEvent('hide', this);
29457     }
29458     
29459 });
29460
29461 Roo.apply(Roo.bootstrap.LocationPicker, {
29462     
29463     OverlayView : function(map, options)
29464     {
29465         options = options || {};
29466         
29467         this.setMap(map);
29468     }
29469     
29470     
29471 });/**
29472  * @class Roo.bootstrap.Alert
29473  * @extends Roo.bootstrap.Component
29474  * Bootstrap Alert class - shows an alert area box
29475  * eg
29476  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29477   Enter a valid email address
29478 </div>
29479  * @licence LGPL
29480  * @cfg {String} title The title of alert
29481  * @cfg {String} html The content of alert
29482  * @cfg {String} weight (  success | info | warning | danger )
29483  * @cfg {String} faicon font-awesomeicon
29484  * 
29485  * @constructor
29486  * Create a new alert
29487  * @param {Object} config The config object
29488  */
29489
29490
29491 Roo.bootstrap.Alert = function(config){
29492     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29493     
29494 };
29495
29496 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29497     
29498     title: '',
29499     html: '',
29500     weight: false,
29501     faicon: false,
29502     
29503     getAutoCreate : function()
29504     {
29505         
29506         var cfg = {
29507             tag : 'div',
29508             cls : 'alert',
29509             cn : [
29510                 {
29511                     tag : 'i',
29512                     cls : 'roo-alert-icon'
29513                     
29514                 },
29515                 {
29516                     tag : 'b',
29517                     cls : 'roo-alert-title',
29518                     html : this.title
29519                 },
29520                 {
29521                     tag : 'span',
29522                     cls : 'roo-alert-text',
29523                     html : this.html
29524                 }
29525             ]
29526         };
29527         
29528         if(this.faicon){
29529             cfg.cn[0].cls += ' fa ' + this.faicon;
29530         }
29531         
29532         if(this.weight){
29533             cfg.cls += ' alert-' + this.weight;
29534         }
29535         
29536         return cfg;
29537     },
29538     
29539     initEvents: function() 
29540     {
29541         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29542     },
29543     
29544     setTitle : function(str)
29545     {
29546         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29547     },
29548     
29549     setText : function(str)
29550     {
29551         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29552     },
29553     
29554     setWeight : function(weight)
29555     {
29556         if(this.weight){
29557             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29558         }
29559         
29560         this.weight = weight;
29561         
29562         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29563     },
29564     
29565     setIcon : function(icon)
29566     {
29567         if(this.faicon){
29568             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29569         }
29570         
29571         this.faicon = icon;
29572         
29573         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29574     },
29575     
29576     hide: function() 
29577     {
29578         this.el.hide();   
29579     },
29580     
29581     show: function() 
29582     {  
29583         this.el.show();   
29584     }
29585     
29586 });
29587
29588  
29589 /*
29590 * Licence: LGPL
29591 */
29592
29593 /**
29594  * @class Roo.bootstrap.UploadCropbox
29595  * @extends Roo.bootstrap.Component
29596  * Bootstrap UploadCropbox class
29597  * @cfg {String} emptyText show when image has been loaded
29598  * @cfg {String} rotateNotify show when image too small to rotate
29599  * @cfg {Number} errorTimeout default 3000
29600  * @cfg {Number} minWidth default 300
29601  * @cfg {Number} minHeight default 300
29602  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29603  * @cfg {Boolean} isDocument (true|false) default false
29604  * @cfg {String} url action url
29605  * @cfg {String} paramName default 'imageUpload'
29606  * @cfg {String} method default POST
29607  * @cfg {Boolean} loadMask (true|false) default true
29608  * @cfg {Boolean} loadingText default 'Loading...'
29609  * 
29610  * @constructor
29611  * Create a new UploadCropbox
29612  * @param {Object} config The config object
29613  */
29614
29615 Roo.bootstrap.UploadCropbox = function(config){
29616     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29617     
29618     this.addEvents({
29619         /**
29620          * @event beforeselectfile
29621          * Fire before select file
29622          * @param {Roo.bootstrap.UploadCropbox} this
29623          */
29624         "beforeselectfile" : true,
29625         /**
29626          * @event initial
29627          * Fire after initEvent
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          */
29630         "initial" : true,
29631         /**
29632          * @event crop
29633          * Fire after initEvent
29634          * @param {Roo.bootstrap.UploadCropbox} this
29635          * @param {String} data
29636          */
29637         "crop" : true,
29638         /**
29639          * @event prepare
29640          * Fire when preparing the file data
29641          * @param {Roo.bootstrap.UploadCropbox} this
29642          * @param {Object} file
29643          */
29644         "prepare" : true,
29645         /**
29646          * @event exception
29647          * Fire when get exception
29648          * @param {Roo.bootstrap.UploadCropbox} this
29649          * @param {XMLHttpRequest} xhr
29650          */
29651         "exception" : true,
29652         /**
29653          * @event beforeloadcanvas
29654          * Fire before load the canvas
29655          * @param {Roo.bootstrap.UploadCropbox} this
29656          * @param {String} src
29657          */
29658         "beforeloadcanvas" : true,
29659         /**
29660          * @event trash
29661          * Fire when trash image
29662          * @param {Roo.bootstrap.UploadCropbox} this
29663          */
29664         "trash" : true,
29665         /**
29666          * @event download
29667          * Fire when download the image
29668          * @param {Roo.bootstrap.UploadCropbox} this
29669          */
29670         "download" : true,
29671         /**
29672          * @event footerbuttonclick
29673          * Fire when footerbuttonclick
29674          * @param {Roo.bootstrap.UploadCropbox} this
29675          * @param {String} type
29676          */
29677         "footerbuttonclick" : true,
29678         /**
29679          * @event resize
29680          * Fire when resize
29681          * @param {Roo.bootstrap.UploadCropbox} this
29682          */
29683         "resize" : true,
29684         /**
29685          * @event rotate
29686          * Fire when rotate the image
29687          * @param {Roo.bootstrap.UploadCropbox} this
29688          * @param {String} pos
29689          */
29690         "rotate" : true,
29691         /**
29692          * @event inspect
29693          * Fire when inspect the file
29694          * @param {Roo.bootstrap.UploadCropbox} this
29695          * @param {Object} file
29696          */
29697         "inspect" : true,
29698         /**
29699          * @event upload
29700          * Fire when xhr upload the file
29701          * @param {Roo.bootstrap.UploadCropbox} this
29702          * @param {Object} data
29703          */
29704         "upload" : true,
29705         /**
29706          * @event arrange
29707          * Fire when arrange the file data
29708          * @param {Roo.bootstrap.UploadCropbox} this
29709          * @param {Object} formData
29710          */
29711         "arrange" : true
29712     });
29713     
29714     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29715 };
29716
29717 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29718     
29719     emptyText : 'Click to upload image',
29720     rotateNotify : 'Image is too small to rotate',
29721     errorTimeout : 3000,
29722     scale : 0,
29723     baseScale : 1,
29724     rotate : 0,
29725     dragable : false,
29726     pinching : false,
29727     mouseX : 0,
29728     mouseY : 0,
29729     cropData : false,
29730     minWidth : 300,
29731     minHeight : 300,
29732     file : false,
29733     exif : {},
29734     baseRotate : 1,
29735     cropType : 'image/jpeg',
29736     buttons : false,
29737     canvasLoaded : false,
29738     isDocument : false,
29739     method : 'POST',
29740     paramName : 'imageUpload',
29741     loadMask : true,
29742     loadingText : 'Loading...',
29743     maskEl : false,
29744     
29745     getAutoCreate : function()
29746     {
29747         var cfg = {
29748             tag : 'div',
29749             cls : 'roo-upload-cropbox',
29750             cn : [
29751                 {
29752                     tag : 'input',
29753                     cls : 'roo-upload-cropbox-selector',
29754                     type : 'file'
29755                 },
29756                 {
29757                     tag : 'div',
29758                     cls : 'roo-upload-cropbox-body',
29759                     style : 'cursor:pointer',
29760                     cn : [
29761                         {
29762                             tag : 'div',
29763                             cls : 'roo-upload-cropbox-preview'
29764                         },
29765                         {
29766                             tag : 'div',
29767                             cls : 'roo-upload-cropbox-thumb'
29768                         },
29769                         {
29770                             tag : 'div',
29771                             cls : 'roo-upload-cropbox-empty-notify',
29772                             html : this.emptyText
29773                         },
29774                         {
29775                             tag : 'div',
29776                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29777                             html : this.rotateNotify
29778                         }
29779                     ]
29780                 },
29781                 {
29782                     tag : 'div',
29783                     cls : 'roo-upload-cropbox-footer',
29784                     cn : {
29785                         tag : 'div',
29786                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29787                         cn : []
29788                     }
29789                 }
29790             ]
29791         };
29792         
29793         return cfg;
29794     },
29795     
29796     onRender : function(ct, position)
29797     {
29798         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29799         
29800         if (this.buttons.length) {
29801             
29802             Roo.each(this.buttons, function(bb) {
29803                 
29804                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29805                 
29806                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29807                 
29808             }, this);
29809         }
29810         
29811         if(this.loadMask){
29812             this.maskEl = this.el;
29813         }
29814     },
29815     
29816     initEvents : function()
29817     {
29818         this.urlAPI = (window.createObjectURL && window) || 
29819                                 (window.URL && URL.revokeObjectURL && URL) || 
29820                                 (window.webkitURL && webkitURL);
29821                         
29822         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29823         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29824         
29825         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29826         this.selectorEl.hide();
29827         
29828         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29829         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29830         
29831         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29832         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29833         this.thumbEl.hide();
29834         
29835         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29836         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29837         
29838         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29839         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29840         this.errorEl.hide();
29841         
29842         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29843         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844         this.footerEl.hide();
29845         
29846         this.setThumbBoxSize();
29847         
29848         this.bind();
29849         
29850         this.resize();
29851         
29852         this.fireEvent('initial', this);
29853     },
29854
29855     bind : function()
29856     {
29857         var _this = this;
29858         
29859         window.addEventListener("resize", function() { _this.resize(); } );
29860         
29861         this.bodyEl.on('click', this.beforeSelectFile, this);
29862         
29863         if(Roo.isTouch){
29864             this.bodyEl.on('touchstart', this.onTouchStart, this);
29865             this.bodyEl.on('touchmove', this.onTouchMove, this);
29866             this.bodyEl.on('touchend', this.onTouchEnd, this);
29867         }
29868         
29869         if(!Roo.isTouch){
29870             this.bodyEl.on('mousedown', this.onMouseDown, this);
29871             this.bodyEl.on('mousemove', this.onMouseMove, this);
29872             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29873             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29874             Roo.get(document).on('mouseup', this.onMouseUp, this);
29875         }
29876         
29877         this.selectorEl.on('change', this.onFileSelected, this);
29878     },
29879     
29880     reset : function()
29881     {    
29882         this.scale = 0;
29883         this.baseScale = 1;
29884         this.rotate = 0;
29885         this.baseRotate = 1;
29886         this.dragable = false;
29887         this.pinching = false;
29888         this.mouseX = 0;
29889         this.mouseY = 0;
29890         this.cropData = false;
29891         this.notifyEl.dom.innerHTML = this.emptyText;
29892         
29893         this.selectorEl.dom.value = '';
29894         
29895     },
29896     
29897     resize : function()
29898     {
29899         if(this.fireEvent('resize', this) != false){
29900             this.setThumbBoxPosition();
29901             this.setCanvasPosition();
29902         }
29903     },
29904     
29905     onFooterButtonClick : function(e, el, o, type)
29906     {
29907         switch (type) {
29908             case 'rotate-left' :
29909                 this.onRotateLeft(e);
29910                 break;
29911             case 'rotate-right' :
29912                 this.onRotateRight(e);
29913                 break;
29914             case 'picture' :
29915                 this.beforeSelectFile(e);
29916                 break;
29917             case 'trash' :
29918                 this.trash(e);
29919                 break;
29920             case 'crop' :
29921                 this.crop(e);
29922                 break;
29923             case 'download' :
29924                 this.download(e);
29925                 break;
29926             default :
29927                 break;
29928         }
29929         
29930         this.fireEvent('footerbuttonclick', this, type);
29931     },
29932     
29933     beforeSelectFile : function(e)
29934     {
29935         e.preventDefault();
29936         
29937         if(this.fireEvent('beforeselectfile', this) != false){
29938             this.selectorEl.dom.click();
29939         }
29940     },
29941     
29942     onFileSelected : function(e)
29943     {
29944         e.preventDefault();
29945         
29946         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29947             return;
29948         }
29949         
29950         var file = this.selectorEl.dom.files[0];
29951         
29952         if(this.fireEvent('inspect', this, file) != false){
29953             this.prepare(file);
29954         }
29955         
29956     },
29957     
29958     trash : function(e)
29959     {
29960         this.fireEvent('trash', this);
29961     },
29962     
29963     download : function(e)
29964     {
29965         this.fireEvent('download', this);
29966     },
29967     
29968     loadCanvas : function(src)
29969     {   
29970         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29971             
29972             this.reset();
29973             
29974             this.imageEl = document.createElement('img');
29975             
29976             var _this = this;
29977             
29978             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29979             
29980             this.imageEl.src = src;
29981         }
29982     },
29983     
29984     onLoadCanvas : function()
29985     {   
29986         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29987         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29988         
29989         this.bodyEl.un('click', this.beforeSelectFile, this);
29990         
29991         this.notifyEl.hide();
29992         this.thumbEl.show();
29993         this.footerEl.show();
29994         
29995         this.baseRotateLevel();
29996         
29997         if(this.isDocument){
29998             this.setThumbBoxSize();
29999         }
30000         
30001         this.setThumbBoxPosition();
30002         
30003         this.baseScaleLevel();
30004         
30005         this.draw();
30006         
30007         this.resize();
30008         
30009         this.canvasLoaded = true;
30010         
30011         if(this.loadMask){
30012             this.maskEl.unmask();
30013         }
30014         
30015     },
30016     
30017     setCanvasPosition : function()
30018     {   
30019         if(!this.canvasEl){
30020             return;
30021         }
30022         
30023         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30024         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30025         
30026         this.previewEl.setLeft(pw);
30027         this.previewEl.setTop(ph);
30028         
30029     },
30030     
30031     onMouseDown : function(e)
30032     {   
30033         e.stopEvent();
30034         
30035         this.dragable = true;
30036         this.pinching = false;
30037         
30038         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30039             this.dragable = false;
30040             return;
30041         }
30042         
30043         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30044         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30045         
30046     },
30047     
30048     onMouseMove : function(e)
30049     {   
30050         e.stopEvent();
30051         
30052         if(!this.canvasLoaded){
30053             return;
30054         }
30055         
30056         if (!this.dragable){
30057             return;
30058         }
30059         
30060         var minX = Math.ceil(this.thumbEl.getLeft(true));
30061         var minY = Math.ceil(this.thumbEl.getTop(true));
30062         
30063         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30064         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30065         
30066         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30067         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30068         
30069         x = x - this.mouseX;
30070         y = y - this.mouseY;
30071         
30072         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30073         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30074         
30075         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30076         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30077         
30078         this.previewEl.setLeft(bgX);
30079         this.previewEl.setTop(bgY);
30080         
30081         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30082         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30083     },
30084     
30085     onMouseUp : function(e)
30086     {   
30087         e.stopEvent();
30088         
30089         this.dragable = false;
30090     },
30091     
30092     onMouseWheel : function(e)
30093     {   
30094         e.stopEvent();
30095         
30096         this.startScale = this.scale;
30097         
30098         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30099         
30100         if(!this.zoomable()){
30101             this.scale = this.startScale;
30102             return;
30103         }
30104         
30105         this.draw();
30106         
30107         return;
30108     },
30109     
30110     zoomable : function()
30111     {
30112         var minScale = this.thumbEl.getWidth() / this.minWidth;
30113         
30114         if(this.minWidth < this.minHeight){
30115             minScale = this.thumbEl.getHeight() / this.minHeight;
30116         }
30117         
30118         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30119         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30120         
30121         if(
30122                 this.isDocument &&
30123                 (this.rotate == 0 || this.rotate == 180) && 
30124                 (
30125                     width > this.imageEl.OriginWidth || 
30126                     height > this.imageEl.OriginHeight ||
30127                     (width < this.minWidth && height < this.minHeight)
30128                 )
30129         ){
30130             return false;
30131         }
30132         
30133         if(
30134                 this.isDocument &&
30135                 (this.rotate == 90 || this.rotate == 270) && 
30136                 (
30137                     width > this.imageEl.OriginWidth || 
30138                     height > this.imageEl.OriginHeight ||
30139                     (width < this.minHeight && height < this.minWidth)
30140                 )
30141         ){
30142             return false;
30143         }
30144         
30145         if(
30146                 !this.isDocument &&
30147                 (this.rotate == 0 || this.rotate == 180) && 
30148                 (
30149                     width < this.minWidth || 
30150                     width > this.imageEl.OriginWidth || 
30151                     height < this.minHeight || 
30152                     height > this.imageEl.OriginHeight
30153                 )
30154         ){
30155             return false;
30156         }
30157         
30158         if(
30159                 !this.isDocument &&
30160                 (this.rotate == 90 || this.rotate == 270) && 
30161                 (
30162                     width < this.minHeight || 
30163                     width > this.imageEl.OriginWidth || 
30164                     height < this.minWidth || 
30165                     height > this.imageEl.OriginHeight
30166                 )
30167         ){
30168             return false;
30169         }
30170         
30171         return true;
30172         
30173     },
30174     
30175     onRotateLeft : function(e)
30176     {   
30177         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30178             
30179             var minScale = this.thumbEl.getWidth() / this.minWidth;
30180             
30181             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30182             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30183             
30184             this.startScale = this.scale;
30185             
30186             while (this.getScaleLevel() < minScale){
30187             
30188                 this.scale = this.scale + 1;
30189                 
30190                 if(!this.zoomable()){
30191                     break;
30192                 }
30193                 
30194                 if(
30195                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30196                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30197                 ){
30198                     continue;
30199                 }
30200                 
30201                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30202
30203                 this.draw();
30204                 
30205                 return;
30206             }
30207             
30208             this.scale = this.startScale;
30209             
30210             this.onRotateFail();
30211             
30212             return false;
30213         }
30214         
30215         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30216
30217         if(this.isDocument){
30218             this.setThumbBoxSize();
30219             this.setThumbBoxPosition();
30220             this.setCanvasPosition();
30221         }
30222         
30223         this.draw();
30224         
30225         this.fireEvent('rotate', this, 'left');
30226         
30227     },
30228     
30229     onRotateRight : function(e)
30230     {
30231         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30232             
30233             var minScale = this.thumbEl.getWidth() / this.minWidth;
30234         
30235             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30236             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30237             
30238             this.startScale = this.scale;
30239             
30240             while (this.getScaleLevel() < minScale){
30241             
30242                 this.scale = this.scale + 1;
30243                 
30244                 if(!this.zoomable()){
30245                     break;
30246                 }
30247                 
30248                 if(
30249                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30250                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30251                 ){
30252                     continue;
30253                 }
30254                 
30255                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30256
30257                 this.draw();
30258                 
30259                 return;
30260             }
30261             
30262             this.scale = this.startScale;
30263             
30264             this.onRotateFail();
30265             
30266             return false;
30267         }
30268         
30269         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30270
30271         if(this.isDocument){
30272             this.setThumbBoxSize();
30273             this.setThumbBoxPosition();
30274             this.setCanvasPosition();
30275         }
30276         
30277         this.draw();
30278         
30279         this.fireEvent('rotate', this, 'right');
30280     },
30281     
30282     onRotateFail : function()
30283     {
30284         this.errorEl.show(true);
30285         
30286         var _this = this;
30287         
30288         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30289     },
30290     
30291     draw : function()
30292     {
30293         this.previewEl.dom.innerHTML = '';
30294         
30295         var canvasEl = document.createElement("canvas");
30296         
30297         var contextEl = canvasEl.getContext("2d");
30298         
30299         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30300         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30301         var center = this.imageEl.OriginWidth / 2;
30302         
30303         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30304             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30305             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30306             center = this.imageEl.OriginHeight / 2;
30307         }
30308         
30309         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30310         
30311         contextEl.translate(center, center);
30312         contextEl.rotate(this.rotate * Math.PI / 180);
30313
30314         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30315         
30316         this.canvasEl = document.createElement("canvas");
30317         
30318         this.contextEl = this.canvasEl.getContext("2d");
30319         
30320         switch (this.rotate) {
30321             case 0 :
30322                 
30323                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30324                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30325                 
30326                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30327                 
30328                 break;
30329             case 90 : 
30330                 
30331                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30332                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30333                 
30334                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30335                     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);
30336                     break;
30337                 }
30338                 
30339                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30340                 
30341                 break;
30342             case 180 :
30343                 
30344                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30345                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30346                 
30347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30348                     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);
30349                     break;
30350                 }
30351                 
30352                 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);
30353                 
30354                 break;
30355             case 270 :
30356                 
30357                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30358                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30359         
30360                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30361                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30362                     break;
30363                 }
30364                 
30365                 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);
30366                 
30367                 break;
30368             default : 
30369                 break;
30370         }
30371         
30372         this.previewEl.appendChild(this.canvasEl);
30373         
30374         this.setCanvasPosition();
30375     },
30376     
30377     crop : function()
30378     {
30379         if(!this.canvasLoaded){
30380             return;
30381         }
30382         
30383         var imageCanvas = document.createElement("canvas");
30384         
30385         var imageContext = imageCanvas.getContext("2d");
30386         
30387         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30388         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30389         
30390         var center = imageCanvas.width / 2;
30391         
30392         imageContext.translate(center, center);
30393         
30394         imageContext.rotate(this.rotate * Math.PI / 180);
30395         
30396         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30397         
30398         var canvas = document.createElement("canvas");
30399         
30400         var context = canvas.getContext("2d");
30401                 
30402         canvas.width = this.minWidth;
30403         canvas.height = this.minHeight;
30404
30405         switch (this.rotate) {
30406             case 0 :
30407                 
30408                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30409                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30410                 
30411                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30412                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30413                 
30414                 var targetWidth = this.minWidth - 2 * x;
30415                 var targetHeight = this.minHeight - 2 * y;
30416                 
30417                 var scale = 1;
30418                 
30419                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30420                     scale = targetWidth / width;
30421                 }
30422                 
30423                 if(x > 0 && y == 0){
30424                     scale = targetHeight / height;
30425                 }
30426                 
30427                 if(x > 0 && y > 0){
30428                     scale = targetWidth / width;
30429                     
30430                     if(width < height){
30431                         scale = targetHeight / height;
30432                     }
30433                 }
30434                 
30435                 context.scale(scale, scale);
30436                 
30437                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30438                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30439
30440                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30441                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30442
30443                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30444                 
30445                 break;
30446             case 90 : 
30447                 
30448                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30449                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30450                 
30451                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30452                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30453                 
30454                 var targetWidth = this.minWidth - 2 * x;
30455                 var targetHeight = this.minHeight - 2 * y;
30456                 
30457                 var scale = 1;
30458                 
30459                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30460                     scale = targetWidth / width;
30461                 }
30462                 
30463                 if(x > 0 && y == 0){
30464                     scale = targetHeight / height;
30465                 }
30466                 
30467                 if(x > 0 && y > 0){
30468                     scale = targetWidth / width;
30469                     
30470                     if(width < height){
30471                         scale = targetHeight / height;
30472                     }
30473                 }
30474                 
30475                 context.scale(scale, scale);
30476                 
30477                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30478                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30479
30480                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30481                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30482                 
30483                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30484                 
30485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30486                 
30487                 break;
30488             case 180 :
30489                 
30490                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30491                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30492                 
30493                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30494                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30495                 
30496                 var targetWidth = this.minWidth - 2 * x;
30497                 var targetHeight = this.minHeight - 2 * y;
30498                 
30499                 var scale = 1;
30500                 
30501                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30502                     scale = targetWidth / width;
30503                 }
30504                 
30505                 if(x > 0 && y == 0){
30506                     scale = targetHeight / height;
30507                 }
30508                 
30509                 if(x > 0 && y > 0){
30510                     scale = targetWidth / width;
30511                     
30512                     if(width < height){
30513                         scale = targetHeight / height;
30514                     }
30515                 }
30516                 
30517                 context.scale(scale, scale);
30518                 
30519                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30520                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30521
30522                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30523                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30524
30525                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30526                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30527                 
30528                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30529                 
30530                 break;
30531             case 270 :
30532                 
30533                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30534                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30535                 
30536                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30537                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30538                 
30539                 var targetWidth = this.minWidth - 2 * x;
30540                 var targetHeight = this.minHeight - 2 * y;
30541                 
30542                 var scale = 1;
30543                 
30544                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30545                     scale = targetWidth / width;
30546                 }
30547                 
30548                 if(x > 0 && y == 0){
30549                     scale = targetHeight / height;
30550                 }
30551                 
30552                 if(x > 0 && y > 0){
30553                     scale = targetWidth / width;
30554                     
30555                     if(width < height){
30556                         scale = targetHeight / height;
30557                     }
30558                 }
30559                 
30560                 context.scale(scale, scale);
30561                 
30562                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30563                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30564
30565                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30566                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30567                 
30568                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30569                 
30570                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30571                 
30572                 break;
30573             default : 
30574                 break;
30575         }
30576         
30577         this.cropData = canvas.toDataURL(this.cropType);
30578         
30579         if(this.fireEvent('crop', this, this.cropData) !== false){
30580             this.process(this.file, this.cropData);
30581         }
30582         
30583         return;
30584         
30585     },
30586     
30587     setThumbBoxSize : function()
30588     {
30589         var width, height;
30590         
30591         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30592             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30593             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30594             
30595             this.minWidth = width;
30596             this.minHeight = height;
30597             
30598             if(this.rotate == 90 || this.rotate == 270){
30599                 this.minWidth = height;
30600                 this.minHeight = width;
30601             }
30602         }
30603         
30604         height = 300;
30605         width = Math.ceil(this.minWidth * height / this.minHeight);
30606         
30607         if(this.minWidth > this.minHeight){
30608             width = 300;
30609             height = Math.ceil(this.minHeight * width / this.minWidth);
30610         }
30611         
30612         this.thumbEl.setStyle({
30613             width : width + 'px',
30614             height : height + 'px'
30615         });
30616
30617         return;
30618             
30619     },
30620     
30621     setThumbBoxPosition : function()
30622     {
30623         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30624         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30625         
30626         this.thumbEl.setLeft(x);
30627         this.thumbEl.setTop(y);
30628         
30629     },
30630     
30631     baseRotateLevel : function()
30632     {
30633         this.baseRotate = 1;
30634         
30635         if(
30636                 typeof(this.exif) != 'undefined' &&
30637                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30638                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30639         ){
30640             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30641         }
30642         
30643         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30644         
30645     },
30646     
30647     baseScaleLevel : function()
30648     {
30649         var width, height;
30650         
30651         if(this.isDocument){
30652             
30653             if(this.baseRotate == 6 || this.baseRotate == 8){
30654             
30655                 height = this.thumbEl.getHeight();
30656                 this.baseScale = height / this.imageEl.OriginWidth;
30657
30658                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30659                     width = this.thumbEl.getWidth();
30660                     this.baseScale = width / this.imageEl.OriginHeight;
30661                 }
30662
30663                 return;
30664             }
30665
30666             height = this.thumbEl.getHeight();
30667             this.baseScale = height / this.imageEl.OriginHeight;
30668
30669             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30670                 width = this.thumbEl.getWidth();
30671                 this.baseScale = width / this.imageEl.OriginWidth;
30672             }
30673
30674             return;
30675         }
30676         
30677         if(this.baseRotate == 6 || this.baseRotate == 8){
30678             
30679             width = this.thumbEl.getHeight();
30680             this.baseScale = width / this.imageEl.OriginHeight;
30681             
30682             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30683                 height = this.thumbEl.getWidth();
30684                 this.baseScale = height / this.imageEl.OriginHeight;
30685             }
30686             
30687             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30688                 height = this.thumbEl.getWidth();
30689                 this.baseScale = height / this.imageEl.OriginHeight;
30690                 
30691                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30692                     width = this.thumbEl.getHeight();
30693                     this.baseScale = width / this.imageEl.OriginWidth;
30694                 }
30695             }
30696             
30697             return;
30698         }
30699         
30700         width = this.thumbEl.getWidth();
30701         this.baseScale = width / this.imageEl.OriginWidth;
30702         
30703         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30704             height = this.thumbEl.getHeight();
30705             this.baseScale = height / this.imageEl.OriginHeight;
30706         }
30707         
30708         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30709             
30710             height = this.thumbEl.getHeight();
30711             this.baseScale = height / this.imageEl.OriginHeight;
30712             
30713             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30714                 width = this.thumbEl.getWidth();
30715                 this.baseScale = width / this.imageEl.OriginWidth;
30716             }
30717             
30718         }
30719         
30720         return;
30721     },
30722     
30723     getScaleLevel : function()
30724     {
30725         return this.baseScale * Math.pow(1.1, this.scale);
30726     },
30727     
30728     onTouchStart : function(e)
30729     {
30730         if(!this.canvasLoaded){
30731             this.beforeSelectFile(e);
30732             return;
30733         }
30734         
30735         var touches = e.browserEvent.touches;
30736         
30737         if(!touches){
30738             return;
30739         }
30740         
30741         if(touches.length == 1){
30742             this.onMouseDown(e);
30743             return;
30744         }
30745         
30746         if(touches.length != 2){
30747             return;
30748         }
30749         
30750         var coords = [];
30751         
30752         for(var i = 0, finger; finger = touches[i]; i++){
30753             coords.push(finger.pageX, finger.pageY);
30754         }
30755         
30756         var x = Math.pow(coords[0] - coords[2], 2);
30757         var y = Math.pow(coords[1] - coords[3], 2);
30758         
30759         this.startDistance = Math.sqrt(x + y);
30760         
30761         this.startScale = this.scale;
30762         
30763         this.pinching = true;
30764         this.dragable = false;
30765         
30766     },
30767     
30768     onTouchMove : function(e)
30769     {
30770         if(!this.pinching && !this.dragable){
30771             return;
30772         }
30773         
30774         var touches = e.browserEvent.touches;
30775         
30776         if(!touches){
30777             return;
30778         }
30779         
30780         if(this.dragable){
30781             this.onMouseMove(e);
30782             return;
30783         }
30784         
30785         var coords = [];
30786         
30787         for(var i = 0, finger; finger = touches[i]; i++){
30788             coords.push(finger.pageX, finger.pageY);
30789         }
30790         
30791         var x = Math.pow(coords[0] - coords[2], 2);
30792         var y = Math.pow(coords[1] - coords[3], 2);
30793         
30794         this.endDistance = Math.sqrt(x + y);
30795         
30796         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30797         
30798         if(!this.zoomable()){
30799             this.scale = this.startScale;
30800             return;
30801         }
30802         
30803         this.draw();
30804         
30805     },
30806     
30807     onTouchEnd : function(e)
30808     {
30809         this.pinching = false;
30810         this.dragable = false;
30811         
30812     },
30813     
30814     process : function(file, crop)
30815     {
30816         if(this.loadMask){
30817             this.maskEl.mask(this.loadingText);
30818         }
30819         
30820         this.xhr = new XMLHttpRequest();
30821         
30822         file.xhr = this.xhr;
30823
30824         this.xhr.open(this.method, this.url, true);
30825         
30826         var headers = {
30827             "Accept": "application/json",
30828             "Cache-Control": "no-cache",
30829             "X-Requested-With": "XMLHttpRequest"
30830         };
30831         
30832         for (var headerName in headers) {
30833             var headerValue = headers[headerName];
30834             if (headerValue) {
30835                 this.xhr.setRequestHeader(headerName, headerValue);
30836             }
30837         }
30838         
30839         var _this = this;
30840         
30841         this.xhr.onload = function()
30842         {
30843             _this.xhrOnLoad(_this.xhr);
30844         }
30845         
30846         this.xhr.onerror = function()
30847         {
30848             _this.xhrOnError(_this.xhr);
30849         }
30850         
30851         var formData = new FormData();
30852
30853         formData.append('returnHTML', 'NO');
30854         
30855         if(crop){
30856             formData.append('crop', crop);
30857         }
30858         
30859         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30860             formData.append(this.paramName, file, file.name);
30861         }
30862         
30863         if(typeof(file.filename) != 'undefined'){
30864             formData.append('filename', file.filename);
30865         }
30866         
30867         if(typeof(file.mimetype) != 'undefined'){
30868             formData.append('mimetype', file.mimetype);
30869         }
30870         
30871         if(this.fireEvent('arrange', this, formData) != false){
30872             this.xhr.send(formData);
30873         };
30874     },
30875     
30876     xhrOnLoad : function(xhr)
30877     {
30878         if(this.loadMask){
30879             this.maskEl.unmask();
30880         }
30881         
30882         if (xhr.readyState !== 4) {
30883             this.fireEvent('exception', this, xhr);
30884             return;
30885         }
30886
30887         var response = Roo.decode(xhr.responseText);
30888         
30889         if(!response.success){
30890             this.fireEvent('exception', this, xhr);
30891             return;
30892         }
30893         
30894         var response = Roo.decode(xhr.responseText);
30895         
30896         this.fireEvent('upload', this, response);
30897         
30898     },
30899     
30900     xhrOnError : function()
30901     {
30902         if(this.loadMask){
30903             this.maskEl.unmask();
30904         }
30905         
30906         Roo.log('xhr on error');
30907         
30908         var response = Roo.decode(xhr.responseText);
30909           
30910         Roo.log(response);
30911         
30912     },
30913     
30914     prepare : function(file)
30915     {   
30916         if(this.loadMask){
30917             this.maskEl.mask(this.loadingText);
30918         }
30919         
30920         this.file = false;
30921         this.exif = {};
30922         
30923         if(typeof(file) === 'string'){
30924             this.loadCanvas(file);
30925             return;
30926         }
30927         
30928         if(!file || !this.urlAPI){
30929             return;
30930         }
30931         
30932         this.file = file;
30933         this.cropType = file.type;
30934         
30935         var _this = this;
30936         
30937         if(this.fireEvent('prepare', this, this.file) != false){
30938             
30939             var reader = new FileReader();
30940             
30941             reader.onload = function (e) {
30942                 if (e.target.error) {
30943                     Roo.log(e.target.error);
30944                     return;
30945                 }
30946                 
30947                 var buffer = e.target.result,
30948                     dataView = new DataView(buffer),
30949                     offset = 2,
30950                     maxOffset = dataView.byteLength - 4,
30951                     markerBytes,
30952                     markerLength;
30953                 
30954                 if (dataView.getUint16(0) === 0xffd8) {
30955                     while (offset < maxOffset) {
30956                         markerBytes = dataView.getUint16(offset);
30957                         
30958                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30959                             markerLength = dataView.getUint16(offset + 2) + 2;
30960                             if (offset + markerLength > dataView.byteLength) {
30961                                 Roo.log('Invalid meta data: Invalid segment size.');
30962                                 break;
30963                             }
30964                             
30965                             if(markerBytes == 0xffe1){
30966                                 _this.parseExifData(
30967                                     dataView,
30968                                     offset,
30969                                     markerLength
30970                                 );
30971                             }
30972                             
30973                             offset += markerLength;
30974                             
30975                             continue;
30976                         }
30977                         
30978                         break;
30979                     }
30980                     
30981                 }
30982                 
30983                 var url = _this.urlAPI.createObjectURL(_this.file);
30984                 
30985                 _this.loadCanvas(url);
30986                 
30987                 return;
30988             }
30989             
30990             reader.readAsArrayBuffer(this.file);
30991             
30992         }
30993         
30994     },
30995     
30996     parseExifData : function(dataView, offset, length)
30997     {
30998         var tiffOffset = offset + 10,
30999             littleEndian,
31000             dirOffset;
31001     
31002         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31003             // No Exif data, might be XMP data instead
31004             return;
31005         }
31006         
31007         // Check for the ASCII code for "Exif" (0x45786966):
31008         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31009             // No Exif data, might be XMP data instead
31010             return;
31011         }
31012         if (tiffOffset + 8 > dataView.byteLength) {
31013             Roo.log('Invalid Exif data: Invalid segment size.');
31014             return;
31015         }
31016         // Check for the two null bytes:
31017         if (dataView.getUint16(offset + 8) !== 0x0000) {
31018             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31019             return;
31020         }
31021         // Check the byte alignment:
31022         switch (dataView.getUint16(tiffOffset)) {
31023         case 0x4949:
31024             littleEndian = true;
31025             break;
31026         case 0x4D4D:
31027             littleEndian = false;
31028             break;
31029         default:
31030             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31031             return;
31032         }
31033         // Check for the TIFF tag marker (0x002A):
31034         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31035             Roo.log('Invalid Exif data: Missing TIFF marker.');
31036             return;
31037         }
31038         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31039         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31040         
31041         this.parseExifTags(
31042             dataView,
31043             tiffOffset,
31044             tiffOffset + dirOffset,
31045             littleEndian
31046         );
31047     },
31048     
31049     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31050     {
31051         var tagsNumber,
31052             dirEndOffset,
31053             i;
31054         if (dirOffset + 6 > dataView.byteLength) {
31055             Roo.log('Invalid Exif data: Invalid directory offset.');
31056             return;
31057         }
31058         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31059         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31060         if (dirEndOffset + 4 > dataView.byteLength) {
31061             Roo.log('Invalid Exif data: Invalid directory size.');
31062             return;
31063         }
31064         for (i = 0; i < tagsNumber; i += 1) {
31065             this.parseExifTag(
31066                 dataView,
31067                 tiffOffset,
31068                 dirOffset + 2 + 12 * i, // tag offset
31069                 littleEndian
31070             );
31071         }
31072         // Return the offset to the next directory:
31073         return dataView.getUint32(dirEndOffset, littleEndian);
31074     },
31075     
31076     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31077     {
31078         var tag = dataView.getUint16(offset, littleEndian);
31079         
31080         this.exif[tag] = this.getExifValue(
31081             dataView,
31082             tiffOffset,
31083             offset,
31084             dataView.getUint16(offset + 2, littleEndian), // tag type
31085             dataView.getUint32(offset + 4, littleEndian), // tag length
31086             littleEndian
31087         );
31088     },
31089     
31090     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31091     {
31092         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31093             tagSize,
31094             dataOffset,
31095             values,
31096             i,
31097             str,
31098             c;
31099     
31100         if (!tagType) {
31101             Roo.log('Invalid Exif data: Invalid tag type.');
31102             return;
31103         }
31104         
31105         tagSize = tagType.size * length;
31106         // Determine if the value is contained in the dataOffset bytes,
31107         // or if the value at the dataOffset is a pointer to the actual data:
31108         dataOffset = tagSize > 4 ?
31109                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31110         if (dataOffset + tagSize > dataView.byteLength) {
31111             Roo.log('Invalid Exif data: Invalid data offset.');
31112             return;
31113         }
31114         if (length === 1) {
31115             return tagType.getValue(dataView, dataOffset, littleEndian);
31116         }
31117         values = [];
31118         for (i = 0; i < length; i += 1) {
31119             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31120         }
31121         
31122         if (tagType.ascii) {
31123             str = '';
31124             // Concatenate the chars:
31125             for (i = 0; i < values.length; i += 1) {
31126                 c = values[i];
31127                 // Ignore the terminating NULL byte(s):
31128                 if (c === '\u0000') {
31129                     break;
31130                 }
31131                 str += c;
31132             }
31133             return str;
31134         }
31135         return values;
31136     }
31137     
31138 });
31139
31140 Roo.apply(Roo.bootstrap.UploadCropbox, {
31141     tags : {
31142         'Orientation': 0x0112
31143     },
31144     
31145     Orientation: {
31146             1: 0, //'top-left',
31147 //            2: 'top-right',
31148             3: 180, //'bottom-right',
31149 //            4: 'bottom-left',
31150 //            5: 'left-top',
31151             6: 90, //'right-top',
31152 //            7: 'right-bottom',
31153             8: 270 //'left-bottom'
31154     },
31155     
31156     exifTagTypes : {
31157         // byte, 8-bit unsigned int:
31158         1: {
31159             getValue: function (dataView, dataOffset) {
31160                 return dataView.getUint8(dataOffset);
31161             },
31162             size: 1
31163         },
31164         // ascii, 8-bit byte:
31165         2: {
31166             getValue: function (dataView, dataOffset) {
31167                 return String.fromCharCode(dataView.getUint8(dataOffset));
31168             },
31169             size: 1,
31170             ascii: true
31171         },
31172         // short, 16 bit int:
31173         3: {
31174             getValue: function (dataView, dataOffset, littleEndian) {
31175                 return dataView.getUint16(dataOffset, littleEndian);
31176             },
31177             size: 2
31178         },
31179         // long, 32 bit int:
31180         4: {
31181             getValue: function (dataView, dataOffset, littleEndian) {
31182                 return dataView.getUint32(dataOffset, littleEndian);
31183             },
31184             size: 4
31185         },
31186         // rational = two long values, first is numerator, second is denominator:
31187         5: {
31188             getValue: function (dataView, dataOffset, littleEndian) {
31189                 return dataView.getUint32(dataOffset, littleEndian) /
31190                     dataView.getUint32(dataOffset + 4, littleEndian);
31191             },
31192             size: 8
31193         },
31194         // slong, 32 bit signed int:
31195         9: {
31196             getValue: function (dataView, dataOffset, littleEndian) {
31197                 return dataView.getInt32(dataOffset, littleEndian);
31198             },
31199             size: 4
31200         },
31201         // srational, two slongs, first is numerator, second is denominator:
31202         10: {
31203             getValue: function (dataView, dataOffset, littleEndian) {
31204                 return dataView.getInt32(dataOffset, littleEndian) /
31205                     dataView.getInt32(dataOffset + 4, littleEndian);
31206             },
31207             size: 8
31208         }
31209     },
31210     
31211     footer : {
31212         STANDARD : [
31213             {
31214                 tag : 'div',
31215                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31216                 action : 'rotate-left',
31217                 cn : [
31218                     {
31219                         tag : 'button',
31220                         cls : 'btn btn-default',
31221                         html : '<i class="fa fa-undo"></i>'
31222                     }
31223                 ]
31224             },
31225             {
31226                 tag : 'div',
31227                 cls : 'btn-group roo-upload-cropbox-picture',
31228                 action : 'picture',
31229                 cn : [
31230                     {
31231                         tag : 'button',
31232                         cls : 'btn btn-default',
31233                         html : '<i class="fa fa-picture-o"></i>'
31234                     }
31235                 ]
31236             },
31237             {
31238                 tag : 'div',
31239                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31240                 action : 'rotate-right',
31241                 cn : [
31242                     {
31243                         tag : 'button',
31244                         cls : 'btn btn-default',
31245                         html : '<i class="fa fa-repeat"></i>'
31246                     }
31247                 ]
31248             }
31249         ],
31250         DOCUMENT : [
31251             {
31252                 tag : 'div',
31253                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31254                 action : 'rotate-left',
31255                 cn : [
31256                     {
31257                         tag : 'button',
31258                         cls : 'btn btn-default',
31259                         html : '<i class="fa fa-undo"></i>'
31260                     }
31261                 ]
31262             },
31263             {
31264                 tag : 'div',
31265                 cls : 'btn-group roo-upload-cropbox-download',
31266                 action : 'download',
31267                 cn : [
31268                     {
31269                         tag : 'button',
31270                         cls : 'btn btn-default',
31271                         html : '<i class="fa fa-download"></i>'
31272                     }
31273                 ]
31274             },
31275             {
31276                 tag : 'div',
31277                 cls : 'btn-group roo-upload-cropbox-crop',
31278                 action : 'crop',
31279                 cn : [
31280                     {
31281                         tag : 'button',
31282                         cls : 'btn btn-default',
31283                         html : '<i class="fa fa-crop"></i>'
31284                     }
31285                 ]
31286             },
31287             {
31288                 tag : 'div',
31289                 cls : 'btn-group roo-upload-cropbox-trash',
31290                 action : 'trash',
31291                 cn : [
31292                     {
31293                         tag : 'button',
31294                         cls : 'btn btn-default',
31295                         html : '<i class="fa fa-trash"></i>'
31296                     }
31297                 ]
31298             },
31299             {
31300                 tag : 'div',
31301                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31302                 action : 'rotate-right',
31303                 cn : [
31304                     {
31305                         tag : 'button',
31306                         cls : 'btn btn-default',
31307                         html : '<i class="fa fa-repeat"></i>'
31308                     }
31309                 ]
31310             }
31311         ],
31312         ROTATOR : [
31313             {
31314                 tag : 'div',
31315                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31316                 action : 'rotate-left',
31317                 cn : [
31318                     {
31319                         tag : 'button',
31320                         cls : 'btn btn-default',
31321                         html : '<i class="fa fa-undo"></i>'
31322                     }
31323                 ]
31324             },
31325             {
31326                 tag : 'div',
31327                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31328                 action : 'rotate-right',
31329                 cn : [
31330                     {
31331                         tag : 'button',
31332                         cls : 'btn btn-default',
31333                         html : '<i class="fa fa-repeat"></i>'
31334                     }
31335                 ]
31336             }
31337         ]
31338     }
31339 });
31340
31341 /*
31342 * Licence: LGPL
31343 */
31344
31345 /**
31346  * @class Roo.bootstrap.DocumentManager
31347  * @extends Roo.bootstrap.Component
31348  * Bootstrap DocumentManager class
31349  * @cfg {String} paramName default 'imageUpload'
31350  * @cfg {String} toolTipName default 'filename'
31351  * @cfg {String} method default POST
31352  * @cfg {String} url action url
31353  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31354  * @cfg {Boolean} multiple multiple upload default true
31355  * @cfg {Number} thumbSize default 300
31356  * @cfg {String} fieldLabel
31357  * @cfg {Number} labelWidth default 4
31358  * @cfg {String} labelAlign (left|top) default left
31359  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31360 * @cfg {Number} labellg set the width of label (1-12)
31361  * @cfg {Number} labelmd set the width of label (1-12)
31362  * @cfg {Number} labelsm set the width of label (1-12)
31363  * @cfg {Number} labelxs set the width of label (1-12)
31364  * 
31365  * @constructor
31366  * Create a new DocumentManager
31367  * @param {Object} config The config object
31368  */
31369
31370 Roo.bootstrap.DocumentManager = function(config){
31371     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31372     
31373     this.files = [];
31374     this.delegates = [];
31375     
31376     this.addEvents({
31377         /**
31378          * @event initial
31379          * Fire when initial the DocumentManager
31380          * @param {Roo.bootstrap.DocumentManager} this
31381          */
31382         "initial" : true,
31383         /**
31384          * @event inspect
31385          * inspect selected file
31386          * @param {Roo.bootstrap.DocumentManager} this
31387          * @param {File} file
31388          */
31389         "inspect" : true,
31390         /**
31391          * @event exception
31392          * Fire when xhr load exception
31393          * @param {Roo.bootstrap.DocumentManager} this
31394          * @param {XMLHttpRequest} xhr
31395          */
31396         "exception" : true,
31397         /**
31398          * @event afterupload
31399          * Fire when xhr load exception
31400          * @param {Roo.bootstrap.DocumentManager} this
31401          * @param {XMLHttpRequest} xhr
31402          */
31403         "afterupload" : true,
31404         /**
31405          * @event prepare
31406          * prepare the form data
31407          * @param {Roo.bootstrap.DocumentManager} this
31408          * @param {Object} formData
31409          */
31410         "prepare" : true,
31411         /**
31412          * @event remove
31413          * Fire when remove the file
31414          * @param {Roo.bootstrap.DocumentManager} this
31415          * @param {Object} file
31416          */
31417         "remove" : true,
31418         /**
31419          * @event refresh
31420          * Fire after refresh the file
31421          * @param {Roo.bootstrap.DocumentManager} this
31422          */
31423         "refresh" : true,
31424         /**
31425          * @event click
31426          * Fire after click the image
31427          * @param {Roo.bootstrap.DocumentManager} this
31428          * @param {Object} file
31429          */
31430         "click" : true,
31431         /**
31432          * @event edit
31433          * Fire when upload a image and editable set to true
31434          * @param {Roo.bootstrap.DocumentManager} this
31435          * @param {Object} file
31436          */
31437         "edit" : true,
31438         /**
31439          * @event beforeselectfile
31440          * Fire before select file
31441          * @param {Roo.bootstrap.DocumentManager} this
31442          */
31443         "beforeselectfile" : true,
31444         /**
31445          * @event process
31446          * Fire before process file
31447          * @param {Roo.bootstrap.DocumentManager} this
31448          * @param {Object} file
31449          */
31450         "process" : true,
31451         /**
31452          * @event previewrendered
31453          * Fire when preview rendered
31454          * @param {Roo.bootstrap.DocumentManager} this
31455          * @param {Object} file
31456          */
31457         "previewrendered" : true,
31458         /**
31459          */
31460         "previewResize" : true
31461         
31462     });
31463 };
31464
31465 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31466     
31467     boxes : 0,
31468     inputName : '',
31469     thumbSize : 300,
31470     multiple : true,
31471     files : false,
31472     method : 'POST',
31473     url : '',
31474     paramName : 'imageUpload',
31475     toolTipName : 'filename',
31476     fieldLabel : '',
31477     labelWidth : 4,
31478     labelAlign : 'left',
31479     editable : true,
31480     delegates : false,
31481     xhr : false, 
31482     
31483     labellg : 0,
31484     labelmd : 0,
31485     labelsm : 0,
31486     labelxs : 0,
31487     
31488     getAutoCreate : function()
31489     {   
31490         var managerWidget = {
31491             tag : 'div',
31492             cls : 'roo-document-manager',
31493             cn : [
31494                 {
31495                     tag : 'input',
31496                     cls : 'roo-document-manager-selector',
31497                     type : 'file'
31498                 },
31499                 {
31500                     tag : 'div',
31501                     cls : 'roo-document-manager-uploader',
31502                     cn : [
31503                         {
31504                             tag : 'div',
31505                             cls : 'roo-document-manager-upload-btn',
31506                             html : '<i class="fa fa-plus"></i>'
31507                         }
31508                     ]
31509                     
31510                 }
31511             ]
31512         };
31513         
31514         var content = [
31515             {
31516                 tag : 'div',
31517                 cls : 'column col-md-12',
31518                 cn : managerWidget
31519             }
31520         ];
31521         
31522         if(this.fieldLabel.length){
31523             
31524             content = [
31525                 {
31526                     tag : 'div',
31527                     cls : 'column col-md-12',
31528                     html : this.fieldLabel
31529                 },
31530                 {
31531                     tag : 'div',
31532                     cls : 'column col-md-12',
31533                     cn : managerWidget
31534                 }
31535             ];
31536
31537             if(this.labelAlign == 'left'){
31538                 content = [
31539                     {
31540                         tag : 'div',
31541                         cls : 'column',
31542                         html : this.fieldLabel
31543                     },
31544                     {
31545                         tag : 'div',
31546                         cls : 'column',
31547                         cn : managerWidget
31548                     }
31549                 ];
31550                 
31551                 if(this.labelWidth > 12){
31552                     content[0].style = "width: " + this.labelWidth + 'px';
31553                 }
31554
31555                 if(this.labelWidth < 13 && this.labelmd == 0){
31556                     this.labelmd = this.labelWidth;
31557                 }
31558
31559                 if(this.labellg > 0){
31560                     content[0].cls += ' col-lg-' + this.labellg;
31561                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31562                 }
31563
31564                 if(this.labelmd > 0){
31565                     content[0].cls += ' col-md-' + this.labelmd;
31566                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31567                 }
31568
31569                 if(this.labelsm > 0){
31570                     content[0].cls += ' col-sm-' + this.labelsm;
31571                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31572                 }
31573
31574                 if(this.labelxs > 0){
31575                     content[0].cls += ' col-xs-' + this.labelxs;
31576                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31577                 }
31578                 
31579             }
31580         }
31581         
31582         var cfg = {
31583             tag : 'div',
31584             cls : 'row clearfix',
31585             cn : content
31586         };
31587         
31588         return cfg;
31589         
31590     },
31591     
31592     initEvents : function()
31593     {
31594         this.managerEl = this.el.select('.roo-document-manager', true).first();
31595         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31596         
31597         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31598         this.selectorEl.hide();
31599         
31600         if(this.multiple){
31601             this.selectorEl.attr('multiple', 'multiple');
31602         }
31603         
31604         this.selectorEl.on('change', this.onFileSelected, this);
31605         
31606         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31607         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31608         
31609         this.uploader.on('click', this.onUploaderClick, this);
31610         
31611         this.renderProgressDialog();
31612         
31613         var _this = this;
31614         
31615         window.addEventListener("resize", function() { _this.refresh(); } );
31616         
31617         this.fireEvent('initial', this);
31618     },
31619     
31620     renderProgressDialog : function()
31621     {
31622         var _this = this;
31623         
31624         this.progressDialog = new Roo.bootstrap.Modal({
31625             cls : 'roo-document-manager-progress-dialog',
31626             allow_close : false,
31627             animate : false,
31628             title : '',
31629             buttons : [
31630                 {
31631                     name  :'cancel',
31632                     weight : 'danger',
31633                     html : 'Cancel'
31634                 }
31635             ], 
31636             listeners : { 
31637                 btnclick : function() {
31638                     _this.uploadCancel();
31639                     this.hide();
31640                 }
31641             }
31642         });
31643          
31644         this.progressDialog.render(Roo.get(document.body));
31645          
31646         this.progress = new Roo.bootstrap.Progress({
31647             cls : 'roo-document-manager-progress',
31648             active : true,
31649             striped : true
31650         });
31651         
31652         this.progress.render(this.progressDialog.getChildContainer());
31653         
31654         this.progressBar = new Roo.bootstrap.ProgressBar({
31655             cls : 'roo-document-manager-progress-bar',
31656             aria_valuenow : 0,
31657             aria_valuemin : 0,
31658             aria_valuemax : 12,
31659             panel : 'success'
31660         });
31661         
31662         this.progressBar.render(this.progress.getChildContainer());
31663     },
31664     
31665     onUploaderClick : function(e)
31666     {
31667         e.preventDefault();
31668      
31669         if(this.fireEvent('beforeselectfile', this) != false){
31670             this.selectorEl.dom.click();
31671         }
31672         
31673     },
31674     
31675     onFileSelected : function(e)
31676     {
31677         e.preventDefault();
31678         
31679         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31680             return;
31681         }
31682         
31683         Roo.each(this.selectorEl.dom.files, function(file){
31684             if(this.fireEvent('inspect', this, file) != false){
31685                 this.files.push(file);
31686             }
31687         }, this);
31688         
31689         this.queue();
31690         
31691     },
31692     
31693     queue : function()
31694     {
31695         this.selectorEl.dom.value = '';
31696         
31697         if(!this.files || !this.files.length){
31698             return;
31699         }
31700         
31701         if(this.boxes > 0 && this.files.length > this.boxes){
31702             this.files = this.files.slice(0, this.boxes);
31703         }
31704         
31705         this.uploader.show();
31706         
31707         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31708             this.uploader.hide();
31709         }
31710         
31711         var _this = this;
31712         
31713         var files = [];
31714         
31715         var docs = [];
31716         
31717         Roo.each(this.files, function(file){
31718             
31719             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31720                 var f = this.renderPreview(file);
31721                 files.push(f);
31722                 return;
31723             }
31724             
31725             if(file.type.indexOf('image') != -1){
31726                 this.delegates.push(
31727                     (function(){
31728                         _this.process(file);
31729                     }).createDelegate(this)
31730                 );
31731         
31732                 return;
31733             }
31734             
31735             docs.push(
31736                 (function(){
31737                     _this.process(file);
31738                 }).createDelegate(this)
31739             );
31740             
31741         }, this);
31742         
31743         this.files = files;
31744         
31745         this.delegates = this.delegates.concat(docs);
31746         
31747         if(!this.delegates.length){
31748             this.refresh();
31749             return;
31750         }
31751         
31752         this.progressBar.aria_valuemax = this.delegates.length;
31753         
31754         this.arrange();
31755         
31756         return;
31757     },
31758     
31759     arrange : function()
31760     {
31761         if(!this.delegates.length){
31762             this.progressDialog.hide();
31763             this.refresh();
31764             return;
31765         }
31766         
31767         var delegate = this.delegates.shift();
31768         
31769         this.progressDialog.show();
31770         
31771         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31772         
31773         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31774         
31775         delegate();
31776     },
31777     
31778     refresh : function()
31779     {
31780         this.uploader.show();
31781         
31782         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31783             this.uploader.hide();
31784         }
31785         
31786         Roo.isTouch ? this.closable(false) : this.closable(true);
31787         
31788         this.fireEvent('refresh', this);
31789     },
31790     
31791     onRemove : function(e, el, o)
31792     {
31793         e.preventDefault();
31794         
31795         this.fireEvent('remove', this, o);
31796         
31797     },
31798     
31799     remove : function(o)
31800     {
31801         var files = [];
31802         
31803         Roo.each(this.files, function(file){
31804             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31805                 files.push(file);
31806                 return;
31807             }
31808
31809             o.target.remove();
31810
31811         }, this);
31812         
31813         this.files = files;
31814         
31815         this.refresh();
31816     },
31817     
31818     clear : function()
31819     {
31820         Roo.each(this.files, function(file){
31821             if(!file.target){
31822                 return;
31823             }
31824             
31825             file.target.remove();
31826
31827         }, this);
31828         
31829         this.files = [];
31830         
31831         this.refresh();
31832     },
31833     
31834     onClick : function(e, el, o)
31835     {
31836         e.preventDefault();
31837         
31838         this.fireEvent('click', this, o);
31839         
31840     },
31841     
31842     closable : function(closable)
31843     {
31844         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31845             
31846             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31847             
31848             if(closable){
31849                 el.show();
31850                 return;
31851             }
31852             
31853             el.hide();
31854             
31855         }, this);
31856     },
31857     
31858     xhrOnLoad : function(xhr)
31859     {
31860         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31861             el.remove();
31862         }, this);
31863         
31864         if (xhr.readyState !== 4) {
31865             this.arrange();
31866             this.fireEvent('exception', this, xhr);
31867             return;
31868         }
31869
31870         var response = Roo.decode(xhr.responseText);
31871         
31872         if(!response.success){
31873             this.arrange();
31874             this.fireEvent('exception', this, xhr);
31875             return;
31876         }
31877         
31878         var file = this.renderPreview(response.data);
31879         
31880         this.files.push(file);
31881         
31882         this.arrange();
31883         
31884         this.fireEvent('afterupload', this, xhr);
31885         
31886     },
31887     
31888     xhrOnError : function(xhr)
31889     {
31890         Roo.log('xhr on error');
31891         
31892         var response = Roo.decode(xhr.responseText);
31893           
31894         Roo.log(response);
31895         
31896         this.arrange();
31897     },
31898     
31899     process : function(file)
31900     {
31901         if(this.fireEvent('process', this, file) !== false){
31902             if(this.editable && file.type.indexOf('image') != -1){
31903                 this.fireEvent('edit', this, file);
31904                 return;
31905             }
31906
31907             this.uploadStart(file, false);
31908
31909             return;
31910         }
31911         
31912     },
31913     
31914     uploadStart : function(file, crop)
31915     {
31916         this.xhr = new XMLHttpRequest();
31917         
31918         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31919             this.arrange();
31920             return;
31921         }
31922         
31923         file.xhr = this.xhr;
31924             
31925         this.managerEl.createChild({
31926             tag : 'div',
31927             cls : 'roo-document-manager-loading',
31928             cn : [
31929                 {
31930                     tag : 'div',
31931                     tooltip : file.name,
31932                     cls : 'roo-document-manager-thumb',
31933                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31934                 }
31935             ]
31936
31937         });
31938
31939         this.xhr.open(this.method, this.url, true);
31940         
31941         var headers = {
31942             "Accept": "application/json",
31943             "Cache-Control": "no-cache",
31944             "X-Requested-With": "XMLHttpRequest"
31945         };
31946         
31947         for (var headerName in headers) {
31948             var headerValue = headers[headerName];
31949             if (headerValue) {
31950                 this.xhr.setRequestHeader(headerName, headerValue);
31951             }
31952         }
31953         
31954         var _this = this;
31955         
31956         this.xhr.onload = function()
31957         {
31958             _this.xhrOnLoad(_this.xhr);
31959         }
31960         
31961         this.xhr.onerror = function()
31962         {
31963             _this.xhrOnError(_this.xhr);
31964         }
31965         
31966         var formData = new FormData();
31967
31968         formData.append('returnHTML', 'NO');
31969         
31970         if(crop){
31971             formData.append('crop', crop);
31972         }
31973         
31974         formData.append(this.paramName, file, file.name);
31975         
31976         var options = {
31977             file : file, 
31978             manually : false
31979         };
31980         
31981         if(this.fireEvent('prepare', this, formData, options) != false){
31982             
31983             if(options.manually){
31984                 return;
31985             }
31986             
31987             this.xhr.send(formData);
31988             return;
31989         };
31990         
31991         this.uploadCancel();
31992     },
31993     
31994     uploadCancel : function()
31995     {
31996         if (this.xhr) {
31997             this.xhr.abort();
31998         }
31999         
32000         this.delegates = [];
32001         
32002         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32003             el.remove();
32004         }, this);
32005         
32006         this.arrange();
32007     },
32008     
32009     renderPreview : function(file)
32010     {
32011         if(typeof(file.target) != 'undefined' && file.target){
32012             return file;
32013         }
32014         
32015         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32016         
32017         var previewEl = this.managerEl.createChild({
32018             tag : 'div',
32019             cls : 'roo-document-manager-preview',
32020             cn : [
32021                 {
32022                     tag : 'div',
32023                     tooltip : file[this.toolTipName],
32024                     cls : 'roo-document-manager-thumb',
32025                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32026                 },
32027                 {
32028                     tag : 'button',
32029                     cls : 'close',
32030                     html : '<i class="fa fa-times-circle"></i>'
32031                 }
32032             ]
32033         });
32034
32035         var close = previewEl.select('button.close', true).first();
32036
32037         close.on('click', this.onRemove, this, file);
32038
32039         file.target = previewEl;
32040
32041         var image = previewEl.select('img', true).first();
32042         
32043         var _this = this;
32044         
32045         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32046         
32047         image.on('click', this.onClick, this, file);
32048         
32049         this.fireEvent('previewrendered', this, file);
32050         
32051         return file;
32052         
32053     },
32054     
32055     onPreviewLoad : function(file, image)
32056     {
32057         if(typeof(file.target) == 'undefined' || !file.target){
32058             return;
32059         }
32060         
32061         var width = image.dom.naturalWidth || image.dom.width;
32062         var height = image.dom.naturalHeight || image.dom.height;
32063         
32064         if(!this.previewResize) {
32065             return;
32066         }
32067         
32068         if(width > height){
32069             file.target.addClass('wide');
32070             return;
32071         }
32072         
32073         file.target.addClass('tall');
32074         return;
32075         
32076     },
32077     
32078     uploadFromSource : function(file, crop)
32079     {
32080         this.xhr = new XMLHttpRequest();
32081         
32082         this.managerEl.createChild({
32083             tag : 'div',
32084             cls : 'roo-document-manager-loading',
32085             cn : [
32086                 {
32087                     tag : 'div',
32088                     tooltip : file.name,
32089                     cls : 'roo-document-manager-thumb',
32090                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32091                 }
32092             ]
32093
32094         });
32095
32096         this.xhr.open(this.method, this.url, true);
32097         
32098         var headers = {
32099             "Accept": "application/json",
32100             "Cache-Control": "no-cache",
32101             "X-Requested-With": "XMLHttpRequest"
32102         };
32103         
32104         for (var headerName in headers) {
32105             var headerValue = headers[headerName];
32106             if (headerValue) {
32107                 this.xhr.setRequestHeader(headerName, headerValue);
32108             }
32109         }
32110         
32111         var _this = this;
32112         
32113         this.xhr.onload = function()
32114         {
32115             _this.xhrOnLoad(_this.xhr);
32116         }
32117         
32118         this.xhr.onerror = function()
32119         {
32120             _this.xhrOnError(_this.xhr);
32121         }
32122         
32123         var formData = new FormData();
32124
32125         formData.append('returnHTML', 'NO');
32126         
32127         formData.append('crop', crop);
32128         
32129         if(typeof(file.filename) != 'undefined'){
32130             formData.append('filename', file.filename);
32131         }
32132         
32133         if(typeof(file.mimetype) != 'undefined'){
32134             formData.append('mimetype', file.mimetype);
32135         }
32136         
32137         Roo.log(formData);
32138         
32139         if(this.fireEvent('prepare', this, formData) != false){
32140             this.xhr.send(formData);
32141         };
32142     }
32143 });
32144
32145 /*
32146 * Licence: LGPL
32147 */
32148
32149 /**
32150  * @class Roo.bootstrap.DocumentViewer
32151  * @extends Roo.bootstrap.Component
32152  * Bootstrap DocumentViewer class
32153  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32154  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32155  * 
32156  * @constructor
32157  * Create a new DocumentViewer
32158  * @param {Object} config The config object
32159  */
32160
32161 Roo.bootstrap.DocumentViewer = function(config){
32162     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32163     
32164     this.addEvents({
32165         /**
32166          * @event initial
32167          * Fire after initEvent
32168          * @param {Roo.bootstrap.DocumentViewer} this
32169          */
32170         "initial" : true,
32171         /**
32172          * @event click
32173          * Fire after click
32174          * @param {Roo.bootstrap.DocumentViewer} this
32175          */
32176         "click" : true,
32177         /**
32178          * @event download
32179          * Fire after download button
32180          * @param {Roo.bootstrap.DocumentViewer} this
32181          */
32182         "download" : true,
32183         /**
32184          * @event trash
32185          * Fire after trash button
32186          * @param {Roo.bootstrap.DocumentViewer} this
32187          */
32188         "trash" : true
32189         
32190     });
32191 };
32192
32193 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32194     
32195     showDownload : true,
32196     
32197     showTrash : true,
32198     
32199     getAutoCreate : function()
32200     {
32201         var cfg = {
32202             tag : 'div',
32203             cls : 'roo-document-viewer',
32204             cn : [
32205                 {
32206                     tag : 'div',
32207                     cls : 'roo-document-viewer-body',
32208                     cn : [
32209                         {
32210                             tag : 'div',
32211                             cls : 'roo-document-viewer-thumb',
32212                             cn : [
32213                                 {
32214                                     tag : 'img',
32215                                     cls : 'roo-document-viewer-image'
32216                                 }
32217                             ]
32218                         }
32219                     ]
32220                 },
32221                 {
32222                     tag : 'div',
32223                     cls : 'roo-document-viewer-footer',
32224                     cn : {
32225                         tag : 'div',
32226                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32227                         cn : [
32228                             {
32229                                 tag : 'div',
32230                                 cls : 'btn-group roo-document-viewer-download',
32231                                 cn : [
32232                                     {
32233                                         tag : 'button',
32234                                         cls : 'btn btn-default',
32235                                         html : '<i class="fa fa-download"></i>'
32236                                     }
32237                                 ]
32238                             },
32239                             {
32240                                 tag : 'div',
32241                                 cls : 'btn-group roo-document-viewer-trash',
32242                                 cn : [
32243                                     {
32244                                         tag : 'button',
32245                                         cls : 'btn btn-default',
32246                                         html : '<i class="fa fa-trash"></i>'
32247                                     }
32248                                 ]
32249                             }
32250                         ]
32251                     }
32252                 }
32253             ]
32254         };
32255         
32256         return cfg;
32257     },
32258     
32259     initEvents : function()
32260     {
32261         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32262         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32263         
32264         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32265         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32266         
32267         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32268         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32269         
32270         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32271         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32272         
32273         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32274         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32275         
32276         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32277         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32278         
32279         this.bodyEl.on('click', this.onClick, this);
32280         this.downloadBtn.on('click', this.onDownload, this);
32281         this.trashBtn.on('click', this.onTrash, this);
32282         
32283         this.downloadBtn.hide();
32284         this.trashBtn.hide();
32285         
32286         if(this.showDownload){
32287             this.downloadBtn.show();
32288         }
32289         
32290         if(this.showTrash){
32291             this.trashBtn.show();
32292         }
32293         
32294         if(!this.showDownload && !this.showTrash) {
32295             this.footerEl.hide();
32296         }
32297         
32298     },
32299     
32300     initial : function()
32301     {
32302         this.fireEvent('initial', this);
32303         
32304     },
32305     
32306     onClick : function(e)
32307     {
32308         e.preventDefault();
32309         
32310         this.fireEvent('click', this);
32311     },
32312     
32313     onDownload : function(e)
32314     {
32315         e.preventDefault();
32316         
32317         this.fireEvent('download', this);
32318     },
32319     
32320     onTrash : function(e)
32321     {
32322         e.preventDefault();
32323         
32324         this.fireEvent('trash', this);
32325     }
32326     
32327 });
32328 /*
32329  * - LGPL
32330  *
32331  * nav progress bar
32332  * 
32333  */
32334
32335 /**
32336  * @class Roo.bootstrap.NavProgressBar
32337  * @extends Roo.bootstrap.Component
32338  * Bootstrap NavProgressBar class
32339  * 
32340  * @constructor
32341  * Create a new nav progress bar
32342  * @param {Object} config The config object
32343  */
32344
32345 Roo.bootstrap.NavProgressBar = function(config){
32346     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32347
32348     this.bullets = this.bullets || [];
32349    
32350 //    Roo.bootstrap.NavProgressBar.register(this);
32351      this.addEvents({
32352         /**
32353              * @event changed
32354              * Fires when the active item changes
32355              * @param {Roo.bootstrap.NavProgressBar} this
32356              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32357              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32358          */
32359         'changed': true
32360      });
32361     
32362 };
32363
32364 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32365     
32366     bullets : [],
32367     barItems : [],
32368     
32369     getAutoCreate : function()
32370     {
32371         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32372         
32373         cfg = {
32374             tag : 'div',
32375             cls : 'roo-navigation-bar-group',
32376             cn : [
32377                 {
32378                     tag : 'div',
32379                     cls : 'roo-navigation-top-bar'
32380                 },
32381                 {
32382                     tag : 'div',
32383                     cls : 'roo-navigation-bullets-bar',
32384                     cn : [
32385                         {
32386                             tag : 'ul',
32387                             cls : 'roo-navigation-bar'
32388                         }
32389                     ]
32390                 },
32391                 
32392                 {
32393                     tag : 'div',
32394                     cls : 'roo-navigation-bottom-bar'
32395                 }
32396             ]
32397             
32398         };
32399         
32400         return cfg;
32401         
32402     },
32403     
32404     initEvents: function() 
32405     {
32406         
32407     },
32408     
32409     onRender : function(ct, position) 
32410     {
32411         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32412         
32413         if(this.bullets.length){
32414             Roo.each(this.bullets, function(b){
32415                this.addItem(b);
32416             }, this);
32417         }
32418         
32419         this.format();
32420         
32421     },
32422     
32423     addItem : function(cfg)
32424     {
32425         var item = new Roo.bootstrap.NavProgressItem(cfg);
32426         
32427         item.parentId = this.id;
32428         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32429         
32430         if(cfg.html){
32431             var top = new Roo.bootstrap.Element({
32432                 tag : 'div',
32433                 cls : 'roo-navigation-bar-text'
32434             });
32435             
32436             var bottom = new Roo.bootstrap.Element({
32437                 tag : 'div',
32438                 cls : 'roo-navigation-bar-text'
32439             });
32440             
32441             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32442             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32443             
32444             var topText = new Roo.bootstrap.Element({
32445                 tag : 'span',
32446                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32447             });
32448             
32449             var bottomText = new Roo.bootstrap.Element({
32450                 tag : 'span',
32451                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32452             });
32453             
32454             topText.onRender(top.el, null);
32455             bottomText.onRender(bottom.el, null);
32456             
32457             item.topEl = top;
32458             item.bottomEl = bottom;
32459         }
32460         
32461         this.barItems.push(item);
32462         
32463         return item;
32464     },
32465     
32466     getActive : function()
32467     {
32468         var active = false;
32469         
32470         Roo.each(this.barItems, function(v){
32471             
32472             if (!v.isActive()) {
32473                 return;
32474             }
32475             
32476             active = v;
32477             return false;
32478             
32479         });
32480         
32481         return active;
32482     },
32483     
32484     setActiveItem : function(item)
32485     {
32486         var prev = false;
32487         
32488         Roo.each(this.barItems, function(v){
32489             if (v.rid == item.rid) {
32490                 return ;
32491             }
32492             
32493             if (v.isActive()) {
32494                 v.setActive(false);
32495                 prev = v;
32496             }
32497         });
32498
32499         item.setActive(true);
32500         
32501         this.fireEvent('changed', this, item, prev);
32502     },
32503     
32504     getBarItem: function(rid)
32505     {
32506         var ret = false;
32507         
32508         Roo.each(this.barItems, function(e) {
32509             if (e.rid != rid) {
32510                 return;
32511             }
32512             
32513             ret =  e;
32514             return false;
32515         });
32516         
32517         return ret;
32518     },
32519     
32520     indexOfItem : function(item)
32521     {
32522         var index = false;
32523         
32524         Roo.each(this.barItems, function(v, i){
32525             
32526             if (v.rid != item.rid) {
32527                 return;
32528             }
32529             
32530             index = i;
32531             return false
32532         });
32533         
32534         return index;
32535     },
32536     
32537     setActiveNext : function()
32538     {
32539         var i = this.indexOfItem(this.getActive());
32540         
32541         if (i > this.barItems.length) {
32542             return;
32543         }
32544         
32545         this.setActiveItem(this.barItems[i+1]);
32546     },
32547     
32548     setActivePrev : function()
32549     {
32550         var i = this.indexOfItem(this.getActive());
32551         
32552         if (i  < 1) {
32553             return;
32554         }
32555         
32556         this.setActiveItem(this.barItems[i-1]);
32557     },
32558     
32559     format : function()
32560     {
32561         if(!this.barItems.length){
32562             return;
32563         }
32564      
32565         var width = 100 / this.barItems.length;
32566         
32567         Roo.each(this.barItems, function(i){
32568             i.el.setStyle('width', width + '%');
32569             i.topEl.el.setStyle('width', width + '%');
32570             i.bottomEl.el.setStyle('width', width + '%');
32571         }, this);
32572         
32573     }
32574     
32575 });
32576 /*
32577  * - LGPL
32578  *
32579  * Nav Progress Item
32580  * 
32581  */
32582
32583 /**
32584  * @class Roo.bootstrap.NavProgressItem
32585  * @extends Roo.bootstrap.Component
32586  * Bootstrap NavProgressItem class
32587  * @cfg {String} rid the reference id
32588  * @cfg {Boolean} active (true|false) Is item active default false
32589  * @cfg {Boolean} disabled (true|false) Is item active default false
32590  * @cfg {String} html
32591  * @cfg {String} position (top|bottom) text position default bottom
32592  * @cfg {String} icon show icon instead of number
32593  * 
32594  * @constructor
32595  * Create a new NavProgressItem
32596  * @param {Object} config The config object
32597  */
32598 Roo.bootstrap.NavProgressItem = function(config){
32599     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32600     this.addEvents({
32601         // raw events
32602         /**
32603          * @event click
32604          * The raw click event for the entire grid.
32605          * @param {Roo.bootstrap.NavProgressItem} this
32606          * @param {Roo.EventObject} e
32607          */
32608         "click" : true
32609     });
32610    
32611 };
32612
32613 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32614     
32615     rid : '',
32616     active : false,
32617     disabled : false,
32618     html : '',
32619     position : 'bottom',
32620     icon : false,
32621     
32622     getAutoCreate : function()
32623     {
32624         var iconCls = 'roo-navigation-bar-item-icon';
32625         
32626         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32627         
32628         var cfg = {
32629             tag: 'li',
32630             cls: 'roo-navigation-bar-item',
32631             cn : [
32632                 {
32633                     tag : 'i',
32634                     cls : iconCls
32635                 }
32636             ]
32637         };
32638         
32639         if(this.active){
32640             cfg.cls += ' active';
32641         }
32642         if(this.disabled){
32643             cfg.cls += ' disabled';
32644         }
32645         
32646         return cfg;
32647     },
32648     
32649     disable : function()
32650     {
32651         this.setDisabled(true);
32652     },
32653     
32654     enable : function()
32655     {
32656         this.setDisabled(false);
32657     },
32658     
32659     initEvents: function() 
32660     {
32661         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32662         
32663         this.iconEl.on('click', this.onClick, this);
32664     },
32665     
32666     onClick : function(e)
32667     {
32668         e.preventDefault();
32669         
32670         if(this.disabled){
32671             return;
32672         }
32673         
32674         if(this.fireEvent('click', this, e) === false){
32675             return;
32676         };
32677         
32678         this.parent().setActiveItem(this);
32679     },
32680     
32681     isActive: function () 
32682     {
32683         return this.active;
32684     },
32685     
32686     setActive : function(state)
32687     {
32688         if(this.active == state){
32689             return;
32690         }
32691         
32692         this.active = state;
32693         
32694         if (state) {
32695             this.el.addClass('active');
32696             return;
32697         }
32698         
32699         this.el.removeClass('active');
32700         
32701         return;
32702     },
32703     
32704     setDisabled : function(state)
32705     {
32706         if(this.disabled == state){
32707             return;
32708         }
32709         
32710         this.disabled = state;
32711         
32712         if (state) {
32713             this.el.addClass('disabled');
32714             return;
32715         }
32716         
32717         this.el.removeClass('disabled');
32718     },
32719     
32720     tooltipEl : function()
32721     {
32722         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32723     }
32724 });
32725  
32726
32727  /*
32728  * - LGPL
32729  *
32730  * FieldLabel
32731  * 
32732  */
32733
32734 /**
32735  * @class Roo.bootstrap.FieldLabel
32736  * @extends Roo.bootstrap.Component
32737  * Bootstrap FieldLabel class
32738  * @cfg {String} html contents of the element
32739  * @cfg {String} tag tag of the element default label
32740  * @cfg {String} cls class of the element
32741  * @cfg {String} target label target 
32742  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32743  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32744  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32745  * @cfg {String} iconTooltip default "This field is required"
32746  * @cfg {String} indicatorpos (left|right) default left
32747  * 
32748  * @constructor
32749  * Create a new FieldLabel
32750  * @param {Object} config The config object
32751  */
32752
32753 Roo.bootstrap.FieldLabel = function(config){
32754     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32755     
32756     this.addEvents({
32757             /**
32758              * @event invalid
32759              * Fires after the field has been marked as invalid.
32760              * @param {Roo.form.FieldLabel} this
32761              * @param {String} msg The validation message
32762              */
32763             invalid : true,
32764             /**
32765              * @event valid
32766              * Fires after the field has been validated with no errors.
32767              * @param {Roo.form.FieldLabel} this
32768              */
32769             valid : true
32770         });
32771 };
32772
32773 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32774     
32775     tag: 'label',
32776     cls: '',
32777     html: '',
32778     target: '',
32779     allowBlank : true,
32780     invalidClass : 'has-warning',
32781     validClass : 'has-success',
32782     iconTooltip : 'This field is required',
32783     indicatorpos : 'left',
32784     
32785     getAutoCreate : function(){
32786         
32787         var cls = "";
32788         if (!this.allowBlank) {
32789             cls  = "visible";
32790         }
32791         
32792         var cfg = {
32793             tag : this.tag,
32794             cls : 'roo-bootstrap-field-label ' + this.cls,
32795             for : this.target,
32796             cn : [
32797                 {
32798                     tag : 'i',
32799                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32800                     tooltip : this.iconTooltip
32801                 },
32802                 {
32803                     tag : 'span',
32804                     html : this.html
32805                 }
32806             ] 
32807         };
32808         
32809         if(this.indicatorpos == 'right'){
32810             var cfg = {
32811                 tag : this.tag,
32812                 cls : 'roo-bootstrap-field-label ' + this.cls,
32813                 for : this.target,
32814                 cn : [
32815                     {
32816                         tag : 'span',
32817                         html : this.html
32818                     },
32819                     {
32820                         tag : 'i',
32821                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32822                         tooltip : this.iconTooltip
32823                     }
32824                 ] 
32825             };
32826         }
32827         
32828         return cfg;
32829     },
32830     
32831     initEvents: function() 
32832     {
32833         Roo.bootstrap.Element.superclass.initEvents.call(this);
32834         
32835         this.indicator = this.indicatorEl();
32836         
32837         if(this.indicator){
32838             this.indicator.removeClass('visible');
32839             this.indicator.addClass('invisible');
32840         }
32841         
32842         Roo.bootstrap.FieldLabel.register(this);
32843     },
32844     
32845     indicatorEl : function()
32846     {
32847         var indicator = this.el.select('i.roo-required-indicator',true).first();
32848         
32849         if(!indicator){
32850             return false;
32851         }
32852         
32853         return indicator;
32854         
32855     },
32856     
32857     /**
32858      * Mark this field as valid
32859      */
32860     markValid : function()
32861     {
32862         if(this.indicator){
32863             this.indicator.removeClass('visible');
32864             this.indicator.addClass('invisible');
32865         }
32866         if (Roo.bootstrap.version == 3) {
32867             this.el.removeClass(this.invalidClass);
32868             this.el.addClass(this.validClass);
32869         } else {
32870             this.el.removeClass('is-invalid');
32871             this.el.addClass('is-valid');
32872         }
32873         
32874         
32875         this.fireEvent('valid', this);
32876     },
32877     
32878     /**
32879      * Mark this field as invalid
32880      * @param {String} msg The validation message
32881      */
32882     markInvalid : function(msg)
32883     {
32884         if(this.indicator){
32885             this.indicator.removeClass('invisible');
32886             this.indicator.addClass('visible');
32887         }
32888           if (Roo.bootstrap.version == 3) {
32889             this.el.removeClass(this.validClass);
32890             this.el.addClass(this.invalidClass);
32891         } else {
32892             this.el.removeClass('is-valid');
32893             this.el.addClass('is-invalid');
32894         }
32895         
32896         
32897         this.fireEvent('invalid', this, msg);
32898     }
32899     
32900    
32901 });
32902
32903 Roo.apply(Roo.bootstrap.FieldLabel, {
32904     
32905     groups: {},
32906     
32907      /**
32908     * register a FieldLabel Group
32909     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32910     */
32911     register : function(label)
32912     {
32913         if(this.groups.hasOwnProperty(label.target)){
32914             return;
32915         }
32916      
32917         this.groups[label.target] = label;
32918         
32919     },
32920     /**
32921     * fetch a FieldLabel Group based on the target
32922     * @param {string} target
32923     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32924     */
32925     get: function(target) {
32926         if (typeof(this.groups[target]) == 'undefined') {
32927             return false;
32928         }
32929         
32930         return this.groups[target] ;
32931     }
32932 });
32933
32934  
32935
32936  /*
32937  * - LGPL
32938  *
32939  * page DateSplitField.
32940  * 
32941  */
32942
32943
32944 /**
32945  * @class Roo.bootstrap.DateSplitField
32946  * @extends Roo.bootstrap.Component
32947  * Bootstrap DateSplitField class
32948  * @cfg {string} fieldLabel - the label associated
32949  * @cfg {Number} labelWidth set the width of label (0-12)
32950  * @cfg {String} labelAlign (top|left)
32951  * @cfg {Boolean} dayAllowBlank (true|false) default false
32952  * @cfg {Boolean} monthAllowBlank (true|false) default false
32953  * @cfg {Boolean} yearAllowBlank (true|false) default false
32954  * @cfg {string} dayPlaceholder 
32955  * @cfg {string} monthPlaceholder
32956  * @cfg {string} yearPlaceholder
32957  * @cfg {string} dayFormat default 'd'
32958  * @cfg {string} monthFormat default 'm'
32959  * @cfg {string} yearFormat default 'Y'
32960  * @cfg {Number} labellg set the width of label (1-12)
32961  * @cfg {Number} labelmd set the width of label (1-12)
32962  * @cfg {Number} labelsm set the width of label (1-12)
32963  * @cfg {Number} labelxs set the width of label (1-12)
32964
32965  *     
32966  * @constructor
32967  * Create a new DateSplitField
32968  * @param {Object} config The config object
32969  */
32970
32971 Roo.bootstrap.DateSplitField = function(config){
32972     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32973     
32974     this.addEvents({
32975         // raw events
32976          /**
32977          * @event years
32978          * getting the data of years
32979          * @param {Roo.bootstrap.DateSplitField} this
32980          * @param {Object} years
32981          */
32982         "years" : true,
32983         /**
32984          * @event days
32985          * getting the data of days
32986          * @param {Roo.bootstrap.DateSplitField} this
32987          * @param {Object} days
32988          */
32989         "days" : true,
32990         /**
32991          * @event invalid
32992          * Fires after the field has been marked as invalid.
32993          * @param {Roo.form.Field} this
32994          * @param {String} msg The validation message
32995          */
32996         invalid : true,
32997        /**
32998          * @event valid
32999          * Fires after the field has been validated with no errors.
33000          * @param {Roo.form.Field} this
33001          */
33002         valid : true
33003     });
33004 };
33005
33006 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33007     
33008     fieldLabel : '',
33009     labelAlign : 'top',
33010     labelWidth : 3,
33011     dayAllowBlank : false,
33012     monthAllowBlank : false,
33013     yearAllowBlank : false,
33014     dayPlaceholder : '',
33015     monthPlaceholder : '',
33016     yearPlaceholder : '',
33017     dayFormat : 'd',
33018     monthFormat : 'm',
33019     yearFormat : 'Y',
33020     isFormField : true,
33021     labellg : 0,
33022     labelmd : 0,
33023     labelsm : 0,
33024     labelxs : 0,
33025     
33026     getAutoCreate : function()
33027     {
33028         var cfg = {
33029             tag : 'div',
33030             cls : 'row roo-date-split-field-group',
33031             cn : [
33032                 {
33033                     tag : 'input',
33034                     type : 'hidden',
33035                     cls : 'form-hidden-field roo-date-split-field-group-value',
33036                     name : this.name
33037                 }
33038             ]
33039         };
33040         
33041         var labelCls = 'col-md-12';
33042         var contentCls = 'col-md-4';
33043         
33044         if(this.fieldLabel){
33045             
33046             var label = {
33047                 tag : 'div',
33048                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33049                 cn : [
33050                     {
33051                         tag : 'label',
33052                         html : this.fieldLabel
33053                     }
33054                 ]
33055             };
33056             
33057             if(this.labelAlign == 'left'){
33058             
33059                 if(this.labelWidth > 12){
33060                     label.style = "width: " + this.labelWidth + 'px';
33061                 }
33062
33063                 if(this.labelWidth < 13 && this.labelmd == 0){
33064                     this.labelmd = this.labelWidth;
33065                 }
33066
33067                 if(this.labellg > 0){
33068                     labelCls = ' col-lg-' + this.labellg;
33069                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33070                 }
33071
33072                 if(this.labelmd > 0){
33073                     labelCls = ' col-md-' + this.labelmd;
33074                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33075                 }
33076
33077                 if(this.labelsm > 0){
33078                     labelCls = ' col-sm-' + this.labelsm;
33079                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33080                 }
33081
33082                 if(this.labelxs > 0){
33083                     labelCls = ' col-xs-' + this.labelxs;
33084                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33085                 }
33086             }
33087             
33088             label.cls += ' ' + labelCls;
33089             
33090             cfg.cn.push(label);
33091         }
33092         
33093         Roo.each(['day', 'month', 'year'], function(t){
33094             cfg.cn.push({
33095                 tag : 'div',
33096                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33097             });
33098         }, this);
33099         
33100         return cfg;
33101     },
33102     
33103     inputEl: function ()
33104     {
33105         return this.el.select('.roo-date-split-field-group-value', true).first();
33106     },
33107     
33108     onRender : function(ct, position) 
33109     {
33110         var _this = this;
33111         
33112         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33113         
33114         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33115         
33116         this.dayField = new Roo.bootstrap.ComboBox({
33117             allowBlank : this.dayAllowBlank,
33118             alwaysQuery : true,
33119             displayField : 'value',
33120             editable : false,
33121             fieldLabel : '',
33122             forceSelection : true,
33123             mode : 'local',
33124             placeholder : this.dayPlaceholder,
33125             selectOnFocus : true,
33126             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33127             triggerAction : 'all',
33128             typeAhead : true,
33129             valueField : 'value',
33130             store : new Roo.data.SimpleStore({
33131                 data : (function() {    
33132                     var days = [];
33133                     _this.fireEvent('days', _this, days);
33134                     return days;
33135                 })(),
33136                 fields : [ 'value' ]
33137             }),
33138             listeners : {
33139                 select : function (_self, record, index)
33140                 {
33141                     _this.setValue(_this.getValue());
33142                 }
33143             }
33144         });
33145
33146         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33147         
33148         this.monthField = new Roo.bootstrap.MonthField({
33149             after : '<i class=\"fa fa-calendar\"></i>',
33150             allowBlank : this.monthAllowBlank,
33151             placeholder : this.monthPlaceholder,
33152             readOnly : true,
33153             listeners : {
33154                 render : function (_self)
33155                 {
33156                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33157                         e.preventDefault();
33158                         _self.focus();
33159                     });
33160                 },
33161                 select : function (_self, oldvalue, newvalue)
33162                 {
33163                     _this.setValue(_this.getValue());
33164                 }
33165             }
33166         });
33167         
33168         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33169         
33170         this.yearField = new Roo.bootstrap.ComboBox({
33171             allowBlank : this.yearAllowBlank,
33172             alwaysQuery : true,
33173             displayField : 'value',
33174             editable : false,
33175             fieldLabel : '',
33176             forceSelection : true,
33177             mode : 'local',
33178             placeholder : this.yearPlaceholder,
33179             selectOnFocus : true,
33180             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33181             triggerAction : 'all',
33182             typeAhead : true,
33183             valueField : 'value',
33184             store : new Roo.data.SimpleStore({
33185                 data : (function() {
33186                     var years = [];
33187                     _this.fireEvent('years', _this, years);
33188                     return years;
33189                 })(),
33190                 fields : [ 'value' ]
33191             }),
33192             listeners : {
33193                 select : function (_self, record, index)
33194                 {
33195                     _this.setValue(_this.getValue());
33196                 }
33197             }
33198         });
33199
33200         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33201     },
33202     
33203     setValue : function(v, format)
33204     {
33205         this.inputEl.dom.value = v;
33206         
33207         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33208         
33209         var d = Date.parseDate(v, f);
33210         
33211         if(!d){
33212             this.validate();
33213             return;
33214         }
33215         
33216         this.setDay(d.format(this.dayFormat));
33217         this.setMonth(d.format(this.monthFormat));
33218         this.setYear(d.format(this.yearFormat));
33219         
33220         this.validate();
33221         
33222         return;
33223     },
33224     
33225     setDay : function(v)
33226     {
33227         this.dayField.setValue(v);
33228         this.inputEl.dom.value = this.getValue();
33229         this.validate();
33230         return;
33231     },
33232     
33233     setMonth : function(v)
33234     {
33235         this.monthField.setValue(v, true);
33236         this.inputEl.dom.value = this.getValue();
33237         this.validate();
33238         return;
33239     },
33240     
33241     setYear : function(v)
33242     {
33243         this.yearField.setValue(v);
33244         this.inputEl.dom.value = this.getValue();
33245         this.validate();
33246         return;
33247     },
33248     
33249     getDay : function()
33250     {
33251         return this.dayField.getValue();
33252     },
33253     
33254     getMonth : function()
33255     {
33256         return this.monthField.getValue();
33257     },
33258     
33259     getYear : function()
33260     {
33261         return this.yearField.getValue();
33262     },
33263     
33264     getValue : function()
33265     {
33266         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33267         
33268         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33269         
33270         return date;
33271     },
33272     
33273     reset : function()
33274     {
33275         this.setDay('');
33276         this.setMonth('');
33277         this.setYear('');
33278         this.inputEl.dom.value = '';
33279         this.validate();
33280         return;
33281     },
33282     
33283     validate : function()
33284     {
33285         var d = this.dayField.validate();
33286         var m = this.monthField.validate();
33287         var y = this.yearField.validate();
33288         
33289         var valid = true;
33290         
33291         if(
33292                 (!this.dayAllowBlank && !d) ||
33293                 (!this.monthAllowBlank && !m) ||
33294                 (!this.yearAllowBlank && !y)
33295         ){
33296             valid = false;
33297         }
33298         
33299         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33300             return valid;
33301         }
33302         
33303         if(valid){
33304             this.markValid();
33305             return valid;
33306         }
33307         
33308         this.markInvalid();
33309         
33310         return valid;
33311     },
33312     
33313     markValid : function()
33314     {
33315         
33316         var label = this.el.select('label', true).first();
33317         var icon = this.el.select('i.fa-star', true).first();
33318
33319         if(label && icon){
33320             icon.remove();
33321         }
33322         
33323         this.fireEvent('valid', this);
33324     },
33325     
33326      /**
33327      * Mark this field as invalid
33328      * @param {String} msg The validation message
33329      */
33330     markInvalid : function(msg)
33331     {
33332         
33333         var label = this.el.select('label', true).first();
33334         var icon = this.el.select('i.fa-star', true).first();
33335
33336         if(label && !icon){
33337             this.el.select('.roo-date-split-field-label', true).createChild({
33338                 tag : 'i',
33339                 cls : 'text-danger fa fa-lg fa-star',
33340                 tooltip : 'This field is required',
33341                 style : 'margin-right:5px;'
33342             }, label, true);
33343         }
33344         
33345         this.fireEvent('invalid', this, msg);
33346     },
33347     
33348     clearInvalid : function()
33349     {
33350         var label = this.el.select('label', true).first();
33351         var icon = this.el.select('i.fa-star', true).first();
33352
33353         if(label && icon){
33354             icon.remove();
33355         }
33356         
33357         this.fireEvent('valid', this);
33358     },
33359     
33360     getName: function()
33361     {
33362         return this.name;
33363     }
33364     
33365 });
33366
33367  /**
33368  *
33369  * This is based on 
33370  * http://masonry.desandro.com
33371  *
33372  * The idea is to render all the bricks based on vertical width...
33373  *
33374  * The original code extends 'outlayer' - we might need to use that....
33375  * 
33376  */
33377
33378
33379 /**
33380  * @class Roo.bootstrap.LayoutMasonry
33381  * @extends Roo.bootstrap.Component
33382  * Bootstrap Layout Masonry class
33383  * 
33384  * @constructor
33385  * Create a new Element
33386  * @param {Object} config The config object
33387  */
33388
33389 Roo.bootstrap.LayoutMasonry = function(config){
33390     
33391     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33392     
33393     this.bricks = [];
33394     
33395     Roo.bootstrap.LayoutMasonry.register(this);
33396     
33397     this.addEvents({
33398         // raw events
33399         /**
33400          * @event layout
33401          * Fire after layout the items
33402          * @param {Roo.bootstrap.LayoutMasonry} this
33403          * @param {Roo.EventObject} e
33404          */
33405         "layout" : true
33406     });
33407     
33408 };
33409
33410 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33411     
33412     /**
33413      * @cfg {Boolean} isLayoutInstant = no animation?
33414      */   
33415     isLayoutInstant : false, // needed?
33416    
33417     /**
33418      * @cfg {Number} boxWidth  width of the columns
33419      */   
33420     boxWidth : 450,
33421     
33422       /**
33423      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33424      */   
33425     boxHeight : 0,
33426     
33427     /**
33428      * @cfg {Number} padWidth padding below box..
33429      */   
33430     padWidth : 10, 
33431     
33432     /**
33433      * @cfg {Number} gutter gutter width..
33434      */   
33435     gutter : 10,
33436     
33437      /**
33438      * @cfg {Number} maxCols maximum number of columns
33439      */   
33440     
33441     maxCols: 0,
33442     
33443     /**
33444      * @cfg {Boolean} isAutoInitial defalut true
33445      */   
33446     isAutoInitial : true, 
33447     
33448     containerWidth: 0,
33449     
33450     /**
33451      * @cfg {Boolean} isHorizontal defalut false
33452      */   
33453     isHorizontal : false, 
33454
33455     currentSize : null,
33456     
33457     tag: 'div',
33458     
33459     cls: '',
33460     
33461     bricks: null, //CompositeElement
33462     
33463     cols : 1,
33464     
33465     _isLayoutInited : false,
33466     
33467 //    isAlternative : false, // only use for vertical layout...
33468     
33469     /**
33470      * @cfg {Number} alternativePadWidth padding below box..
33471      */   
33472     alternativePadWidth : 50,
33473     
33474     selectedBrick : [],
33475     
33476     getAutoCreate : function(){
33477         
33478         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33479         
33480         var cfg = {
33481             tag: this.tag,
33482             cls: 'blog-masonary-wrapper ' + this.cls,
33483             cn : {
33484                 cls : 'mas-boxes masonary'
33485             }
33486         };
33487         
33488         return cfg;
33489     },
33490     
33491     getChildContainer: function( )
33492     {
33493         if (this.boxesEl) {
33494             return this.boxesEl;
33495         }
33496         
33497         this.boxesEl = this.el.select('.mas-boxes').first();
33498         
33499         return this.boxesEl;
33500     },
33501     
33502     
33503     initEvents : function()
33504     {
33505         var _this = this;
33506         
33507         if(this.isAutoInitial){
33508             Roo.log('hook children rendered');
33509             this.on('childrenrendered', function() {
33510                 Roo.log('children rendered');
33511                 _this.initial();
33512             } ,this);
33513         }
33514     },
33515     
33516     initial : function()
33517     {
33518         this.selectedBrick = [];
33519         
33520         this.currentSize = this.el.getBox(true);
33521         
33522         Roo.EventManager.onWindowResize(this.resize, this); 
33523
33524         if(!this.isAutoInitial){
33525             this.layout();
33526             return;
33527         }
33528         
33529         this.layout();
33530         
33531         return;
33532         //this.layout.defer(500,this);
33533         
33534     },
33535     
33536     resize : function()
33537     {
33538         var cs = this.el.getBox(true);
33539         
33540         if (
33541                 this.currentSize.width == cs.width && 
33542                 this.currentSize.x == cs.x && 
33543                 this.currentSize.height == cs.height && 
33544                 this.currentSize.y == cs.y 
33545         ) {
33546             Roo.log("no change in with or X or Y");
33547             return;
33548         }
33549         
33550         this.currentSize = cs;
33551         
33552         this.layout();
33553         
33554     },
33555     
33556     layout : function()
33557     {   
33558         this._resetLayout();
33559         
33560         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33561         
33562         this.layoutItems( isInstant );
33563       
33564         this._isLayoutInited = true;
33565         
33566         this.fireEvent('layout', this);
33567         
33568     },
33569     
33570     _resetLayout : function()
33571     {
33572         if(this.isHorizontal){
33573             this.horizontalMeasureColumns();
33574             return;
33575         }
33576         
33577         this.verticalMeasureColumns();
33578         
33579     },
33580     
33581     verticalMeasureColumns : function()
33582     {
33583         this.getContainerWidth();
33584         
33585 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33586 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33587 //            return;
33588 //        }
33589         
33590         var boxWidth = this.boxWidth + this.padWidth;
33591         
33592         if(this.containerWidth < this.boxWidth){
33593             boxWidth = this.containerWidth
33594         }
33595         
33596         var containerWidth = this.containerWidth;
33597         
33598         var cols = Math.floor(containerWidth / boxWidth);
33599         
33600         this.cols = Math.max( cols, 1 );
33601         
33602         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33603         
33604         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33605         
33606         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33607         
33608         this.colWidth = boxWidth + avail - this.padWidth;
33609         
33610         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33611         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33612     },
33613     
33614     horizontalMeasureColumns : function()
33615     {
33616         this.getContainerWidth();
33617         
33618         var boxWidth = this.boxWidth;
33619         
33620         if(this.containerWidth < boxWidth){
33621             boxWidth = this.containerWidth;
33622         }
33623         
33624         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33625         
33626         this.el.setHeight(boxWidth);
33627         
33628     },
33629     
33630     getContainerWidth : function()
33631     {
33632         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33633     },
33634     
33635     layoutItems : function( isInstant )
33636     {
33637         Roo.log(this.bricks);
33638         
33639         var items = Roo.apply([], this.bricks);
33640         
33641         if(this.isHorizontal){
33642             this._horizontalLayoutItems( items , isInstant );
33643             return;
33644         }
33645         
33646 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33647 //            this._verticalAlternativeLayoutItems( items , isInstant );
33648 //            return;
33649 //        }
33650         
33651         this._verticalLayoutItems( items , isInstant );
33652         
33653     },
33654     
33655     _verticalLayoutItems : function ( items , isInstant)
33656     {
33657         if ( !items || !items.length ) {
33658             return;
33659         }
33660         
33661         var standard = [
33662             ['xs', 'xs', 'xs', 'tall'],
33663             ['xs', 'xs', 'tall'],
33664             ['xs', 'xs', 'sm'],
33665             ['xs', 'xs', 'xs'],
33666             ['xs', 'tall'],
33667             ['xs', 'sm'],
33668             ['xs', 'xs'],
33669             ['xs'],
33670             
33671             ['sm', 'xs', 'xs'],
33672             ['sm', 'xs'],
33673             ['sm'],
33674             
33675             ['tall', 'xs', 'xs', 'xs'],
33676             ['tall', 'xs', 'xs'],
33677             ['tall', 'xs'],
33678             ['tall']
33679             
33680         ];
33681         
33682         var queue = [];
33683         
33684         var boxes = [];
33685         
33686         var box = [];
33687         
33688         Roo.each(items, function(item, k){
33689             
33690             switch (item.size) {
33691                 // these layouts take up a full box,
33692                 case 'md' :
33693                 case 'md-left' :
33694                 case 'md-right' :
33695                 case 'wide' :
33696                     
33697                     if(box.length){
33698                         boxes.push(box);
33699                         box = [];
33700                     }
33701                     
33702                     boxes.push([item]);
33703                     
33704                     break;
33705                     
33706                 case 'xs' :
33707                 case 'sm' :
33708                 case 'tall' :
33709                     
33710                     box.push(item);
33711                     
33712                     break;
33713                 default :
33714                     break;
33715                     
33716             }
33717             
33718         }, this);
33719         
33720         if(box.length){
33721             boxes.push(box);
33722             box = [];
33723         }
33724         
33725         var filterPattern = function(box, length)
33726         {
33727             if(!box.length){
33728                 return;
33729             }
33730             
33731             var match = false;
33732             
33733             var pattern = box.slice(0, length);
33734             
33735             var format = [];
33736             
33737             Roo.each(pattern, function(i){
33738                 format.push(i.size);
33739             }, this);
33740             
33741             Roo.each(standard, function(s){
33742                 
33743                 if(String(s) != String(format)){
33744                     return;
33745                 }
33746                 
33747                 match = true;
33748                 return false;
33749                 
33750             }, this);
33751             
33752             if(!match && length == 1){
33753                 return;
33754             }
33755             
33756             if(!match){
33757                 filterPattern(box, length - 1);
33758                 return;
33759             }
33760                 
33761             queue.push(pattern);
33762
33763             box = box.slice(length, box.length);
33764
33765             filterPattern(box, 4);
33766
33767             return;
33768             
33769         }
33770         
33771         Roo.each(boxes, function(box, k){
33772             
33773             if(!box.length){
33774                 return;
33775             }
33776             
33777             if(box.length == 1){
33778                 queue.push(box);
33779                 return;
33780             }
33781             
33782             filterPattern(box, 4);
33783             
33784         }, this);
33785         
33786         this._processVerticalLayoutQueue( queue, isInstant );
33787         
33788     },
33789     
33790 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33791 //    {
33792 //        if ( !items || !items.length ) {
33793 //            return;
33794 //        }
33795 //
33796 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33797 //        
33798 //    },
33799     
33800     _horizontalLayoutItems : function ( items , isInstant)
33801     {
33802         if ( !items || !items.length || items.length < 3) {
33803             return;
33804         }
33805         
33806         items.reverse();
33807         
33808         var eItems = items.slice(0, 3);
33809         
33810         items = items.slice(3, items.length);
33811         
33812         var standard = [
33813             ['xs', 'xs', 'xs', 'wide'],
33814             ['xs', 'xs', 'wide'],
33815             ['xs', 'xs', 'sm'],
33816             ['xs', 'xs', 'xs'],
33817             ['xs', 'wide'],
33818             ['xs', 'sm'],
33819             ['xs', 'xs'],
33820             ['xs'],
33821             
33822             ['sm', 'xs', 'xs'],
33823             ['sm', 'xs'],
33824             ['sm'],
33825             
33826             ['wide', 'xs', 'xs', 'xs'],
33827             ['wide', 'xs', 'xs'],
33828             ['wide', 'xs'],
33829             ['wide'],
33830             
33831             ['wide-thin']
33832         ];
33833         
33834         var queue = [];
33835         
33836         var boxes = [];
33837         
33838         var box = [];
33839         
33840         Roo.each(items, function(item, k){
33841             
33842             switch (item.size) {
33843                 case 'md' :
33844                 case 'md-left' :
33845                 case 'md-right' :
33846                 case 'tall' :
33847                     
33848                     if(box.length){
33849                         boxes.push(box);
33850                         box = [];
33851                     }
33852                     
33853                     boxes.push([item]);
33854                     
33855                     break;
33856                     
33857                 case 'xs' :
33858                 case 'sm' :
33859                 case 'wide' :
33860                 case 'wide-thin' :
33861                     
33862                     box.push(item);
33863                     
33864                     break;
33865                 default :
33866                     break;
33867                     
33868             }
33869             
33870         }, this);
33871         
33872         if(box.length){
33873             boxes.push(box);
33874             box = [];
33875         }
33876         
33877         var filterPattern = function(box, length)
33878         {
33879             if(!box.length){
33880                 return;
33881             }
33882             
33883             var match = false;
33884             
33885             var pattern = box.slice(0, length);
33886             
33887             var format = [];
33888             
33889             Roo.each(pattern, function(i){
33890                 format.push(i.size);
33891             }, this);
33892             
33893             Roo.each(standard, function(s){
33894                 
33895                 if(String(s) != String(format)){
33896                     return;
33897                 }
33898                 
33899                 match = true;
33900                 return false;
33901                 
33902             }, this);
33903             
33904             if(!match && length == 1){
33905                 return;
33906             }
33907             
33908             if(!match){
33909                 filterPattern(box, length - 1);
33910                 return;
33911             }
33912                 
33913             queue.push(pattern);
33914
33915             box = box.slice(length, box.length);
33916
33917             filterPattern(box, 4);
33918
33919             return;
33920             
33921         }
33922         
33923         Roo.each(boxes, function(box, k){
33924             
33925             if(!box.length){
33926                 return;
33927             }
33928             
33929             if(box.length == 1){
33930                 queue.push(box);
33931                 return;
33932             }
33933             
33934             filterPattern(box, 4);
33935             
33936         }, this);
33937         
33938         
33939         var prune = [];
33940         
33941         var pos = this.el.getBox(true);
33942         
33943         var minX = pos.x;
33944         
33945         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33946         
33947         var hit_end = false;
33948         
33949         Roo.each(queue, function(box){
33950             
33951             if(hit_end){
33952                 
33953                 Roo.each(box, function(b){
33954                 
33955                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33956                     b.el.hide();
33957
33958                 }, this);
33959
33960                 return;
33961             }
33962             
33963             var mx = 0;
33964             
33965             Roo.each(box, function(b){
33966                 
33967                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33968                 b.el.show();
33969
33970                 mx = Math.max(mx, b.x);
33971                 
33972             }, this);
33973             
33974             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33975             
33976             if(maxX < minX){
33977                 
33978                 Roo.each(box, function(b){
33979                 
33980                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33981                     b.el.hide();
33982                     
33983                 }, this);
33984                 
33985                 hit_end = true;
33986                 
33987                 return;
33988             }
33989             
33990             prune.push(box);
33991             
33992         }, this);
33993         
33994         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33995     },
33996     
33997     /** Sets position of item in DOM
33998     * @param {Element} item
33999     * @param {Number} x - horizontal position
34000     * @param {Number} y - vertical position
34001     * @param {Boolean} isInstant - disables transitions
34002     */
34003     _processVerticalLayoutQueue : function( queue, isInstant )
34004     {
34005         var pos = this.el.getBox(true);
34006         var x = pos.x;
34007         var y = pos.y;
34008         var maxY = [];
34009         
34010         for (var i = 0; i < this.cols; i++){
34011             maxY[i] = pos.y;
34012         }
34013         
34014         Roo.each(queue, function(box, k){
34015             
34016             var col = k % this.cols;
34017             
34018             Roo.each(box, function(b,kk){
34019                 
34020                 b.el.position('absolute');
34021                 
34022                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34023                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34024                 
34025                 if(b.size == 'md-left' || b.size == 'md-right'){
34026                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34027                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34028                 }
34029                 
34030                 b.el.setWidth(width);
34031                 b.el.setHeight(height);
34032                 // iframe?
34033                 b.el.select('iframe',true).setSize(width,height);
34034                 
34035             }, this);
34036             
34037             for (var i = 0; i < this.cols; i++){
34038                 
34039                 if(maxY[i] < maxY[col]){
34040                     col = i;
34041                     continue;
34042                 }
34043                 
34044                 col = Math.min(col, i);
34045                 
34046             }
34047             
34048             x = pos.x + col * (this.colWidth + this.padWidth);
34049             
34050             y = maxY[col];
34051             
34052             var positions = [];
34053             
34054             switch (box.length){
34055                 case 1 :
34056                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34057                     break;
34058                 case 2 :
34059                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34060                     break;
34061                 case 3 :
34062                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34063                     break;
34064                 case 4 :
34065                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34066                     break;
34067                 default :
34068                     break;
34069             }
34070             
34071             Roo.each(box, function(b,kk){
34072                 
34073                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34074                 
34075                 var sz = b.el.getSize();
34076                 
34077                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34078                 
34079             }, this);
34080             
34081         }, this);
34082         
34083         var mY = 0;
34084         
34085         for (var i = 0; i < this.cols; i++){
34086             mY = Math.max(mY, maxY[i]);
34087         }
34088         
34089         this.el.setHeight(mY - pos.y);
34090         
34091     },
34092     
34093 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34094 //    {
34095 //        var pos = this.el.getBox(true);
34096 //        var x = pos.x;
34097 //        var y = pos.y;
34098 //        var maxX = pos.right;
34099 //        
34100 //        var maxHeight = 0;
34101 //        
34102 //        Roo.each(items, function(item, k){
34103 //            
34104 //            var c = k % 2;
34105 //            
34106 //            item.el.position('absolute');
34107 //                
34108 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34109 //
34110 //            item.el.setWidth(width);
34111 //
34112 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34113 //
34114 //            item.el.setHeight(height);
34115 //            
34116 //            if(c == 0){
34117 //                item.el.setXY([x, y], isInstant ? false : true);
34118 //            } else {
34119 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34120 //            }
34121 //            
34122 //            y = y + height + this.alternativePadWidth;
34123 //            
34124 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34125 //            
34126 //        }, this);
34127 //        
34128 //        this.el.setHeight(maxHeight);
34129 //        
34130 //    },
34131     
34132     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34133     {
34134         var pos = this.el.getBox(true);
34135         
34136         var minX = pos.x;
34137         var minY = pos.y;
34138         
34139         var maxX = pos.right;
34140         
34141         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34142         
34143         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34144         
34145         Roo.each(queue, function(box, k){
34146             
34147             Roo.each(box, function(b, kk){
34148                 
34149                 b.el.position('absolute');
34150                 
34151                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34152                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34153                 
34154                 if(b.size == 'md-left' || b.size == 'md-right'){
34155                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34156                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34157                 }
34158                 
34159                 b.el.setWidth(width);
34160                 b.el.setHeight(height);
34161                 
34162             }, this);
34163             
34164             if(!box.length){
34165                 return;
34166             }
34167             
34168             var positions = [];
34169             
34170             switch (box.length){
34171                 case 1 :
34172                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34173                     break;
34174                 case 2 :
34175                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34176                     break;
34177                 case 3 :
34178                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34179                     break;
34180                 case 4 :
34181                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34182                     break;
34183                 default :
34184                     break;
34185             }
34186             
34187             Roo.each(box, function(b,kk){
34188                 
34189                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34190                 
34191                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34192                 
34193             }, this);
34194             
34195         }, this);
34196         
34197     },
34198     
34199     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34200     {
34201         Roo.each(eItems, function(b,k){
34202             
34203             b.size = (k == 0) ? 'sm' : 'xs';
34204             b.x = (k == 0) ? 2 : 1;
34205             b.y = (k == 0) ? 2 : 1;
34206             
34207             b.el.position('absolute');
34208             
34209             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34210                 
34211             b.el.setWidth(width);
34212             
34213             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34214             
34215             b.el.setHeight(height);
34216             
34217         }, this);
34218
34219         var positions = [];
34220         
34221         positions.push({
34222             x : maxX - this.unitWidth * 2 - this.gutter,
34223             y : minY
34224         });
34225         
34226         positions.push({
34227             x : maxX - this.unitWidth,
34228             y : minY + (this.unitWidth + this.gutter) * 2
34229         });
34230         
34231         positions.push({
34232             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34233             y : minY
34234         });
34235         
34236         Roo.each(eItems, function(b,k){
34237             
34238             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34239
34240         }, this);
34241         
34242     },
34243     
34244     getVerticalOneBoxColPositions : function(x, y, box)
34245     {
34246         var pos = [];
34247         
34248         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34249         
34250         if(box[0].size == 'md-left'){
34251             rand = 0;
34252         }
34253         
34254         if(box[0].size == 'md-right'){
34255             rand = 1;
34256         }
34257         
34258         pos.push({
34259             x : x + (this.unitWidth + this.gutter) * rand,
34260             y : y
34261         });
34262         
34263         return pos;
34264     },
34265     
34266     getVerticalTwoBoxColPositions : function(x, y, box)
34267     {
34268         var pos = [];
34269         
34270         if(box[0].size == 'xs'){
34271             
34272             pos.push({
34273                 x : x,
34274                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34275             });
34276
34277             pos.push({
34278                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34279                 y : y
34280             });
34281             
34282             return pos;
34283             
34284         }
34285         
34286         pos.push({
34287             x : x,
34288             y : y
34289         });
34290
34291         pos.push({
34292             x : x + (this.unitWidth + this.gutter) * 2,
34293             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34294         });
34295         
34296         return pos;
34297         
34298     },
34299     
34300     getVerticalThreeBoxColPositions : function(x, y, box)
34301     {
34302         var pos = [];
34303         
34304         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34305             
34306             pos.push({
34307                 x : x,
34308                 y : y
34309             });
34310
34311             pos.push({
34312                 x : x + (this.unitWidth + this.gutter) * 1,
34313                 y : y
34314             });
34315             
34316             pos.push({
34317                 x : x + (this.unitWidth + this.gutter) * 2,
34318                 y : y
34319             });
34320             
34321             return pos;
34322             
34323         }
34324         
34325         if(box[0].size == 'xs' && box[1].size == 'xs'){
34326             
34327             pos.push({
34328                 x : x,
34329                 y : y
34330             });
34331
34332             pos.push({
34333                 x : x,
34334                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34335             });
34336             
34337             pos.push({
34338                 x : x + (this.unitWidth + this.gutter) * 1,
34339                 y : y
34340             });
34341             
34342             return pos;
34343             
34344         }
34345         
34346         pos.push({
34347             x : x,
34348             y : y
34349         });
34350
34351         pos.push({
34352             x : x + (this.unitWidth + this.gutter) * 2,
34353             y : y
34354         });
34355
34356         pos.push({
34357             x : x + (this.unitWidth + this.gutter) * 2,
34358             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34359         });
34360             
34361         return pos;
34362         
34363     },
34364     
34365     getVerticalFourBoxColPositions : function(x, y, box)
34366     {
34367         var pos = [];
34368         
34369         if(box[0].size == 'xs'){
34370             
34371             pos.push({
34372                 x : x,
34373                 y : y
34374             });
34375
34376             pos.push({
34377                 x : x,
34378                 y : y + (this.unitHeight + this.gutter) * 1
34379             });
34380             
34381             pos.push({
34382                 x : x,
34383                 y : y + (this.unitHeight + this.gutter) * 2
34384             });
34385             
34386             pos.push({
34387                 x : x + (this.unitWidth + this.gutter) * 1,
34388                 y : y
34389             });
34390             
34391             return pos;
34392             
34393         }
34394         
34395         pos.push({
34396             x : x,
34397             y : y
34398         });
34399
34400         pos.push({
34401             x : x + (this.unitWidth + this.gutter) * 2,
34402             y : y
34403         });
34404
34405         pos.push({
34406             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34407             y : y + (this.unitHeight + this.gutter) * 1
34408         });
34409
34410         pos.push({
34411             x : x + (this.unitWidth + this.gutter) * 2,
34412             y : y + (this.unitWidth + this.gutter) * 2
34413         });
34414
34415         return pos;
34416         
34417     },
34418     
34419     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34420     {
34421         var pos = [];
34422         
34423         if(box[0].size == 'md-left'){
34424             pos.push({
34425                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34426                 y : minY
34427             });
34428             
34429             return pos;
34430         }
34431         
34432         if(box[0].size == 'md-right'){
34433             pos.push({
34434                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34435                 y : minY + (this.unitWidth + this.gutter) * 1
34436             });
34437             
34438             return pos;
34439         }
34440         
34441         var rand = Math.floor(Math.random() * (4 - box[0].y));
34442         
34443         pos.push({
34444             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34445             y : minY + (this.unitWidth + this.gutter) * rand
34446         });
34447         
34448         return pos;
34449         
34450     },
34451     
34452     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34453     {
34454         var pos = [];
34455         
34456         if(box[0].size == 'xs'){
34457             
34458             pos.push({
34459                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34460                 y : minY
34461             });
34462
34463             pos.push({
34464                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34465                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34466             });
34467             
34468             return pos;
34469             
34470         }
34471         
34472         pos.push({
34473             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34474             y : minY
34475         });
34476
34477         pos.push({
34478             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34479             y : minY + (this.unitWidth + this.gutter) * 2
34480         });
34481         
34482         return pos;
34483         
34484     },
34485     
34486     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34487     {
34488         var pos = [];
34489         
34490         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34491             
34492             pos.push({
34493                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34494                 y : minY
34495             });
34496
34497             pos.push({
34498                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34499                 y : minY + (this.unitWidth + this.gutter) * 1
34500             });
34501             
34502             pos.push({
34503                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34504                 y : minY + (this.unitWidth + this.gutter) * 2
34505             });
34506             
34507             return pos;
34508             
34509         }
34510         
34511         if(box[0].size == 'xs' && box[1].size == 'xs'){
34512             
34513             pos.push({
34514                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34515                 y : minY
34516             });
34517
34518             pos.push({
34519                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34520                 y : minY
34521             });
34522             
34523             pos.push({
34524                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34525                 y : minY + (this.unitWidth + this.gutter) * 1
34526             });
34527             
34528             return pos;
34529             
34530         }
34531         
34532         pos.push({
34533             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34534             y : minY
34535         });
34536
34537         pos.push({
34538             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34539             y : minY + (this.unitWidth + this.gutter) * 2
34540         });
34541
34542         pos.push({
34543             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34544             y : minY + (this.unitWidth + this.gutter) * 2
34545         });
34546             
34547         return pos;
34548         
34549     },
34550     
34551     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34552     {
34553         var pos = [];
34554         
34555         if(box[0].size == 'xs'){
34556             
34557             pos.push({
34558                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34559                 y : minY
34560             });
34561
34562             pos.push({
34563                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34564                 y : minY
34565             });
34566             
34567             pos.push({
34568                 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),
34569                 y : minY
34570             });
34571             
34572             pos.push({
34573                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34574                 y : minY + (this.unitWidth + this.gutter) * 1
34575             });
34576             
34577             return pos;
34578             
34579         }
34580         
34581         pos.push({
34582             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34583             y : minY
34584         });
34585         
34586         pos.push({
34587             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34588             y : minY + (this.unitWidth + this.gutter) * 2
34589         });
34590         
34591         pos.push({
34592             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34593             y : minY + (this.unitWidth + this.gutter) * 2
34594         });
34595         
34596         pos.push({
34597             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),
34598             y : minY + (this.unitWidth + this.gutter) * 2
34599         });
34600
34601         return pos;
34602         
34603     },
34604     
34605     /**
34606     * remove a Masonry Brick
34607     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34608     */
34609     removeBrick : function(brick_id)
34610     {
34611         if (!brick_id) {
34612             return;
34613         }
34614         
34615         for (var i = 0; i<this.bricks.length; i++) {
34616             if (this.bricks[i].id == brick_id) {
34617                 this.bricks.splice(i,1);
34618                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34619                 this.initial();
34620             }
34621         }
34622     },
34623     
34624     /**
34625     * adds a Masonry Brick
34626     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34627     */
34628     addBrick : function(cfg)
34629     {
34630         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34631         //this.register(cn);
34632         cn.parentId = this.id;
34633         cn.render(this.el);
34634         return cn;
34635     },
34636     
34637     /**
34638     * register a Masonry Brick
34639     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34640     */
34641     
34642     register : function(brick)
34643     {
34644         this.bricks.push(brick);
34645         brick.masonryId = this.id;
34646     },
34647     
34648     /**
34649     * clear all the Masonry Brick
34650     */
34651     clearAll : function()
34652     {
34653         this.bricks = [];
34654         //this.getChildContainer().dom.innerHTML = "";
34655         this.el.dom.innerHTML = '';
34656     },
34657     
34658     getSelected : function()
34659     {
34660         if (!this.selectedBrick) {
34661             return false;
34662         }
34663         
34664         return this.selectedBrick;
34665     }
34666 });
34667
34668 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34669     
34670     groups: {},
34671      /**
34672     * register a Masonry Layout
34673     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34674     */
34675     
34676     register : function(layout)
34677     {
34678         this.groups[layout.id] = layout;
34679     },
34680     /**
34681     * fetch a  Masonry Layout based on the masonry layout ID
34682     * @param {string} the masonry layout to add
34683     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34684     */
34685     
34686     get: function(layout_id) {
34687         if (typeof(this.groups[layout_id]) == 'undefined') {
34688             return false;
34689         }
34690         return this.groups[layout_id] ;
34691     }
34692     
34693     
34694     
34695 });
34696
34697  
34698
34699  /**
34700  *
34701  * This is based on 
34702  * http://masonry.desandro.com
34703  *
34704  * The idea is to render all the bricks based on vertical width...
34705  *
34706  * The original code extends 'outlayer' - we might need to use that....
34707  * 
34708  */
34709
34710
34711 /**
34712  * @class Roo.bootstrap.LayoutMasonryAuto
34713  * @extends Roo.bootstrap.Component
34714  * Bootstrap Layout Masonry class
34715  * 
34716  * @constructor
34717  * Create a new Element
34718  * @param {Object} config The config object
34719  */
34720
34721 Roo.bootstrap.LayoutMasonryAuto = function(config){
34722     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34723 };
34724
34725 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34726     
34727       /**
34728      * @cfg {Boolean} isFitWidth  - resize the width..
34729      */   
34730     isFitWidth : false,  // options..
34731     /**
34732      * @cfg {Boolean} isOriginLeft = left align?
34733      */   
34734     isOriginLeft : true,
34735     /**
34736      * @cfg {Boolean} isOriginTop = top align?
34737      */   
34738     isOriginTop : false,
34739     /**
34740      * @cfg {Boolean} isLayoutInstant = no animation?
34741      */   
34742     isLayoutInstant : false, // needed?
34743     /**
34744      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34745      */   
34746     isResizingContainer : true,
34747     /**
34748      * @cfg {Number} columnWidth  width of the columns 
34749      */   
34750     
34751     columnWidth : 0,
34752     
34753     /**
34754      * @cfg {Number} maxCols maximum number of columns
34755      */   
34756     
34757     maxCols: 0,
34758     /**
34759      * @cfg {Number} padHeight padding below box..
34760      */   
34761     
34762     padHeight : 10, 
34763     
34764     /**
34765      * @cfg {Boolean} isAutoInitial defalut true
34766      */   
34767     
34768     isAutoInitial : true, 
34769     
34770     // private?
34771     gutter : 0,
34772     
34773     containerWidth: 0,
34774     initialColumnWidth : 0,
34775     currentSize : null,
34776     
34777     colYs : null, // array.
34778     maxY : 0,
34779     padWidth: 10,
34780     
34781     
34782     tag: 'div',
34783     cls: '',
34784     bricks: null, //CompositeElement
34785     cols : 0, // array?
34786     // element : null, // wrapped now this.el
34787     _isLayoutInited : null, 
34788     
34789     
34790     getAutoCreate : function(){
34791         
34792         var cfg = {
34793             tag: this.tag,
34794             cls: 'blog-masonary-wrapper ' + this.cls,
34795             cn : {
34796                 cls : 'mas-boxes masonary'
34797             }
34798         };
34799         
34800         return cfg;
34801     },
34802     
34803     getChildContainer: function( )
34804     {
34805         if (this.boxesEl) {
34806             return this.boxesEl;
34807         }
34808         
34809         this.boxesEl = this.el.select('.mas-boxes').first();
34810         
34811         return this.boxesEl;
34812     },
34813     
34814     
34815     initEvents : function()
34816     {
34817         var _this = this;
34818         
34819         if(this.isAutoInitial){
34820             Roo.log('hook children rendered');
34821             this.on('childrenrendered', function() {
34822                 Roo.log('children rendered');
34823                 _this.initial();
34824             } ,this);
34825         }
34826         
34827     },
34828     
34829     initial : function()
34830     {
34831         this.reloadItems();
34832
34833         this.currentSize = this.el.getBox(true);
34834
34835         /// was window resize... - let's see if this works..
34836         Roo.EventManager.onWindowResize(this.resize, this); 
34837
34838         if(!this.isAutoInitial){
34839             this.layout();
34840             return;
34841         }
34842         
34843         this.layout.defer(500,this);
34844     },
34845     
34846     reloadItems: function()
34847     {
34848         this.bricks = this.el.select('.masonry-brick', true);
34849         
34850         this.bricks.each(function(b) {
34851             //Roo.log(b.getSize());
34852             if (!b.attr('originalwidth')) {
34853                 b.attr('originalwidth',  b.getSize().width);
34854             }
34855             
34856         });
34857         
34858         Roo.log(this.bricks.elements.length);
34859     },
34860     
34861     resize : function()
34862     {
34863         Roo.log('resize');
34864         var cs = this.el.getBox(true);
34865         
34866         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34867             Roo.log("no change in with or X");
34868             return;
34869         }
34870         this.currentSize = cs;
34871         this.layout();
34872     },
34873     
34874     layout : function()
34875     {
34876          Roo.log('layout');
34877         this._resetLayout();
34878         //this._manageStamps();
34879       
34880         // don't animate first layout
34881         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34882         this.layoutItems( isInstant );
34883       
34884         // flag for initalized
34885         this._isLayoutInited = true;
34886     },
34887     
34888     layoutItems : function( isInstant )
34889     {
34890         //var items = this._getItemsForLayout( this.items );
34891         // original code supports filtering layout items.. we just ignore it..
34892         
34893         this._layoutItems( this.bricks , isInstant );
34894       
34895         this._postLayout();
34896     },
34897     _layoutItems : function ( items , isInstant)
34898     {
34899        //this.fireEvent( 'layout', this, items );
34900     
34901
34902         if ( !items || !items.elements.length ) {
34903           // no items, emit event with empty array
34904             return;
34905         }
34906
34907         var queue = [];
34908         items.each(function(item) {
34909             Roo.log("layout item");
34910             Roo.log(item);
34911             // get x/y object from method
34912             var position = this._getItemLayoutPosition( item );
34913             // enqueue
34914             position.item = item;
34915             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34916             queue.push( position );
34917         }, this);
34918       
34919         this._processLayoutQueue( queue );
34920     },
34921     /** Sets position of item in DOM
34922     * @param {Element} item
34923     * @param {Number} x - horizontal position
34924     * @param {Number} y - vertical position
34925     * @param {Boolean} isInstant - disables transitions
34926     */
34927     _processLayoutQueue : function( queue )
34928     {
34929         for ( var i=0, len = queue.length; i < len; i++ ) {
34930             var obj = queue[i];
34931             obj.item.position('absolute');
34932             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34933         }
34934     },
34935       
34936     
34937     /**
34938     * Any logic you want to do after each layout,
34939     * i.e. size the container
34940     */
34941     _postLayout : function()
34942     {
34943         this.resizeContainer();
34944     },
34945     
34946     resizeContainer : function()
34947     {
34948         if ( !this.isResizingContainer ) {
34949             return;
34950         }
34951         var size = this._getContainerSize();
34952         if ( size ) {
34953             this.el.setSize(size.width,size.height);
34954             this.boxesEl.setSize(size.width,size.height);
34955         }
34956     },
34957     
34958     
34959     
34960     _resetLayout : function()
34961     {
34962         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34963         this.colWidth = this.el.getWidth();
34964         //this.gutter = this.el.getWidth(); 
34965         
34966         this.measureColumns();
34967
34968         // reset column Y
34969         var i = this.cols;
34970         this.colYs = [];
34971         while (i--) {
34972             this.colYs.push( 0 );
34973         }
34974     
34975         this.maxY = 0;
34976     },
34977
34978     measureColumns : function()
34979     {
34980         this.getContainerWidth();
34981       // if columnWidth is 0, default to outerWidth of first item
34982         if ( !this.columnWidth ) {
34983             var firstItem = this.bricks.first();
34984             Roo.log(firstItem);
34985             this.columnWidth  = this.containerWidth;
34986             if (firstItem && firstItem.attr('originalwidth') ) {
34987                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34988             }
34989             // columnWidth fall back to item of first element
34990             Roo.log("set column width?");
34991                         this.initialColumnWidth = this.columnWidth  ;
34992
34993             // if first elem has no width, default to size of container
34994             
34995         }
34996         
34997         
34998         if (this.initialColumnWidth) {
34999             this.columnWidth = this.initialColumnWidth;
35000         }
35001         
35002         
35003             
35004         // column width is fixed at the top - however if container width get's smaller we should
35005         // reduce it...
35006         
35007         // this bit calcs how man columns..
35008             
35009         var columnWidth = this.columnWidth += this.gutter;
35010       
35011         // calculate columns
35012         var containerWidth = this.containerWidth + this.gutter;
35013         
35014         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35015         // fix rounding errors, typically with gutters
35016         var excess = columnWidth - containerWidth % columnWidth;
35017         
35018         
35019         // if overshoot is less than a pixel, round up, otherwise floor it
35020         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35021         cols = Math[ mathMethod ]( cols );
35022         this.cols = Math.max( cols, 1 );
35023         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35024         
35025          // padding positioning..
35026         var totalColWidth = this.cols * this.columnWidth;
35027         var padavail = this.containerWidth - totalColWidth;
35028         // so for 2 columns - we need 3 'pads'
35029         
35030         var padNeeded = (1+this.cols) * this.padWidth;
35031         
35032         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35033         
35034         this.columnWidth += padExtra
35035         //this.padWidth = Math.floor(padavail /  ( this.cols));
35036         
35037         // adjust colum width so that padding is fixed??
35038         
35039         // we have 3 columns ... total = width * 3
35040         // we have X left over... that should be used by 
35041         
35042         //if (this.expandC) {
35043             
35044         //}
35045         
35046         
35047         
35048     },
35049     
35050     getContainerWidth : function()
35051     {
35052        /* // container is parent if fit width
35053         var container = this.isFitWidth ? this.element.parentNode : this.element;
35054         // check that this.size and size are there
35055         // IE8 triggers resize on body size change, so they might not be
35056         
35057         var size = getSize( container );  //FIXME
35058         this.containerWidth = size && size.innerWidth; //FIXME
35059         */
35060          
35061         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35062         
35063     },
35064     
35065     _getItemLayoutPosition : function( item )  // what is item?
35066     {
35067         // we resize the item to our columnWidth..
35068       
35069         item.setWidth(this.columnWidth);
35070         item.autoBoxAdjust  = false;
35071         
35072         var sz = item.getSize();
35073  
35074         // how many columns does this brick span
35075         var remainder = this.containerWidth % this.columnWidth;
35076         
35077         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35078         // round if off by 1 pixel, otherwise use ceil
35079         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35080         colSpan = Math.min( colSpan, this.cols );
35081         
35082         // normally this should be '1' as we dont' currently allow multi width columns..
35083         
35084         var colGroup = this._getColGroup( colSpan );
35085         // get the minimum Y value from the columns
35086         var minimumY = Math.min.apply( Math, colGroup );
35087         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35088         
35089         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35090          
35091         // position the brick
35092         var position = {
35093             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35094             y: this.currentSize.y + minimumY + this.padHeight
35095         };
35096         
35097         Roo.log(position);
35098         // apply setHeight to necessary columns
35099         var setHeight = minimumY + sz.height + this.padHeight;
35100         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35101         
35102         var setSpan = this.cols + 1 - colGroup.length;
35103         for ( var i = 0; i < setSpan; i++ ) {
35104           this.colYs[ shortColIndex + i ] = setHeight ;
35105         }
35106       
35107         return position;
35108     },
35109     
35110     /**
35111      * @param {Number} colSpan - number of columns the element spans
35112      * @returns {Array} colGroup
35113      */
35114     _getColGroup : function( colSpan )
35115     {
35116         if ( colSpan < 2 ) {
35117           // if brick spans only one column, use all the column Ys
35118           return this.colYs;
35119         }
35120       
35121         var colGroup = [];
35122         // how many different places could this brick fit horizontally
35123         var groupCount = this.cols + 1 - colSpan;
35124         // for each group potential horizontal position
35125         for ( var i = 0; i < groupCount; i++ ) {
35126           // make an array of colY values for that one group
35127           var groupColYs = this.colYs.slice( i, i + colSpan );
35128           // and get the max value of the array
35129           colGroup[i] = Math.max.apply( Math, groupColYs );
35130         }
35131         return colGroup;
35132     },
35133     /*
35134     _manageStamp : function( stamp )
35135     {
35136         var stampSize =  stamp.getSize();
35137         var offset = stamp.getBox();
35138         // get the columns that this stamp affects
35139         var firstX = this.isOriginLeft ? offset.x : offset.right;
35140         var lastX = firstX + stampSize.width;
35141         var firstCol = Math.floor( firstX / this.columnWidth );
35142         firstCol = Math.max( 0, firstCol );
35143         
35144         var lastCol = Math.floor( lastX / this.columnWidth );
35145         // lastCol should not go over if multiple of columnWidth #425
35146         lastCol -= lastX % this.columnWidth ? 0 : 1;
35147         lastCol = Math.min( this.cols - 1, lastCol );
35148         
35149         // set colYs to bottom of the stamp
35150         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35151             stampSize.height;
35152             
35153         for ( var i = firstCol; i <= lastCol; i++ ) {
35154           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35155         }
35156     },
35157     */
35158     
35159     _getContainerSize : function()
35160     {
35161         this.maxY = Math.max.apply( Math, this.colYs );
35162         var size = {
35163             height: this.maxY
35164         };
35165       
35166         if ( this.isFitWidth ) {
35167             size.width = this._getContainerFitWidth();
35168         }
35169       
35170         return size;
35171     },
35172     
35173     _getContainerFitWidth : function()
35174     {
35175         var unusedCols = 0;
35176         // count unused columns
35177         var i = this.cols;
35178         while ( --i ) {
35179           if ( this.colYs[i] !== 0 ) {
35180             break;
35181           }
35182           unusedCols++;
35183         }
35184         // fit container to columns that have been used
35185         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35186     },
35187     
35188     needsResizeLayout : function()
35189     {
35190         var previousWidth = this.containerWidth;
35191         this.getContainerWidth();
35192         return previousWidth !== this.containerWidth;
35193     }
35194  
35195 });
35196
35197  
35198
35199  /*
35200  * - LGPL
35201  *
35202  * element
35203  * 
35204  */
35205
35206 /**
35207  * @class Roo.bootstrap.MasonryBrick
35208  * @extends Roo.bootstrap.Component
35209  * Bootstrap MasonryBrick class
35210  * 
35211  * @constructor
35212  * Create a new MasonryBrick
35213  * @param {Object} config The config object
35214  */
35215
35216 Roo.bootstrap.MasonryBrick = function(config){
35217     
35218     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35219     
35220     Roo.bootstrap.MasonryBrick.register(this);
35221     
35222     this.addEvents({
35223         // raw events
35224         /**
35225          * @event click
35226          * When a MasonryBrick is clcik
35227          * @param {Roo.bootstrap.MasonryBrick} this
35228          * @param {Roo.EventObject} e
35229          */
35230         "click" : true
35231     });
35232 };
35233
35234 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35235     
35236     /**
35237      * @cfg {String} title
35238      */   
35239     title : '',
35240     /**
35241      * @cfg {String} html
35242      */   
35243     html : '',
35244     /**
35245      * @cfg {String} bgimage
35246      */   
35247     bgimage : '',
35248     /**
35249      * @cfg {String} videourl
35250      */   
35251     videourl : '',
35252     /**
35253      * @cfg {String} cls
35254      */   
35255     cls : '',
35256     /**
35257      * @cfg {String} href
35258      */   
35259     href : '',
35260     /**
35261      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35262      */   
35263     size : 'xs',
35264     
35265     /**
35266      * @cfg {String} placetitle (center|bottom)
35267      */   
35268     placetitle : '',
35269     
35270     /**
35271      * @cfg {Boolean} isFitContainer defalut true
35272      */   
35273     isFitContainer : true, 
35274     
35275     /**
35276      * @cfg {Boolean} preventDefault defalut false
35277      */   
35278     preventDefault : false, 
35279     
35280     /**
35281      * @cfg {Boolean} inverse defalut false
35282      */   
35283     maskInverse : false, 
35284     
35285     getAutoCreate : function()
35286     {
35287         if(!this.isFitContainer){
35288             return this.getSplitAutoCreate();
35289         }
35290         
35291         var cls = 'masonry-brick masonry-brick-full';
35292         
35293         if(this.href.length){
35294             cls += ' masonry-brick-link';
35295         }
35296         
35297         if(this.bgimage.length){
35298             cls += ' masonry-brick-image';
35299         }
35300         
35301         if(this.maskInverse){
35302             cls += ' mask-inverse';
35303         }
35304         
35305         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35306             cls += ' enable-mask';
35307         }
35308         
35309         if(this.size){
35310             cls += ' masonry-' + this.size + '-brick';
35311         }
35312         
35313         if(this.placetitle.length){
35314             
35315             switch (this.placetitle) {
35316                 case 'center' :
35317                     cls += ' masonry-center-title';
35318                     break;
35319                 case 'bottom' :
35320                     cls += ' masonry-bottom-title';
35321                     break;
35322                 default:
35323                     break;
35324             }
35325             
35326         } else {
35327             if(!this.html.length && !this.bgimage.length){
35328                 cls += ' masonry-center-title';
35329             }
35330
35331             if(!this.html.length && this.bgimage.length){
35332                 cls += ' masonry-bottom-title';
35333             }
35334         }
35335         
35336         if(this.cls){
35337             cls += ' ' + this.cls;
35338         }
35339         
35340         var cfg = {
35341             tag: (this.href.length) ? 'a' : 'div',
35342             cls: cls,
35343             cn: [
35344                 {
35345                     tag: 'div',
35346                     cls: 'masonry-brick-mask'
35347                 },
35348                 {
35349                     tag: 'div',
35350                     cls: 'masonry-brick-paragraph',
35351                     cn: []
35352                 }
35353             ]
35354         };
35355         
35356         if(this.href.length){
35357             cfg.href = this.href;
35358         }
35359         
35360         var cn = cfg.cn[1].cn;
35361         
35362         if(this.title.length){
35363             cn.push({
35364                 tag: 'h4',
35365                 cls: 'masonry-brick-title',
35366                 html: this.title
35367             });
35368         }
35369         
35370         if(this.html.length){
35371             cn.push({
35372                 tag: 'p',
35373                 cls: 'masonry-brick-text',
35374                 html: this.html
35375             });
35376         }
35377         
35378         if (!this.title.length && !this.html.length) {
35379             cfg.cn[1].cls += ' hide';
35380         }
35381         
35382         if(this.bgimage.length){
35383             cfg.cn.push({
35384                 tag: 'img',
35385                 cls: 'masonry-brick-image-view',
35386                 src: this.bgimage
35387             });
35388         }
35389         
35390         if(this.videourl.length){
35391             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35392             // youtube support only?
35393             cfg.cn.push({
35394                 tag: 'iframe',
35395                 cls: 'masonry-brick-image-view',
35396                 src: vurl,
35397                 frameborder : 0,
35398                 allowfullscreen : true
35399             });
35400         }
35401         
35402         return cfg;
35403         
35404     },
35405     
35406     getSplitAutoCreate : function()
35407     {
35408         var cls = 'masonry-brick masonry-brick-split';
35409         
35410         if(this.href.length){
35411             cls += ' masonry-brick-link';
35412         }
35413         
35414         if(this.bgimage.length){
35415             cls += ' masonry-brick-image';
35416         }
35417         
35418         if(this.size){
35419             cls += ' masonry-' + this.size + '-brick';
35420         }
35421         
35422         switch (this.placetitle) {
35423             case 'center' :
35424                 cls += ' masonry-center-title';
35425                 break;
35426             case 'bottom' :
35427                 cls += ' masonry-bottom-title';
35428                 break;
35429             default:
35430                 if(!this.bgimage.length){
35431                     cls += ' masonry-center-title';
35432                 }
35433
35434                 if(this.bgimage.length){
35435                     cls += ' masonry-bottom-title';
35436                 }
35437                 break;
35438         }
35439         
35440         if(this.cls){
35441             cls += ' ' + this.cls;
35442         }
35443         
35444         var cfg = {
35445             tag: (this.href.length) ? 'a' : 'div',
35446             cls: cls,
35447             cn: [
35448                 {
35449                     tag: 'div',
35450                     cls: 'masonry-brick-split-head',
35451                     cn: [
35452                         {
35453                             tag: 'div',
35454                             cls: 'masonry-brick-paragraph',
35455                             cn: []
35456                         }
35457                     ]
35458                 },
35459                 {
35460                     tag: 'div',
35461                     cls: 'masonry-brick-split-body',
35462                     cn: []
35463                 }
35464             ]
35465         };
35466         
35467         if(this.href.length){
35468             cfg.href = this.href;
35469         }
35470         
35471         if(this.title.length){
35472             cfg.cn[0].cn[0].cn.push({
35473                 tag: 'h4',
35474                 cls: 'masonry-brick-title',
35475                 html: this.title
35476             });
35477         }
35478         
35479         if(this.html.length){
35480             cfg.cn[1].cn.push({
35481                 tag: 'p',
35482                 cls: 'masonry-brick-text',
35483                 html: this.html
35484             });
35485         }
35486
35487         if(this.bgimage.length){
35488             cfg.cn[0].cn.push({
35489                 tag: 'img',
35490                 cls: 'masonry-brick-image-view',
35491                 src: this.bgimage
35492             });
35493         }
35494         
35495         if(this.videourl.length){
35496             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35497             // youtube support only?
35498             cfg.cn[0].cn.cn.push({
35499                 tag: 'iframe',
35500                 cls: 'masonry-brick-image-view',
35501                 src: vurl,
35502                 frameborder : 0,
35503                 allowfullscreen : true
35504             });
35505         }
35506         
35507         return cfg;
35508     },
35509     
35510     initEvents: function() 
35511     {
35512         switch (this.size) {
35513             case 'xs' :
35514                 this.x = 1;
35515                 this.y = 1;
35516                 break;
35517             case 'sm' :
35518                 this.x = 2;
35519                 this.y = 2;
35520                 break;
35521             case 'md' :
35522             case 'md-left' :
35523             case 'md-right' :
35524                 this.x = 3;
35525                 this.y = 3;
35526                 break;
35527             case 'tall' :
35528                 this.x = 2;
35529                 this.y = 3;
35530                 break;
35531             case 'wide' :
35532                 this.x = 3;
35533                 this.y = 2;
35534                 break;
35535             case 'wide-thin' :
35536                 this.x = 3;
35537                 this.y = 1;
35538                 break;
35539                         
35540             default :
35541                 break;
35542         }
35543         
35544         if(Roo.isTouch){
35545             this.el.on('touchstart', this.onTouchStart, this);
35546             this.el.on('touchmove', this.onTouchMove, this);
35547             this.el.on('touchend', this.onTouchEnd, this);
35548             this.el.on('contextmenu', this.onContextMenu, this);
35549         } else {
35550             this.el.on('mouseenter'  ,this.enter, this);
35551             this.el.on('mouseleave', this.leave, this);
35552             this.el.on('click', this.onClick, this);
35553         }
35554         
35555         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35556             this.parent().bricks.push(this);   
35557         }
35558         
35559     },
35560     
35561     onClick: function(e, el)
35562     {
35563         var time = this.endTimer - this.startTimer;
35564         // Roo.log(e.preventDefault());
35565         if(Roo.isTouch){
35566             if(time > 1000){
35567                 e.preventDefault();
35568                 return;
35569             }
35570         }
35571         
35572         if(!this.preventDefault){
35573             return;
35574         }
35575         
35576         e.preventDefault();
35577         
35578         if (this.activeClass != '') {
35579             this.selectBrick();
35580         }
35581         
35582         this.fireEvent('click', this, e);
35583     },
35584     
35585     enter: function(e, el)
35586     {
35587         e.preventDefault();
35588         
35589         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35590             return;
35591         }
35592         
35593         if(this.bgimage.length && this.html.length){
35594             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35595         }
35596     },
35597     
35598     leave: function(e, el)
35599     {
35600         e.preventDefault();
35601         
35602         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35603             return;
35604         }
35605         
35606         if(this.bgimage.length && this.html.length){
35607             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35608         }
35609     },
35610     
35611     onTouchStart: function(e, el)
35612     {
35613 //        e.preventDefault();
35614         
35615         this.touchmoved = false;
35616         
35617         if(!this.isFitContainer){
35618             return;
35619         }
35620         
35621         if(!this.bgimage.length || !this.html.length){
35622             return;
35623         }
35624         
35625         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35626         
35627         this.timer = new Date().getTime();
35628         
35629     },
35630     
35631     onTouchMove: function(e, el)
35632     {
35633         this.touchmoved = true;
35634     },
35635     
35636     onContextMenu : function(e,el)
35637     {
35638         e.preventDefault();
35639         e.stopPropagation();
35640         return false;
35641     },
35642     
35643     onTouchEnd: function(e, el)
35644     {
35645 //        e.preventDefault();
35646         
35647         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35648         
35649             this.leave(e,el);
35650             
35651             return;
35652         }
35653         
35654         if(!this.bgimage.length || !this.html.length){
35655             
35656             if(this.href.length){
35657                 window.location.href = this.href;
35658             }
35659             
35660             return;
35661         }
35662         
35663         if(!this.isFitContainer){
35664             return;
35665         }
35666         
35667         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35668         
35669         window.location.href = this.href;
35670     },
35671     
35672     //selection on single brick only
35673     selectBrick : function() {
35674         
35675         if (!this.parentId) {
35676             return;
35677         }
35678         
35679         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35680         var index = m.selectedBrick.indexOf(this.id);
35681         
35682         if ( index > -1) {
35683             m.selectedBrick.splice(index,1);
35684             this.el.removeClass(this.activeClass);
35685             return;
35686         }
35687         
35688         for(var i = 0; i < m.selectedBrick.length; i++) {
35689             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35690             b.el.removeClass(b.activeClass);
35691         }
35692         
35693         m.selectedBrick = [];
35694         
35695         m.selectedBrick.push(this.id);
35696         this.el.addClass(this.activeClass);
35697         return;
35698     },
35699     
35700     isSelected : function(){
35701         return this.el.hasClass(this.activeClass);
35702         
35703     }
35704 });
35705
35706 Roo.apply(Roo.bootstrap.MasonryBrick, {
35707     
35708     //groups: {},
35709     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35710      /**
35711     * register a Masonry Brick
35712     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35713     */
35714     
35715     register : function(brick)
35716     {
35717         //this.groups[brick.id] = brick;
35718         this.groups.add(brick.id, brick);
35719     },
35720     /**
35721     * fetch a  masonry brick based on the masonry brick ID
35722     * @param {string} the masonry brick to add
35723     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35724     */
35725     
35726     get: function(brick_id) 
35727     {
35728         // if (typeof(this.groups[brick_id]) == 'undefined') {
35729         //     return false;
35730         // }
35731         // return this.groups[brick_id] ;
35732         
35733         if(this.groups.key(brick_id)) {
35734             return this.groups.key(brick_id);
35735         }
35736         
35737         return false;
35738     }
35739     
35740     
35741     
35742 });
35743
35744  /*
35745  * - LGPL
35746  *
35747  * element
35748  * 
35749  */
35750
35751 /**
35752  * @class Roo.bootstrap.Brick
35753  * @extends Roo.bootstrap.Component
35754  * Bootstrap Brick class
35755  * 
35756  * @constructor
35757  * Create a new Brick
35758  * @param {Object} config The config object
35759  */
35760
35761 Roo.bootstrap.Brick = function(config){
35762     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35763     
35764     this.addEvents({
35765         // raw events
35766         /**
35767          * @event click
35768          * When a Brick is click
35769          * @param {Roo.bootstrap.Brick} this
35770          * @param {Roo.EventObject} e
35771          */
35772         "click" : true
35773     });
35774 };
35775
35776 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35777     
35778     /**
35779      * @cfg {String} title
35780      */   
35781     title : '',
35782     /**
35783      * @cfg {String} html
35784      */   
35785     html : '',
35786     /**
35787      * @cfg {String} bgimage
35788      */   
35789     bgimage : '',
35790     /**
35791      * @cfg {String} cls
35792      */   
35793     cls : '',
35794     /**
35795      * @cfg {String} href
35796      */   
35797     href : '',
35798     /**
35799      * @cfg {String} video
35800      */   
35801     video : '',
35802     /**
35803      * @cfg {Boolean} square
35804      */   
35805     square : true,
35806     
35807     getAutoCreate : function()
35808     {
35809         var cls = 'roo-brick';
35810         
35811         if(this.href.length){
35812             cls += ' roo-brick-link';
35813         }
35814         
35815         if(this.bgimage.length){
35816             cls += ' roo-brick-image';
35817         }
35818         
35819         if(!this.html.length && !this.bgimage.length){
35820             cls += ' roo-brick-center-title';
35821         }
35822         
35823         if(!this.html.length && this.bgimage.length){
35824             cls += ' roo-brick-bottom-title';
35825         }
35826         
35827         if(this.cls){
35828             cls += ' ' + this.cls;
35829         }
35830         
35831         var cfg = {
35832             tag: (this.href.length) ? 'a' : 'div',
35833             cls: cls,
35834             cn: [
35835                 {
35836                     tag: 'div',
35837                     cls: 'roo-brick-paragraph',
35838                     cn: []
35839                 }
35840             ]
35841         };
35842         
35843         if(this.href.length){
35844             cfg.href = this.href;
35845         }
35846         
35847         var cn = cfg.cn[0].cn;
35848         
35849         if(this.title.length){
35850             cn.push({
35851                 tag: 'h4',
35852                 cls: 'roo-brick-title',
35853                 html: this.title
35854             });
35855         }
35856         
35857         if(this.html.length){
35858             cn.push({
35859                 tag: 'p',
35860                 cls: 'roo-brick-text',
35861                 html: this.html
35862             });
35863         } else {
35864             cn.cls += ' hide';
35865         }
35866         
35867         if(this.bgimage.length){
35868             cfg.cn.push({
35869                 tag: 'img',
35870                 cls: 'roo-brick-image-view',
35871                 src: this.bgimage
35872             });
35873         }
35874         
35875         return cfg;
35876     },
35877     
35878     initEvents: function() 
35879     {
35880         if(this.title.length || this.html.length){
35881             this.el.on('mouseenter'  ,this.enter, this);
35882             this.el.on('mouseleave', this.leave, this);
35883         }
35884         
35885         Roo.EventManager.onWindowResize(this.resize, this); 
35886         
35887         if(this.bgimage.length){
35888             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35889             this.imageEl.on('load', this.onImageLoad, this);
35890             return;
35891         }
35892         
35893         this.resize();
35894     },
35895     
35896     onImageLoad : function()
35897     {
35898         this.resize();
35899     },
35900     
35901     resize : function()
35902     {
35903         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35904         
35905         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35906         
35907         if(this.bgimage.length){
35908             var image = this.el.select('.roo-brick-image-view', true).first();
35909             
35910             image.setWidth(paragraph.getWidth());
35911             
35912             if(this.square){
35913                 image.setHeight(paragraph.getWidth());
35914             }
35915             
35916             this.el.setHeight(image.getHeight());
35917             paragraph.setHeight(image.getHeight());
35918             
35919         }
35920         
35921     },
35922     
35923     enter: function(e, el)
35924     {
35925         e.preventDefault();
35926         
35927         if(this.bgimage.length){
35928             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35929             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35930         }
35931     },
35932     
35933     leave: function(e, el)
35934     {
35935         e.preventDefault();
35936         
35937         if(this.bgimage.length){
35938             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35939             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35940         }
35941     }
35942     
35943 });
35944
35945  
35946
35947  /*
35948  * - LGPL
35949  *
35950  * Number field 
35951  */
35952
35953 /**
35954  * @class Roo.bootstrap.NumberField
35955  * @extends Roo.bootstrap.Input
35956  * Bootstrap NumberField class
35957  * 
35958  * 
35959  * 
35960  * 
35961  * @constructor
35962  * Create a new NumberField
35963  * @param {Object} config The config object
35964  */
35965
35966 Roo.bootstrap.NumberField = function(config){
35967     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35968 };
35969
35970 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35971     
35972     /**
35973      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35974      */
35975     allowDecimals : true,
35976     /**
35977      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35978      */
35979     decimalSeparator : ".",
35980     /**
35981      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35982      */
35983     decimalPrecision : 2,
35984     /**
35985      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35986      */
35987     allowNegative : true,
35988     
35989     /**
35990      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35991      */
35992     allowZero: true,
35993     /**
35994      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35995      */
35996     minValue : Number.NEGATIVE_INFINITY,
35997     /**
35998      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35999      */
36000     maxValue : Number.MAX_VALUE,
36001     /**
36002      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36003      */
36004     minText : "The minimum value for this field is {0}",
36005     /**
36006      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36007      */
36008     maxText : "The maximum value for this field is {0}",
36009     /**
36010      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36011      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36012      */
36013     nanText : "{0} is not a valid number",
36014     /**
36015      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36016      */
36017     thousandsDelimiter : false,
36018     /**
36019      * @cfg {String} valueAlign alignment of value
36020      */
36021     valueAlign : "left",
36022
36023     getAutoCreate : function()
36024     {
36025         var hiddenInput = {
36026             tag: 'input',
36027             type: 'hidden',
36028             id: Roo.id(),
36029             cls: 'hidden-number-input'
36030         };
36031         
36032         if (this.name) {
36033             hiddenInput.name = this.name;
36034         }
36035         
36036         this.name = '';
36037         
36038         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36039         
36040         this.name = hiddenInput.name;
36041         
36042         if(cfg.cn.length > 0) {
36043             cfg.cn.push(hiddenInput);
36044         }
36045         
36046         return cfg;
36047     },
36048
36049     // private
36050     initEvents : function()
36051     {   
36052         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36053         
36054         var allowed = "0123456789";
36055         
36056         if(this.allowDecimals){
36057             allowed += this.decimalSeparator;
36058         }
36059         
36060         if(this.allowNegative){
36061             allowed += "-";
36062         }
36063         
36064         if(this.thousandsDelimiter) {
36065             allowed += ",";
36066         }
36067         
36068         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36069         
36070         var keyPress = function(e){
36071             
36072             var k = e.getKey();
36073             
36074             var c = e.getCharCode();
36075             
36076             if(
36077                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36078                     allowed.indexOf(String.fromCharCode(c)) === -1
36079             ){
36080                 e.stopEvent();
36081                 return;
36082             }
36083             
36084             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36085                 return;
36086             }
36087             
36088             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36089                 e.stopEvent();
36090             }
36091         };
36092         
36093         this.el.on("keypress", keyPress, this);
36094     },
36095     
36096     validateValue : function(value)
36097     {
36098         
36099         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36100             return false;
36101         }
36102         
36103         var num = this.parseValue(value);
36104         
36105         if(isNaN(num)){
36106             this.markInvalid(String.format(this.nanText, value));
36107             return false;
36108         }
36109         
36110         if(num < this.minValue){
36111             this.markInvalid(String.format(this.minText, this.minValue));
36112             return false;
36113         }
36114         
36115         if(num > this.maxValue){
36116             this.markInvalid(String.format(this.maxText, this.maxValue));
36117             return false;
36118         }
36119         
36120         return true;
36121     },
36122
36123     getValue : function()
36124     {
36125         var v = this.hiddenEl().getValue();
36126         
36127         return this.fixPrecision(this.parseValue(v));
36128     },
36129
36130     parseValue : function(value)
36131     {
36132         if(this.thousandsDelimiter) {
36133             value += "";
36134             r = new RegExp(",", "g");
36135             value = value.replace(r, "");
36136         }
36137         
36138         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36139         return isNaN(value) ? '' : value;
36140     },
36141
36142     fixPrecision : function(value)
36143     {
36144         if(this.thousandsDelimiter) {
36145             value += "";
36146             r = new RegExp(",", "g");
36147             value = value.replace(r, "");
36148         }
36149         
36150         var nan = isNaN(value);
36151         
36152         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36153             return nan ? '' : value;
36154         }
36155         return parseFloat(value).toFixed(this.decimalPrecision);
36156     },
36157
36158     setValue : function(v)
36159     {
36160         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36161         
36162         this.value = v;
36163         
36164         if(this.rendered){
36165             
36166             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36167             
36168             this.inputEl().dom.value = (v == '') ? '' :
36169                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36170             
36171             if(!this.allowZero && v === '0') {
36172                 this.hiddenEl().dom.value = '';
36173                 this.inputEl().dom.value = '';
36174             }
36175             
36176             this.validate();
36177         }
36178     },
36179
36180     decimalPrecisionFcn : function(v)
36181     {
36182         return Math.floor(v);
36183     },
36184
36185     beforeBlur : function()
36186     {
36187         var v = this.parseValue(this.getRawValue());
36188         
36189         if(v || v === 0 || v === ''){
36190             this.setValue(v);
36191         }
36192     },
36193     
36194     hiddenEl : function()
36195     {
36196         return this.el.select('input.hidden-number-input',true).first();
36197     }
36198     
36199 });
36200
36201  
36202
36203 /*
36204 * Licence: LGPL
36205 */
36206
36207 /**
36208  * @class Roo.bootstrap.DocumentSlider
36209  * @extends Roo.bootstrap.Component
36210  * Bootstrap DocumentSlider class
36211  * 
36212  * @constructor
36213  * Create a new DocumentViewer
36214  * @param {Object} config The config object
36215  */
36216
36217 Roo.bootstrap.DocumentSlider = function(config){
36218     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36219     
36220     this.files = [];
36221     
36222     this.addEvents({
36223         /**
36224          * @event initial
36225          * Fire after initEvent
36226          * @param {Roo.bootstrap.DocumentSlider} this
36227          */
36228         "initial" : true,
36229         /**
36230          * @event update
36231          * Fire after update
36232          * @param {Roo.bootstrap.DocumentSlider} this
36233          */
36234         "update" : true,
36235         /**
36236          * @event click
36237          * Fire after click
36238          * @param {Roo.bootstrap.DocumentSlider} this
36239          */
36240         "click" : true
36241     });
36242 };
36243
36244 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36245     
36246     files : false,
36247     
36248     indicator : 0,
36249     
36250     getAutoCreate : function()
36251     {
36252         var cfg = {
36253             tag : 'div',
36254             cls : 'roo-document-slider',
36255             cn : [
36256                 {
36257                     tag : 'div',
36258                     cls : 'roo-document-slider-header',
36259                     cn : [
36260                         {
36261                             tag : 'div',
36262                             cls : 'roo-document-slider-header-title'
36263                         }
36264                     ]
36265                 },
36266                 {
36267                     tag : 'div',
36268                     cls : 'roo-document-slider-body',
36269                     cn : [
36270                         {
36271                             tag : 'div',
36272                             cls : 'roo-document-slider-prev',
36273                             cn : [
36274                                 {
36275                                     tag : 'i',
36276                                     cls : 'fa fa-chevron-left'
36277                                 }
36278                             ]
36279                         },
36280                         {
36281                             tag : 'div',
36282                             cls : 'roo-document-slider-thumb',
36283                             cn : [
36284                                 {
36285                                     tag : 'img',
36286                                     cls : 'roo-document-slider-image'
36287                                 }
36288                             ]
36289                         },
36290                         {
36291                             tag : 'div',
36292                             cls : 'roo-document-slider-next',
36293                             cn : [
36294                                 {
36295                                     tag : 'i',
36296                                     cls : 'fa fa-chevron-right'
36297                                 }
36298                             ]
36299                         }
36300                     ]
36301                 }
36302             ]
36303         };
36304         
36305         return cfg;
36306     },
36307     
36308     initEvents : function()
36309     {
36310         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36311         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36312         
36313         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36314         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36315         
36316         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36317         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36318         
36319         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36320         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36321         
36322         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36323         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36324         
36325         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36326         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36327         
36328         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36329         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36330         
36331         this.thumbEl.on('click', this.onClick, this);
36332         
36333         this.prevIndicator.on('click', this.prev, this);
36334         
36335         this.nextIndicator.on('click', this.next, this);
36336         
36337     },
36338     
36339     initial : function()
36340     {
36341         if(this.files.length){
36342             this.indicator = 1;
36343             this.update()
36344         }
36345         
36346         this.fireEvent('initial', this);
36347     },
36348     
36349     update : function()
36350     {
36351         this.imageEl.attr('src', this.files[this.indicator - 1]);
36352         
36353         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36354         
36355         this.prevIndicator.show();
36356         
36357         if(this.indicator == 1){
36358             this.prevIndicator.hide();
36359         }
36360         
36361         this.nextIndicator.show();
36362         
36363         if(this.indicator == this.files.length){
36364             this.nextIndicator.hide();
36365         }
36366         
36367         this.thumbEl.scrollTo('top');
36368         
36369         this.fireEvent('update', this);
36370     },
36371     
36372     onClick : function(e)
36373     {
36374         e.preventDefault();
36375         
36376         this.fireEvent('click', this);
36377     },
36378     
36379     prev : function(e)
36380     {
36381         e.preventDefault();
36382         
36383         this.indicator = Math.max(1, this.indicator - 1);
36384         
36385         this.update();
36386     },
36387     
36388     next : function(e)
36389     {
36390         e.preventDefault();
36391         
36392         this.indicator = Math.min(this.files.length, this.indicator + 1);
36393         
36394         this.update();
36395     }
36396 });
36397 /*
36398  * - LGPL
36399  *
36400  * RadioSet
36401  *
36402  *
36403  */
36404
36405 /**
36406  * @class Roo.bootstrap.RadioSet
36407  * @extends Roo.bootstrap.Input
36408  * Bootstrap RadioSet class
36409  * @cfg {String} indicatorpos (left|right) default left
36410  * @cfg {Boolean} inline (true|false) inline the element (default true)
36411  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36412  * @constructor
36413  * Create a new RadioSet
36414  * @param {Object} config The config object
36415  */
36416
36417 Roo.bootstrap.RadioSet = function(config){
36418     
36419     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36420     
36421     this.radioes = [];
36422     
36423     Roo.bootstrap.RadioSet.register(this);
36424     
36425     this.addEvents({
36426         /**
36427         * @event check
36428         * Fires when the element is checked or unchecked.
36429         * @param {Roo.bootstrap.RadioSet} this This radio
36430         * @param {Roo.bootstrap.Radio} item The checked item
36431         */
36432        check : true,
36433        /**
36434         * @event click
36435         * Fires when the element is click.
36436         * @param {Roo.bootstrap.RadioSet} this This radio set
36437         * @param {Roo.bootstrap.Radio} item The checked item
36438         * @param {Roo.EventObject} e The event object
36439         */
36440        click : true
36441     });
36442     
36443 };
36444
36445 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36446
36447     radioes : false,
36448     
36449     inline : true,
36450     
36451     weight : '',
36452     
36453     indicatorpos : 'left',
36454     
36455     getAutoCreate : function()
36456     {
36457         var label = {
36458             tag : 'label',
36459             cls : 'roo-radio-set-label',
36460             cn : [
36461                 {
36462                     tag : 'span',
36463                     html : this.fieldLabel
36464                 }
36465             ]
36466         };
36467         if (Roo.bootstrap.version == 3) {
36468             
36469             
36470             if(this.indicatorpos == 'left'){
36471                 label.cn.unshift({
36472                     tag : 'i',
36473                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36474                     tooltip : 'This field is required'
36475                 });
36476             } else {
36477                 label.cn.push({
36478                     tag : 'i',
36479                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36480                     tooltip : 'This field is required'
36481                 });
36482             }
36483         }
36484         var items = {
36485             tag : 'div',
36486             cls : 'roo-radio-set-items'
36487         };
36488         
36489         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36490         
36491         if (align === 'left' && this.fieldLabel.length) {
36492             
36493             items = {
36494                 cls : "roo-radio-set-right", 
36495                 cn: [
36496                     items
36497                 ]
36498             };
36499             
36500             if(this.labelWidth > 12){
36501                 label.style = "width: " + this.labelWidth + 'px';
36502             }
36503             
36504             if(this.labelWidth < 13 && this.labelmd == 0){
36505                 this.labelmd = this.labelWidth;
36506             }
36507             
36508             if(this.labellg > 0){
36509                 label.cls += ' col-lg-' + this.labellg;
36510                 items.cls += ' col-lg-' + (12 - this.labellg);
36511             }
36512             
36513             if(this.labelmd > 0){
36514                 label.cls += ' col-md-' + this.labelmd;
36515                 items.cls += ' col-md-' + (12 - this.labelmd);
36516             }
36517             
36518             if(this.labelsm > 0){
36519                 label.cls += ' col-sm-' + this.labelsm;
36520                 items.cls += ' col-sm-' + (12 - this.labelsm);
36521             }
36522             
36523             if(this.labelxs > 0){
36524                 label.cls += ' col-xs-' + this.labelxs;
36525                 items.cls += ' col-xs-' + (12 - this.labelxs);
36526             }
36527         }
36528         
36529         var cfg = {
36530             tag : 'div',
36531             cls : 'roo-radio-set',
36532             cn : [
36533                 {
36534                     tag : 'input',
36535                     cls : 'roo-radio-set-input',
36536                     type : 'hidden',
36537                     name : this.name,
36538                     value : this.value ? this.value :  ''
36539                 },
36540                 label,
36541                 items
36542             ]
36543         };
36544         
36545         if(this.weight.length){
36546             cfg.cls += ' roo-radio-' + this.weight;
36547         }
36548         
36549         if(this.inline) {
36550             cfg.cls += ' roo-radio-set-inline';
36551         }
36552         
36553         var settings=this;
36554         ['xs','sm','md','lg'].map(function(size){
36555             if (settings[size]) {
36556                 cfg.cls += ' col-' + size + '-' + settings[size];
36557             }
36558         });
36559         
36560         return cfg;
36561         
36562     },
36563
36564     initEvents : function()
36565     {
36566         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36567         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36568         
36569         if(!this.fieldLabel.length){
36570             this.labelEl.hide();
36571         }
36572         
36573         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36574         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36575         
36576         this.indicator = this.indicatorEl();
36577         
36578         if(this.indicator){
36579             this.indicator.addClass('invisible');
36580         }
36581         
36582         this.originalValue = this.getValue();
36583         
36584     },
36585     
36586     inputEl: function ()
36587     {
36588         return this.el.select('.roo-radio-set-input', true).first();
36589     },
36590     
36591     getChildContainer : function()
36592     {
36593         return this.itemsEl;
36594     },
36595     
36596     register : function(item)
36597     {
36598         this.radioes.push(item);
36599         
36600     },
36601     
36602     validate : function()
36603     {   
36604         if(this.getVisibilityEl().hasClass('hidden')){
36605             return true;
36606         }
36607         
36608         var valid = false;
36609         
36610         Roo.each(this.radioes, function(i){
36611             if(!i.checked){
36612                 return;
36613             }
36614             
36615             valid = true;
36616             return false;
36617         });
36618         
36619         if(this.allowBlank) {
36620             return true;
36621         }
36622         
36623         if(this.disabled || valid){
36624             this.markValid();
36625             return true;
36626         }
36627         
36628         this.markInvalid();
36629         return false;
36630         
36631     },
36632     
36633     markValid : function()
36634     {
36635         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36636             this.indicatorEl().removeClass('visible');
36637             this.indicatorEl().addClass('invisible');
36638         }
36639         
36640         
36641         if (Roo.bootstrap.version == 3) {
36642             this.el.removeClass([this.invalidClass, this.validClass]);
36643             this.el.addClass(this.validClass);
36644         } else {
36645             this.el.removeClass(['is-invalid','is-valid']);
36646             this.el.addClass(['is-valid']);
36647         }
36648         this.fireEvent('valid', this);
36649     },
36650     
36651     markInvalid : function(msg)
36652     {
36653         if(this.allowBlank || this.disabled){
36654             return;
36655         }
36656         
36657         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36658             this.indicatorEl().removeClass('invisible');
36659             this.indicatorEl().addClass('visible');
36660         }
36661         if (Roo.bootstrap.version == 3) {
36662             this.el.removeClass([this.invalidClass, this.validClass]);
36663             this.el.addClass(this.invalidClass);
36664         } else {
36665             this.el.removeClass(['is-invalid','is-valid']);
36666             this.el.addClass(['is-invalid']);
36667         }
36668         
36669         this.fireEvent('invalid', this, msg);
36670         
36671     },
36672     
36673     setValue : function(v, suppressEvent)
36674     {   
36675         if(this.value === v){
36676             return;
36677         }
36678         
36679         this.value = v;
36680         
36681         if(this.rendered){
36682             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36683         }
36684         
36685         Roo.each(this.radioes, function(i){
36686             i.checked = false;
36687             i.el.removeClass('checked');
36688         });
36689         
36690         Roo.each(this.radioes, function(i){
36691             
36692             if(i.value === v || i.value.toString() === v.toString()){
36693                 i.checked = true;
36694                 i.el.addClass('checked');
36695                 
36696                 if(suppressEvent !== true){
36697                     this.fireEvent('check', this, i);
36698                 }
36699                 
36700                 return false;
36701             }
36702             
36703         }, this);
36704         
36705         this.validate();
36706     },
36707     
36708     clearInvalid : function(){
36709         
36710         if(!this.el || this.preventMark){
36711             return;
36712         }
36713         
36714         this.el.removeClass([this.invalidClass]);
36715         
36716         this.fireEvent('valid', this);
36717     }
36718     
36719 });
36720
36721 Roo.apply(Roo.bootstrap.RadioSet, {
36722     
36723     groups: {},
36724     
36725     register : function(set)
36726     {
36727         this.groups[set.name] = set;
36728     },
36729     
36730     get: function(name) 
36731     {
36732         if (typeof(this.groups[name]) == 'undefined') {
36733             return false;
36734         }
36735         
36736         return this.groups[name] ;
36737     }
36738     
36739 });
36740 /*
36741  * Based on:
36742  * Ext JS Library 1.1.1
36743  * Copyright(c) 2006-2007, Ext JS, LLC.
36744  *
36745  * Originally Released Under LGPL - original licence link has changed is not relivant.
36746  *
36747  * Fork - LGPL
36748  * <script type="text/javascript">
36749  */
36750
36751
36752 /**
36753  * @class Roo.bootstrap.SplitBar
36754  * @extends Roo.util.Observable
36755  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36756  * <br><br>
36757  * Usage:
36758  * <pre><code>
36759 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36760                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36761 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36762 split.minSize = 100;
36763 split.maxSize = 600;
36764 split.animate = true;
36765 split.on('moved', splitterMoved);
36766 </code></pre>
36767  * @constructor
36768  * Create a new SplitBar
36769  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36770  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36771  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36772  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36773                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36774                         position of the SplitBar).
36775  */
36776 Roo.bootstrap.SplitBar = function(cfg){
36777     
36778     /** @private */
36779     
36780     //{
36781     //  dragElement : elm
36782     //  resizingElement: el,
36783         // optional..
36784     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36785     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36786         // existingProxy ???
36787     //}
36788     
36789     this.el = Roo.get(cfg.dragElement, true);
36790     this.el.dom.unselectable = "on";
36791     /** @private */
36792     this.resizingEl = Roo.get(cfg.resizingElement, true);
36793
36794     /**
36795      * @private
36796      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36797      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36798      * @type Number
36799      */
36800     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36801     
36802     /**
36803      * The minimum size of the resizing element. (Defaults to 0)
36804      * @type Number
36805      */
36806     this.minSize = 0;
36807     
36808     /**
36809      * The maximum size of the resizing element. (Defaults to 2000)
36810      * @type Number
36811      */
36812     this.maxSize = 2000;
36813     
36814     /**
36815      * Whether to animate the transition to the new size
36816      * @type Boolean
36817      */
36818     this.animate = false;
36819     
36820     /**
36821      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36822      * @type Boolean
36823      */
36824     this.useShim = false;
36825     
36826     /** @private */
36827     this.shim = null;
36828     
36829     if(!cfg.existingProxy){
36830         /** @private */
36831         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36832     }else{
36833         this.proxy = Roo.get(cfg.existingProxy).dom;
36834     }
36835     /** @private */
36836     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36837     
36838     /** @private */
36839     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36840     
36841     /** @private */
36842     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36843     
36844     /** @private */
36845     this.dragSpecs = {};
36846     
36847     /**
36848      * @private The adapter to use to positon and resize elements
36849      */
36850     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36851     this.adapter.init(this);
36852     
36853     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36854         /** @private */
36855         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36856         this.el.addClass("roo-splitbar-h");
36857     }else{
36858         /** @private */
36859         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36860         this.el.addClass("roo-splitbar-v");
36861     }
36862     
36863     this.addEvents({
36864         /**
36865          * @event resize
36866          * Fires when the splitter is moved (alias for {@link #event-moved})
36867          * @param {Roo.bootstrap.SplitBar} this
36868          * @param {Number} newSize the new width or height
36869          */
36870         "resize" : true,
36871         /**
36872          * @event moved
36873          * Fires when the splitter is moved
36874          * @param {Roo.bootstrap.SplitBar} this
36875          * @param {Number} newSize the new width or height
36876          */
36877         "moved" : true,
36878         /**
36879          * @event beforeresize
36880          * Fires before the splitter is dragged
36881          * @param {Roo.bootstrap.SplitBar} this
36882          */
36883         "beforeresize" : true,
36884
36885         "beforeapply" : true
36886     });
36887
36888     Roo.util.Observable.call(this);
36889 };
36890
36891 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36892     onStartProxyDrag : function(x, y){
36893         this.fireEvent("beforeresize", this);
36894         if(!this.overlay){
36895             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36896             o.unselectable();
36897             o.enableDisplayMode("block");
36898             // all splitbars share the same overlay
36899             Roo.bootstrap.SplitBar.prototype.overlay = o;
36900         }
36901         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36902         this.overlay.show();
36903         Roo.get(this.proxy).setDisplayed("block");
36904         var size = this.adapter.getElementSize(this);
36905         this.activeMinSize = this.getMinimumSize();;
36906         this.activeMaxSize = this.getMaximumSize();;
36907         var c1 = size - this.activeMinSize;
36908         var c2 = Math.max(this.activeMaxSize - size, 0);
36909         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36910             this.dd.resetConstraints();
36911             this.dd.setXConstraint(
36912                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36913                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36914             );
36915             this.dd.setYConstraint(0, 0);
36916         }else{
36917             this.dd.resetConstraints();
36918             this.dd.setXConstraint(0, 0);
36919             this.dd.setYConstraint(
36920                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36921                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36922             );
36923          }
36924         this.dragSpecs.startSize = size;
36925         this.dragSpecs.startPoint = [x, y];
36926         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36927     },
36928     
36929     /** 
36930      * @private Called after the drag operation by the DDProxy
36931      */
36932     onEndProxyDrag : function(e){
36933         Roo.get(this.proxy).setDisplayed(false);
36934         var endPoint = Roo.lib.Event.getXY(e);
36935         if(this.overlay){
36936             this.overlay.hide();
36937         }
36938         var newSize;
36939         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36940             newSize = this.dragSpecs.startSize + 
36941                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36942                     endPoint[0] - this.dragSpecs.startPoint[0] :
36943                     this.dragSpecs.startPoint[0] - endPoint[0]
36944                 );
36945         }else{
36946             newSize = this.dragSpecs.startSize + 
36947                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36948                     endPoint[1] - this.dragSpecs.startPoint[1] :
36949                     this.dragSpecs.startPoint[1] - endPoint[1]
36950                 );
36951         }
36952         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36953         if(newSize != this.dragSpecs.startSize){
36954             if(this.fireEvent('beforeapply', this, newSize) !== false){
36955                 this.adapter.setElementSize(this, newSize);
36956                 this.fireEvent("moved", this, newSize);
36957                 this.fireEvent("resize", this, newSize);
36958             }
36959         }
36960     },
36961     
36962     /**
36963      * Get the adapter this SplitBar uses
36964      * @return The adapter object
36965      */
36966     getAdapter : function(){
36967         return this.adapter;
36968     },
36969     
36970     /**
36971      * Set the adapter this SplitBar uses
36972      * @param {Object} adapter A SplitBar adapter object
36973      */
36974     setAdapter : function(adapter){
36975         this.adapter = adapter;
36976         this.adapter.init(this);
36977     },
36978     
36979     /**
36980      * Gets the minimum size for the resizing element
36981      * @return {Number} The minimum size
36982      */
36983     getMinimumSize : function(){
36984         return this.minSize;
36985     },
36986     
36987     /**
36988      * Sets the minimum size for the resizing element
36989      * @param {Number} minSize The minimum size
36990      */
36991     setMinimumSize : function(minSize){
36992         this.minSize = minSize;
36993     },
36994     
36995     /**
36996      * Gets the maximum size for the resizing element
36997      * @return {Number} The maximum size
36998      */
36999     getMaximumSize : function(){
37000         return this.maxSize;
37001     },
37002     
37003     /**
37004      * Sets the maximum size for the resizing element
37005      * @param {Number} maxSize The maximum size
37006      */
37007     setMaximumSize : function(maxSize){
37008         this.maxSize = maxSize;
37009     },
37010     
37011     /**
37012      * Sets the initialize size for the resizing element
37013      * @param {Number} size The initial size
37014      */
37015     setCurrentSize : function(size){
37016         var oldAnimate = this.animate;
37017         this.animate = false;
37018         this.adapter.setElementSize(this, size);
37019         this.animate = oldAnimate;
37020     },
37021     
37022     /**
37023      * Destroy this splitbar. 
37024      * @param {Boolean} removeEl True to remove the element
37025      */
37026     destroy : function(removeEl){
37027         if(this.shim){
37028             this.shim.remove();
37029         }
37030         this.dd.unreg();
37031         this.proxy.parentNode.removeChild(this.proxy);
37032         if(removeEl){
37033             this.el.remove();
37034         }
37035     }
37036 });
37037
37038 /**
37039  * @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.
37040  */
37041 Roo.bootstrap.SplitBar.createProxy = function(dir){
37042     var proxy = new Roo.Element(document.createElement("div"));
37043     proxy.unselectable();
37044     var cls = 'roo-splitbar-proxy';
37045     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37046     document.body.appendChild(proxy.dom);
37047     return proxy.dom;
37048 };
37049
37050 /** 
37051  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37052  * Default Adapter. It assumes the splitter and resizing element are not positioned
37053  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37054  */
37055 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37056 };
37057
37058 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37059     // do nothing for now
37060     init : function(s){
37061     
37062     },
37063     /**
37064      * Called before drag operations to get the current size of the resizing element. 
37065      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37066      */
37067      getElementSize : function(s){
37068         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37069             return s.resizingEl.getWidth();
37070         }else{
37071             return s.resizingEl.getHeight();
37072         }
37073     },
37074     
37075     /**
37076      * Called after drag operations to set the size of the resizing element.
37077      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37078      * @param {Number} newSize The new size to set
37079      * @param {Function} onComplete A function to be invoked when resizing is complete
37080      */
37081     setElementSize : function(s, newSize, onComplete){
37082         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37083             if(!s.animate){
37084                 s.resizingEl.setWidth(newSize);
37085                 if(onComplete){
37086                     onComplete(s, newSize);
37087                 }
37088             }else{
37089                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37090             }
37091         }else{
37092             
37093             if(!s.animate){
37094                 s.resizingEl.setHeight(newSize);
37095                 if(onComplete){
37096                     onComplete(s, newSize);
37097                 }
37098             }else{
37099                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37100             }
37101         }
37102     }
37103 };
37104
37105 /** 
37106  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37107  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37108  * Adapter that  moves the splitter element to align with the resized sizing element. 
37109  * Used with an absolute positioned SplitBar.
37110  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37111  * document.body, make sure you assign an id to the body element.
37112  */
37113 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37114     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37115     this.container = Roo.get(container);
37116 };
37117
37118 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37119     init : function(s){
37120         this.basic.init(s);
37121     },
37122     
37123     getElementSize : function(s){
37124         return this.basic.getElementSize(s);
37125     },
37126     
37127     setElementSize : function(s, newSize, onComplete){
37128         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37129     },
37130     
37131     moveSplitter : function(s){
37132         var yes = Roo.bootstrap.SplitBar;
37133         switch(s.placement){
37134             case yes.LEFT:
37135                 s.el.setX(s.resizingEl.getRight());
37136                 break;
37137             case yes.RIGHT:
37138                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37139                 break;
37140             case yes.TOP:
37141                 s.el.setY(s.resizingEl.getBottom());
37142                 break;
37143             case yes.BOTTOM:
37144                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37145                 break;
37146         }
37147     }
37148 };
37149
37150 /**
37151  * Orientation constant - Create a vertical SplitBar
37152  * @static
37153  * @type Number
37154  */
37155 Roo.bootstrap.SplitBar.VERTICAL = 1;
37156
37157 /**
37158  * Orientation constant - Create a horizontal SplitBar
37159  * @static
37160  * @type Number
37161  */
37162 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37163
37164 /**
37165  * Placement constant - The resizing element is to the left of the splitter element
37166  * @static
37167  * @type Number
37168  */
37169 Roo.bootstrap.SplitBar.LEFT = 1;
37170
37171 /**
37172  * Placement constant - The resizing element is to the right of the splitter element
37173  * @static
37174  * @type Number
37175  */
37176 Roo.bootstrap.SplitBar.RIGHT = 2;
37177
37178 /**
37179  * Placement constant - The resizing element is positioned above the splitter element
37180  * @static
37181  * @type Number
37182  */
37183 Roo.bootstrap.SplitBar.TOP = 3;
37184
37185 /**
37186  * Placement constant - The resizing element is positioned under splitter element
37187  * @static
37188  * @type Number
37189  */
37190 Roo.bootstrap.SplitBar.BOTTOM = 4;
37191 Roo.namespace("Roo.bootstrap.layout");/*
37192  * Based on:
37193  * Ext JS Library 1.1.1
37194  * Copyright(c) 2006-2007, Ext JS, LLC.
37195  *
37196  * Originally Released Under LGPL - original licence link has changed is not relivant.
37197  *
37198  * Fork - LGPL
37199  * <script type="text/javascript">
37200  */
37201
37202 /**
37203  * @class Roo.bootstrap.layout.Manager
37204  * @extends Roo.bootstrap.Component
37205  * Base class for layout managers.
37206  */
37207 Roo.bootstrap.layout.Manager = function(config)
37208 {
37209     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37210
37211
37212
37213
37214
37215     /** false to disable window resize monitoring @type Boolean */
37216     this.monitorWindowResize = true;
37217     this.regions = {};
37218     this.addEvents({
37219         /**
37220          * @event layout
37221          * Fires when a layout is performed.
37222          * @param {Roo.LayoutManager} this
37223          */
37224         "layout" : true,
37225         /**
37226          * @event regionresized
37227          * Fires when the user resizes a region.
37228          * @param {Roo.LayoutRegion} region The resized region
37229          * @param {Number} newSize The new size (width for east/west, height for north/south)
37230          */
37231         "regionresized" : true,
37232         /**
37233          * @event regioncollapsed
37234          * Fires when a region is collapsed.
37235          * @param {Roo.LayoutRegion} region The collapsed region
37236          */
37237         "regioncollapsed" : true,
37238         /**
37239          * @event regionexpanded
37240          * Fires when a region is expanded.
37241          * @param {Roo.LayoutRegion} region The expanded region
37242          */
37243         "regionexpanded" : true
37244     });
37245     this.updating = false;
37246
37247     if (config.el) {
37248         this.el = Roo.get(config.el);
37249         this.initEvents();
37250     }
37251
37252 };
37253
37254 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37255
37256
37257     regions : null,
37258
37259     monitorWindowResize : true,
37260
37261
37262     updating : false,
37263
37264
37265     onRender : function(ct, position)
37266     {
37267         if(!this.el){
37268             this.el = Roo.get(ct);
37269             this.initEvents();
37270         }
37271         //this.fireEvent('render',this);
37272     },
37273
37274
37275     initEvents: function()
37276     {
37277
37278
37279         // ie scrollbar fix
37280         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37281             document.body.scroll = "no";
37282         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37283             this.el.position('relative');
37284         }
37285         this.id = this.el.id;
37286         this.el.addClass("roo-layout-container");
37287         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37288         if(this.el.dom != document.body ) {
37289             this.el.on('resize', this.layout,this);
37290             this.el.on('show', this.layout,this);
37291         }
37292
37293     },
37294
37295     /**
37296      * Returns true if this layout is currently being updated
37297      * @return {Boolean}
37298      */
37299     isUpdating : function(){
37300         return this.updating;
37301     },
37302
37303     /**
37304      * Suspend the LayoutManager from doing auto-layouts while
37305      * making multiple add or remove calls
37306      */
37307     beginUpdate : function(){
37308         this.updating = true;
37309     },
37310
37311     /**
37312      * Restore auto-layouts and optionally disable the manager from performing a layout
37313      * @param {Boolean} noLayout true to disable a layout update
37314      */
37315     endUpdate : function(noLayout){
37316         this.updating = false;
37317         if(!noLayout){
37318             this.layout();
37319         }
37320     },
37321
37322     layout: function(){
37323         // abstract...
37324     },
37325
37326     onRegionResized : function(region, newSize){
37327         this.fireEvent("regionresized", region, newSize);
37328         this.layout();
37329     },
37330
37331     onRegionCollapsed : function(region){
37332         this.fireEvent("regioncollapsed", region);
37333     },
37334
37335     onRegionExpanded : function(region){
37336         this.fireEvent("regionexpanded", region);
37337     },
37338
37339     /**
37340      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37341      * performs box-model adjustments.
37342      * @return {Object} The size as an object {width: (the width), height: (the height)}
37343      */
37344     getViewSize : function()
37345     {
37346         var size;
37347         if(this.el.dom != document.body){
37348             size = this.el.getSize();
37349         }else{
37350             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37351         }
37352         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37353         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37354         return size;
37355     },
37356
37357     /**
37358      * Returns the Element this layout is bound to.
37359      * @return {Roo.Element}
37360      */
37361     getEl : function(){
37362         return this.el;
37363     },
37364
37365     /**
37366      * Returns the specified region.
37367      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37368      * @return {Roo.LayoutRegion}
37369      */
37370     getRegion : function(target){
37371         return this.regions[target.toLowerCase()];
37372     },
37373
37374     onWindowResize : function(){
37375         if(this.monitorWindowResize){
37376             this.layout();
37377         }
37378     }
37379 });
37380 /*
37381  * Based on:
37382  * Ext JS Library 1.1.1
37383  * Copyright(c) 2006-2007, Ext JS, LLC.
37384  *
37385  * Originally Released Under LGPL - original licence link has changed is not relivant.
37386  *
37387  * Fork - LGPL
37388  * <script type="text/javascript">
37389  */
37390 /**
37391  * @class Roo.bootstrap.layout.Border
37392  * @extends Roo.bootstrap.layout.Manager
37393  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37394  * please see: examples/bootstrap/nested.html<br><br>
37395  
37396 <b>The container the layout is rendered into can be either the body element or any other element.
37397 If it is not the body element, the container needs to either be an absolute positioned element,
37398 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37399 the container size if it is not the body element.</b>
37400
37401 * @constructor
37402 * Create a new Border
37403 * @param {Object} config Configuration options
37404  */
37405 Roo.bootstrap.layout.Border = function(config){
37406     config = config || {};
37407     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37408     
37409     
37410     
37411     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37412         if(config[region]){
37413             config[region].region = region;
37414             this.addRegion(config[region]);
37415         }
37416     },this);
37417     
37418 };
37419
37420 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37421
37422 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37423     
37424     parent : false, // this might point to a 'nest' or a ???
37425     
37426     /**
37427      * Creates and adds a new region if it doesn't already exist.
37428      * @param {String} target The target region key (north, south, east, west or center).
37429      * @param {Object} config The regions config object
37430      * @return {BorderLayoutRegion} The new region
37431      */
37432     addRegion : function(config)
37433     {
37434         if(!this.regions[config.region]){
37435             var r = this.factory(config);
37436             this.bindRegion(r);
37437         }
37438         return this.regions[config.region];
37439     },
37440
37441     // private (kinda)
37442     bindRegion : function(r){
37443         this.regions[r.config.region] = r;
37444         
37445         r.on("visibilitychange",    this.layout, this);
37446         r.on("paneladded",          this.layout, this);
37447         r.on("panelremoved",        this.layout, this);
37448         r.on("invalidated",         this.layout, this);
37449         r.on("resized",             this.onRegionResized, this);
37450         r.on("collapsed",           this.onRegionCollapsed, this);
37451         r.on("expanded",            this.onRegionExpanded, this);
37452     },
37453
37454     /**
37455      * Performs a layout update.
37456      */
37457     layout : function()
37458     {
37459         if(this.updating) {
37460             return;
37461         }
37462         
37463         // render all the rebions if they have not been done alreayd?
37464         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37465             if(this.regions[region] && !this.regions[region].bodyEl){
37466                 this.regions[region].onRender(this.el)
37467             }
37468         },this);
37469         
37470         var size = this.getViewSize();
37471         var w = size.width;
37472         var h = size.height;
37473         var centerW = w;
37474         var centerH = h;
37475         var centerY = 0;
37476         var centerX = 0;
37477         //var x = 0, y = 0;
37478
37479         var rs = this.regions;
37480         var north = rs["north"];
37481         var south = rs["south"]; 
37482         var west = rs["west"];
37483         var east = rs["east"];
37484         var center = rs["center"];
37485         //if(this.hideOnLayout){ // not supported anymore
37486             //c.el.setStyle("display", "none");
37487         //}
37488         if(north && north.isVisible()){
37489             var b = north.getBox();
37490             var m = north.getMargins();
37491             b.width = w - (m.left+m.right);
37492             b.x = m.left;
37493             b.y = m.top;
37494             centerY = b.height + b.y + m.bottom;
37495             centerH -= centerY;
37496             north.updateBox(this.safeBox(b));
37497         }
37498         if(south && south.isVisible()){
37499             var b = south.getBox();
37500             var m = south.getMargins();
37501             b.width = w - (m.left+m.right);
37502             b.x = m.left;
37503             var totalHeight = (b.height + m.top + m.bottom);
37504             b.y = h - totalHeight + m.top;
37505             centerH -= totalHeight;
37506             south.updateBox(this.safeBox(b));
37507         }
37508         if(west && west.isVisible()){
37509             var b = west.getBox();
37510             var m = west.getMargins();
37511             b.height = centerH - (m.top+m.bottom);
37512             b.x = m.left;
37513             b.y = centerY + m.top;
37514             var totalWidth = (b.width + m.left + m.right);
37515             centerX += totalWidth;
37516             centerW -= totalWidth;
37517             west.updateBox(this.safeBox(b));
37518         }
37519         if(east && east.isVisible()){
37520             var b = east.getBox();
37521             var m = east.getMargins();
37522             b.height = centerH - (m.top+m.bottom);
37523             var totalWidth = (b.width + m.left + m.right);
37524             b.x = w - totalWidth + m.left;
37525             b.y = centerY + m.top;
37526             centerW -= totalWidth;
37527             east.updateBox(this.safeBox(b));
37528         }
37529         if(center){
37530             var m = center.getMargins();
37531             var centerBox = {
37532                 x: centerX + m.left,
37533                 y: centerY + m.top,
37534                 width: centerW - (m.left+m.right),
37535                 height: centerH - (m.top+m.bottom)
37536             };
37537             //if(this.hideOnLayout){
37538                 //center.el.setStyle("display", "block");
37539             //}
37540             center.updateBox(this.safeBox(centerBox));
37541         }
37542         this.el.repaint();
37543         this.fireEvent("layout", this);
37544     },
37545
37546     // private
37547     safeBox : function(box){
37548         box.width = Math.max(0, box.width);
37549         box.height = Math.max(0, box.height);
37550         return box;
37551     },
37552
37553     /**
37554      * Adds a ContentPanel (or subclass) to this layout.
37555      * @param {String} target The target region key (north, south, east, west or center).
37556      * @param {Roo.ContentPanel} panel The panel to add
37557      * @return {Roo.ContentPanel} The added panel
37558      */
37559     add : function(target, panel){
37560          
37561         target = target.toLowerCase();
37562         return this.regions[target].add(panel);
37563     },
37564
37565     /**
37566      * Remove a ContentPanel (or subclass) to this layout.
37567      * @param {String} target The target region key (north, south, east, west or center).
37568      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37569      * @return {Roo.ContentPanel} The removed panel
37570      */
37571     remove : function(target, panel){
37572         target = target.toLowerCase();
37573         return this.regions[target].remove(panel);
37574     },
37575
37576     /**
37577      * Searches all regions for a panel with the specified id
37578      * @param {String} panelId
37579      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37580      */
37581     findPanel : function(panelId){
37582         var rs = this.regions;
37583         for(var target in rs){
37584             if(typeof rs[target] != "function"){
37585                 var p = rs[target].getPanel(panelId);
37586                 if(p){
37587                     return p;
37588                 }
37589             }
37590         }
37591         return null;
37592     },
37593
37594     /**
37595      * Searches all regions for a panel with the specified id and activates (shows) it.
37596      * @param {String/ContentPanel} panelId The panels id or the panel itself
37597      * @return {Roo.ContentPanel} The shown panel or null
37598      */
37599     showPanel : function(panelId) {
37600       var rs = this.regions;
37601       for(var target in rs){
37602          var r = rs[target];
37603          if(typeof r != "function"){
37604             if(r.hasPanel(panelId)){
37605                return r.showPanel(panelId);
37606             }
37607          }
37608       }
37609       return null;
37610    },
37611
37612    /**
37613      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37614      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37615      */
37616    /*
37617     restoreState : function(provider){
37618         if(!provider){
37619             provider = Roo.state.Manager;
37620         }
37621         var sm = new Roo.LayoutStateManager();
37622         sm.init(this, provider);
37623     },
37624 */
37625  
37626  
37627     /**
37628      * Adds a xtype elements to the layout.
37629      * <pre><code>
37630
37631 layout.addxtype({
37632        xtype : 'ContentPanel',
37633        region: 'west',
37634        items: [ .... ]
37635    }
37636 );
37637
37638 layout.addxtype({
37639         xtype : 'NestedLayoutPanel',
37640         region: 'west',
37641         layout: {
37642            center: { },
37643            west: { }   
37644         },
37645         items : [ ... list of content panels or nested layout panels.. ]
37646    }
37647 );
37648 </code></pre>
37649      * @param {Object} cfg Xtype definition of item to add.
37650      */
37651     addxtype : function(cfg)
37652     {
37653         // basically accepts a pannel...
37654         // can accept a layout region..!?!?
37655         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37656         
37657         
37658         // theory?  children can only be panels??
37659         
37660         //if (!cfg.xtype.match(/Panel$/)) {
37661         //    return false;
37662         //}
37663         var ret = false;
37664         
37665         if (typeof(cfg.region) == 'undefined') {
37666             Roo.log("Failed to add Panel, region was not set");
37667             Roo.log(cfg);
37668             return false;
37669         }
37670         var region = cfg.region;
37671         delete cfg.region;
37672         
37673           
37674         var xitems = [];
37675         if (cfg.items) {
37676             xitems = cfg.items;
37677             delete cfg.items;
37678         }
37679         var nb = false;
37680         
37681         if ( region == 'center') {
37682             Roo.log("Center: " + cfg.title);
37683         }
37684         
37685         
37686         switch(cfg.xtype) 
37687         {
37688             case 'Content':  // ContentPanel (el, cfg)
37689             case 'Scroll':  // ContentPanel (el, cfg)
37690             case 'View': 
37691                 cfg.autoCreate = cfg.autoCreate || true;
37692                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37693                 //} else {
37694                 //    var el = this.el.createChild();
37695                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37696                 //}
37697                 
37698                 this.add(region, ret);
37699                 break;
37700             
37701             /*
37702             case 'TreePanel': // our new panel!
37703                 cfg.el = this.el.createChild();
37704                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37705                 this.add(region, ret);
37706                 break;
37707             */
37708             
37709             case 'Nest': 
37710                 // create a new Layout (which is  a Border Layout...
37711                 
37712                 var clayout = cfg.layout;
37713                 clayout.el  = this.el.createChild();
37714                 clayout.items   = clayout.items  || [];
37715                 
37716                 delete cfg.layout;
37717                 
37718                 // replace this exitems with the clayout ones..
37719                 xitems = clayout.items;
37720                  
37721                 // force background off if it's in center...
37722                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37723                     cfg.background = false;
37724                 }
37725                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37726                 
37727                 
37728                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37729                 //console.log('adding nested layout panel '  + cfg.toSource());
37730                 this.add(region, ret);
37731                 nb = {}; /// find first...
37732                 break;
37733             
37734             case 'Grid':
37735                 
37736                 // needs grid and region
37737                 
37738                 //var el = this.getRegion(region).el.createChild();
37739                 /*
37740                  *var el = this.el.createChild();
37741                 // create the grid first...
37742                 cfg.grid.container = el;
37743                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37744                 */
37745                 
37746                 if (region == 'center' && this.active ) {
37747                     cfg.background = false;
37748                 }
37749                 
37750                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37751                 
37752                 this.add(region, ret);
37753                 /*
37754                 if (cfg.background) {
37755                     // render grid on panel activation (if panel background)
37756                     ret.on('activate', function(gp) {
37757                         if (!gp.grid.rendered) {
37758                     //        gp.grid.render(el);
37759                         }
37760                     });
37761                 } else {
37762                   //  cfg.grid.render(el);
37763                 }
37764                 */
37765                 break;
37766            
37767            
37768             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37769                 // it was the old xcomponent building that caused this before.
37770                 // espeically if border is the top element in the tree.
37771                 ret = this;
37772                 break; 
37773                 
37774                     
37775                 
37776                 
37777                 
37778             default:
37779                 /*
37780                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37781                     
37782                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37783                     this.add(region, ret);
37784                 } else {
37785                 */
37786                     Roo.log(cfg);
37787                     throw "Can not add '" + cfg.xtype + "' to Border";
37788                     return null;
37789              
37790                                 
37791              
37792         }
37793         this.beginUpdate();
37794         // add children..
37795         var region = '';
37796         var abn = {};
37797         Roo.each(xitems, function(i)  {
37798             region = nb && i.region ? i.region : false;
37799             
37800             var add = ret.addxtype(i);
37801            
37802             if (region) {
37803                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37804                 if (!i.background) {
37805                     abn[region] = nb[region] ;
37806                 }
37807             }
37808             
37809         });
37810         this.endUpdate();
37811
37812         // make the last non-background panel active..
37813         //if (nb) { Roo.log(abn); }
37814         if (nb) {
37815             
37816             for(var r in abn) {
37817                 region = this.getRegion(r);
37818                 if (region) {
37819                     // tried using nb[r], but it does not work..
37820                      
37821                     region.showPanel(abn[r]);
37822                    
37823                 }
37824             }
37825         }
37826         return ret;
37827         
37828     },
37829     
37830     
37831 // private
37832     factory : function(cfg)
37833     {
37834         
37835         var validRegions = Roo.bootstrap.layout.Border.regions;
37836
37837         var target = cfg.region;
37838         cfg.mgr = this;
37839         
37840         var r = Roo.bootstrap.layout;
37841         Roo.log(target);
37842         switch(target){
37843             case "north":
37844                 return new r.North(cfg);
37845             case "south":
37846                 return new r.South(cfg);
37847             case "east":
37848                 return new r.East(cfg);
37849             case "west":
37850                 return new r.West(cfg);
37851             case "center":
37852                 return new r.Center(cfg);
37853         }
37854         throw 'Layout region "'+target+'" not supported.';
37855     }
37856     
37857     
37858 });
37859  /*
37860  * Based on:
37861  * Ext JS Library 1.1.1
37862  * Copyright(c) 2006-2007, Ext JS, LLC.
37863  *
37864  * Originally Released Under LGPL - original licence link has changed is not relivant.
37865  *
37866  * Fork - LGPL
37867  * <script type="text/javascript">
37868  */
37869  
37870 /**
37871  * @class Roo.bootstrap.layout.Basic
37872  * @extends Roo.util.Observable
37873  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37874  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37875  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37876  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37877  * @cfg {string}   region  the region that it inhabits..
37878  * @cfg {bool}   skipConfig skip config?
37879  * 
37880
37881  */
37882 Roo.bootstrap.layout.Basic = function(config){
37883     
37884     this.mgr = config.mgr;
37885     
37886     this.position = config.region;
37887     
37888     var skipConfig = config.skipConfig;
37889     
37890     this.events = {
37891         /**
37892          * @scope Roo.BasicLayoutRegion
37893          */
37894         
37895         /**
37896          * @event beforeremove
37897          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37898          * @param {Roo.LayoutRegion} this
37899          * @param {Roo.ContentPanel} panel The panel
37900          * @param {Object} e The cancel event object
37901          */
37902         "beforeremove" : true,
37903         /**
37904          * @event invalidated
37905          * Fires when the layout for this region is changed.
37906          * @param {Roo.LayoutRegion} this
37907          */
37908         "invalidated" : true,
37909         /**
37910          * @event visibilitychange
37911          * Fires when this region is shown or hidden 
37912          * @param {Roo.LayoutRegion} this
37913          * @param {Boolean} visibility true or false
37914          */
37915         "visibilitychange" : true,
37916         /**
37917          * @event paneladded
37918          * Fires when a panel is added. 
37919          * @param {Roo.LayoutRegion} this
37920          * @param {Roo.ContentPanel} panel The panel
37921          */
37922         "paneladded" : true,
37923         /**
37924          * @event panelremoved
37925          * Fires when a panel is removed. 
37926          * @param {Roo.LayoutRegion} this
37927          * @param {Roo.ContentPanel} panel The panel
37928          */
37929         "panelremoved" : true,
37930         /**
37931          * @event beforecollapse
37932          * Fires when this region before collapse.
37933          * @param {Roo.LayoutRegion} this
37934          */
37935         "beforecollapse" : true,
37936         /**
37937          * @event collapsed
37938          * Fires when this region is collapsed.
37939          * @param {Roo.LayoutRegion} this
37940          */
37941         "collapsed" : true,
37942         /**
37943          * @event expanded
37944          * Fires when this region is expanded.
37945          * @param {Roo.LayoutRegion} this
37946          */
37947         "expanded" : true,
37948         /**
37949          * @event slideshow
37950          * Fires when this region is slid into view.
37951          * @param {Roo.LayoutRegion} this
37952          */
37953         "slideshow" : true,
37954         /**
37955          * @event slidehide
37956          * Fires when this region slides out of view. 
37957          * @param {Roo.LayoutRegion} this
37958          */
37959         "slidehide" : true,
37960         /**
37961          * @event panelactivated
37962          * Fires when a panel is activated. 
37963          * @param {Roo.LayoutRegion} this
37964          * @param {Roo.ContentPanel} panel The activated panel
37965          */
37966         "panelactivated" : true,
37967         /**
37968          * @event resized
37969          * Fires when the user resizes this region. 
37970          * @param {Roo.LayoutRegion} this
37971          * @param {Number} newSize The new size (width for east/west, height for north/south)
37972          */
37973         "resized" : true
37974     };
37975     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37976     this.panels = new Roo.util.MixedCollection();
37977     this.panels.getKey = this.getPanelId.createDelegate(this);
37978     this.box = null;
37979     this.activePanel = null;
37980     // ensure listeners are added...
37981     
37982     if (config.listeners || config.events) {
37983         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37984             listeners : config.listeners || {},
37985             events : config.events || {}
37986         });
37987     }
37988     
37989     if(skipConfig !== true){
37990         this.applyConfig(config);
37991     }
37992 };
37993
37994 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37995 {
37996     getPanelId : function(p){
37997         return p.getId();
37998     },
37999     
38000     applyConfig : function(config){
38001         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38002         this.config = config;
38003         
38004     },
38005     
38006     /**
38007      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38008      * the width, for horizontal (north, south) the height.
38009      * @param {Number} newSize The new width or height
38010      */
38011     resizeTo : function(newSize){
38012         var el = this.el ? this.el :
38013                  (this.activePanel ? this.activePanel.getEl() : null);
38014         if(el){
38015             switch(this.position){
38016                 case "east":
38017                 case "west":
38018                     el.setWidth(newSize);
38019                     this.fireEvent("resized", this, newSize);
38020                 break;
38021                 case "north":
38022                 case "south":
38023                     el.setHeight(newSize);
38024                     this.fireEvent("resized", this, newSize);
38025                 break;                
38026             }
38027         }
38028     },
38029     
38030     getBox : function(){
38031         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38032     },
38033     
38034     getMargins : function(){
38035         return this.margins;
38036     },
38037     
38038     updateBox : function(box){
38039         this.box = box;
38040         var el = this.activePanel.getEl();
38041         el.dom.style.left = box.x + "px";
38042         el.dom.style.top = box.y + "px";
38043         this.activePanel.setSize(box.width, box.height);
38044     },
38045     
38046     /**
38047      * Returns the container element for this region.
38048      * @return {Roo.Element}
38049      */
38050     getEl : function(){
38051         return this.activePanel;
38052     },
38053     
38054     /**
38055      * Returns true if this region is currently visible.
38056      * @return {Boolean}
38057      */
38058     isVisible : function(){
38059         return this.activePanel ? true : false;
38060     },
38061     
38062     setActivePanel : function(panel){
38063         panel = this.getPanel(panel);
38064         if(this.activePanel && this.activePanel != panel){
38065             this.activePanel.setActiveState(false);
38066             this.activePanel.getEl().setLeftTop(-10000,-10000);
38067         }
38068         this.activePanel = panel;
38069         panel.setActiveState(true);
38070         if(this.box){
38071             panel.setSize(this.box.width, this.box.height);
38072         }
38073         this.fireEvent("panelactivated", this, panel);
38074         this.fireEvent("invalidated");
38075     },
38076     
38077     /**
38078      * Show the specified panel.
38079      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38080      * @return {Roo.ContentPanel} The shown panel or null
38081      */
38082     showPanel : function(panel){
38083         panel = this.getPanel(panel);
38084         if(panel){
38085             this.setActivePanel(panel);
38086         }
38087         return panel;
38088     },
38089     
38090     /**
38091      * Get the active panel for this region.
38092      * @return {Roo.ContentPanel} The active panel or null
38093      */
38094     getActivePanel : function(){
38095         return this.activePanel;
38096     },
38097     
38098     /**
38099      * Add the passed ContentPanel(s)
38100      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38101      * @return {Roo.ContentPanel} The panel added (if only one was added)
38102      */
38103     add : function(panel){
38104         if(arguments.length > 1){
38105             for(var i = 0, len = arguments.length; i < len; i++) {
38106                 this.add(arguments[i]);
38107             }
38108             return null;
38109         }
38110         if(this.hasPanel(panel)){
38111             this.showPanel(panel);
38112             return panel;
38113         }
38114         var el = panel.getEl();
38115         if(el.dom.parentNode != this.mgr.el.dom){
38116             this.mgr.el.dom.appendChild(el.dom);
38117         }
38118         if(panel.setRegion){
38119             panel.setRegion(this);
38120         }
38121         this.panels.add(panel);
38122         el.setStyle("position", "absolute");
38123         if(!panel.background){
38124             this.setActivePanel(panel);
38125             if(this.config.initialSize && this.panels.getCount()==1){
38126                 this.resizeTo(this.config.initialSize);
38127             }
38128         }
38129         this.fireEvent("paneladded", this, panel);
38130         return panel;
38131     },
38132     
38133     /**
38134      * Returns true if the panel is in this region.
38135      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38136      * @return {Boolean}
38137      */
38138     hasPanel : function(panel){
38139         if(typeof panel == "object"){ // must be panel obj
38140             panel = panel.getId();
38141         }
38142         return this.getPanel(panel) ? true : false;
38143     },
38144     
38145     /**
38146      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38148      * @param {Boolean} preservePanel Overrides the config preservePanel option
38149      * @return {Roo.ContentPanel} The panel that was removed
38150      */
38151     remove : function(panel, preservePanel){
38152         panel = this.getPanel(panel);
38153         if(!panel){
38154             return null;
38155         }
38156         var e = {};
38157         this.fireEvent("beforeremove", this, panel, e);
38158         if(e.cancel === true){
38159             return null;
38160         }
38161         var panelId = panel.getId();
38162         this.panels.removeKey(panelId);
38163         return panel;
38164     },
38165     
38166     /**
38167      * Returns the panel specified or null if it's not in this region.
38168      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38169      * @return {Roo.ContentPanel}
38170      */
38171     getPanel : function(id){
38172         if(typeof id == "object"){ // must be panel obj
38173             return id;
38174         }
38175         return this.panels.get(id);
38176     },
38177     
38178     /**
38179      * Returns this regions position (north/south/east/west/center).
38180      * @return {String} 
38181      */
38182     getPosition: function(){
38183         return this.position;    
38184     }
38185 });/*
38186  * Based on:
38187  * Ext JS Library 1.1.1
38188  * Copyright(c) 2006-2007, Ext JS, LLC.
38189  *
38190  * Originally Released Under LGPL - original licence link has changed is not relivant.
38191  *
38192  * Fork - LGPL
38193  * <script type="text/javascript">
38194  */
38195  
38196 /**
38197  * @class Roo.bootstrap.layout.Region
38198  * @extends Roo.bootstrap.layout.Basic
38199  * This class represents a region in a layout manager.
38200  
38201  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38202  * @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})
38203  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38204  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38205  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38206  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38207  * @cfg {String}    title           The title for the region (overrides panel titles)
38208  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38209  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38210  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38211  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38212  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38213  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38214  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38215  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38216  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38217  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38218
38219  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38220  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38221  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38222  * @cfg {Number}    width           For East/West panels
38223  * @cfg {Number}    height          For North/South panels
38224  * @cfg {Boolean}   split           To show the splitter
38225  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38226  * 
38227  * @cfg {string}   cls             Extra CSS classes to add to region
38228  * 
38229  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38230  * @cfg {string}   region  the region that it inhabits..
38231  *
38232
38233  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38234  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38235
38236  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38237  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38238  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38239  */
38240 Roo.bootstrap.layout.Region = function(config)
38241 {
38242     this.applyConfig(config);
38243
38244     var mgr = config.mgr;
38245     var pos = config.region;
38246     config.skipConfig = true;
38247     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38248     
38249     if (mgr.el) {
38250         this.onRender(mgr.el);   
38251     }
38252      
38253     this.visible = true;
38254     this.collapsed = false;
38255     this.unrendered_panels = [];
38256 };
38257
38258 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38259
38260     position: '', // set by wrapper (eg. north/south etc..)
38261     unrendered_panels : null,  // unrendered panels.
38262     
38263     tabPosition : false,
38264     
38265     mgr: false, // points to 'Border'
38266     
38267     
38268     createBody : function(){
38269         /** This region's body element 
38270         * @type Roo.Element */
38271         this.bodyEl = this.el.createChild({
38272                 tag: "div",
38273                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38274         });
38275     },
38276
38277     onRender: function(ctr, pos)
38278     {
38279         var dh = Roo.DomHelper;
38280         /** This region's container element 
38281         * @type Roo.Element */
38282         this.el = dh.append(ctr.dom, {
38283                 tag: "div",
38284                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38285             }, true);
38286         /** This region's title element 
38287         * @type Roo.Element */
38288     
38289         this.titleEl = dh.append(this.el.dom,  {
38290                 tag: "div",
38291                 unselectable: "on",
38292                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38293                 children:[
38294                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38295                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38296                 ]
38297             }, true);
38298         
38299         this.titleEl.enableDisplayMode();
38300         /** This region's title text element 
38301         * @type HTMLElement */
38302         this.titleTextEl = this.titleEl.dom.firstChild;
38303         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38304         /*
38305         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38306         this.closeBtn.enableDisplayMode();
38307         this.closeBtn.on("click", this.closeClicked, this);
38308         this.closeBtn.hide();
38309     */
38310         this.createBody(this.config);
38311         if(this.config.hideWhenEmpty){
38312             this.hide();
38313             this.on("paneladded", this.validateVisibility, this);
38314             this.on("panelremoved", this.validateVisibility, this);
38315         }
38316         if(this.autoScroll){
38317             this.bodyEl.setStyle("overflow", "auto");
38318         }else{
38319             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38320         }
38321         //if(c.titlebar !== false){
38322             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38323                 this.titleEl.hide();
38324             }else{
38325                 this.titleEl.show();
38326                 if(this.config.title){
38327                     this.titleTextEl.innerHTML = this.config.title;
38328                 }
38329             }
38330         //}
38331         if(this.config.collapsed){
38332             this.collapse(true);
38333         }
38334         if(this.config.hidden){
38335             this.hide();
38336         }
38337         
38338         if (this.unrendered_panels && this.unrendered_panels.length) {
38339             for (var i =0;i< this.unrendered_panels.length; i++) {
38340                 this.add(this.unrendered_panels[i]);
38341             }
38342             this.unrendered_panels = null;
38343             
38344         }
38345         
38346     },
38347     
38348     applyConfig : function(c)
38349     {
38350         /*
38351          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38352             var dh = Roo.DomHelper;
38353             if(c.titlebar !== false){
38354                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38355                 this.collapseBtn.on("click", this.collapse, this);
38356                 this.collapseBtn.enableDisplayMode();
38357                 /*
38358                 if(c.showPin === true || this.showPin){
38359                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38360                     this.stickBtn.enableDisplayMode();
38361                     this.stickBtn.on("click", this.expand, this);
38362                     this.stickBtn.hide();
38363                 }
38364                 
38365             }
38366             */
38367             /** This region's collapsed element
38368             * @type Roo.Element */
38369             /*
38370              *
38371             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38372                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38373             ]}, true);
38374             
38375             if(c.floatable !== false){
38376                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38377                this.collapsedEl.on("click", this.collapseClick, this);
38378             }
38379
38380             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38381                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38382                    id: "message", unselectable: "on", style:{"float":"left"}});
38383                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38384              }
38385             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38386             this.expandBtn.on("click", this.expand, this);
38387             
38388         }
38389         
38390         if(this.collapseBtn){
38391             this.collapseBtn.setVisible(c.collapsible == true);
38392         }
38393         
38394         this.cmargins = c.cmargins || this.cmargins ||
38395                          (this.position == "west" || this.position == "east" ?
38396                              {top: 0, left: 2, right:2, bottom: 0} :
38397                              {top: 2, left: 0, right:0, bottom: 2});
38398         */
38399         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38400         
38401         
38402         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38403         
38404         this.autoScroll = c.autoScroll || false;
38405         
38406         
38407        
38408         
38409         this.duration = c.duration || .30;
38410         this.slideDuration = c.slideDuration || .45;
38411         this.config = c;
38412        
38413     },
38414     /**
38415      * Returns true if this region is currently visible.
38416      * @return {Boolean}
38417      */
38418     isVisible : function(){
38419         return this.visible;
38420     },
38421
38422     /**
38423      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38424      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38425      */
38426     //setCollapsedTitle : function(title){
38427     //    title = title || "&#160;";
38428      //   if(this.collapsedTitleTextEl){
38429       //      this.collapsedTitleTextEl.innerHTML = title;
38430        // }
38431     //},
38432
38433     getBox : function(){
38434         var b;
38435       //  if(!this.collapsed){
38436             b = this.el.getBox(false, true);
38437        // }else{
38438           //  b = this.collapsedEl.getBox(false, true);
38439         //}
38440         return b;
38441     },
38442
38443     getMargins : function(){
38444         return this.margins;
38445         //return this.collapsed ? this.cmargins : this.margins;
38446     },
38447 /*
38448     highlight : function(){
38449         this.el.addClass("x-layout-panel-dragover");
38450     },
38451
38452     unhighlight : function(){
38453         this.el.removeClass("x-layout-panel-dragover");
38454     },
38455 */
38456     updateBox : function(box)
38457     {
38458         if (!this.bodyEl) {
38459             return; // not rendered yet..
38460         }
38461         
38462         this.box = box;
38463         if(!this.collapsed){
38464             this.el.dom.style.left = box.x + "px";
38465             this.el.dom.style.top = box.y + "px";
38466             this.updateBody(box.width, box.height);
38467         }else{
38468             this.collapsedEl.dom.style.left = box.x + "px";
38469             this.collapsedEl.dom.style.top = box.y + "px";
38470             this.collapsedEl.setSize(box.width, box.height);
38471         }
38472         if(this.tabs){
38473             this.tabs.autoSizeTabs();
38474         }
38475     },
38476
38477     updateBody : function(w, h)
38478     {
38479         if(w !== null){
38480             this.el.setWidth(w);
38481             w -= this.el.getBorderWidth("rl");
38482             if(this.config.adjustments){
38483                 w += this.config.adjustments[0];
38484             }
38485         }
38486         if(h !== null && h > 0){
38487             this.el.setHeight(h);
38488             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38489             h -= this.el.getBorderWidth("tb");
38490             if(this.config.adjustments){
38491                 h += this.config.adjustments[1];
38492             }
38493             this.bodyEl.setHeight(h);
38494             if(this.tabs){
38495                 h = this.tabs.syncHeight(h);
38496             }
38497         }
38498         if(this.panelSize){
38499             w = w !== null ? w : this.panelSize.width;
38500             h = h !== null ? h : this.panelSize.height;
38501         }
38502         if(this.activePanel){
38503             var el = this.activePanel.getEl();
38504             w = w !== null ? w : el.getWidth();
38505             h = h !== null ? h : el.getHeight();
38506             this.panelSize = {width: w, height: h};
38507             this.activePanel.setSize(w, h);
38508         }
38509         if(Roo.isIE && this.tabs){
38510             this.tabs.el.repaint();
38511         }
38512     },
38513
38514     /**
38515      * Returns the container element for this region.
38516      * @return {Roo.Element}
38517      */
38518     getEl : function(){
38519         return this.el;
38520     },
38521
38522     /**
38523      * Hides this region.
38524      */
38525     hide : function(){
38526         //if(!this.collapsed){
38527             this.el.dom.style.left = "-2000px";
38528             this.el.hide();
38529         //}else{
38530          //   this.collapsedEl.dom.style.left = "-2000px";
38531          //   this.collapsedEl.hide();
38532        // }
38533         this.visible = false;
38534         this.fireEvent("visibilitychange", this, false);
38535     },
38536
38537     /**
38538      * Shows this region if it was previously hidden.
38539      */
38540     show : function(){
38541         //if(!this.collapsed){
38542             this.el.show();
38543         //}else{
38544         //    this.collapsedEl.show();
38545        // }
38546         this.visible = true;
38547         this.fireEvent("visibilitychange", this, true);
38548     },
38549 /*
38550     closeClicked : function(){
38551         if(this.activePanel){
38552             this.remove(this.activePanel);
38553         }
38554     },
38555
38556     collapseClick : function(e){
38557         if(this.isSlid){
38558            e.stopPropagation();
38559            this.slideIn();
38560         }else{
38561            e.stopPropagation();
38562            this.slideOut();
38563         }
38564     },
38565 */
38566     /**
38567      * Collapses this region.
38568      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38569      */
38570     /*
38571     collapse : function(skipAnim, skipCheck = false){
38572         if(this.collapsed) {
38573             return;
38574         }
38575         
38576         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38577             
38578             this.collapsed = true;
38579             if(this.split){
38580                 this.split.el.hide();
38581             }
38582             if(this.config.animate && skipAnim !== true){
38583                 this.fireEvent("invalidated", this);
38584                 this.animateCollapse();
38585             }else{
38586                 this.el.setLocation(-20000,-20000);
38587                 this.el.hide();
38588                 this.collapsedEl.show();
38589                 this.fireEvent("collapsed", this);
38590                 this.fireEvent("invalidated", this);
38591             }
38592         }
38593         
38594     },
38595 */
38596     animateCollapse : function(){
38597         // overridden
38598     },
38599
38600     /**
38601      * Expands this region if it was previously collapsed.
38602      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38603      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38604      */
38605     /*
38606     expand : function(e, skipAnim){
38607         if(e) {
38608             e.stopPropagation();
38609         }
38610         if(!this.collapsed || this.el.hasActiveFx()) {
38611             return;
38612         }
38613         if(this.isSlid){
38614             this.afterSlideIn();
38615             skipAnim = true;
38616         }
38617         this.collapsed = false;
38618         if(this.config.animate && skipAnim !== true){
38619             this.animateExpand();
38620         }else{
38621             this.el.show();
38622             if(this.split){
38623                 this.split.el.show();
38624             }
38625             this.collapsedEl.setLocation(-2000,-2000);
38626             this.collapsedEl.hide();
38627             this.fireEvent("invalidated", this);
38628             this.fireEvent("expanded", this);
38629         }
38630     },
38631 */
38632     animateExpand : function(){
38633         // overridden
38634     },
38635
38636     initTabs : function()
38637     {
38638         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38639         
38640         var ts = new Roo.bootstrap.panel.Tabs({
38641             el: this.bodyEl.dom,
38642             region : this,
38643             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38644             disableTooltips: this.config.disableTabTips,
38645             toolbar : this.config.toolbar
38646         });
38647         
38648         if(this.config.hideTabs){
38649             ts.stripWrap.setDisplayed(false);
38650         }
38651         this.tabs = ts;
38652         ts.resizeTabs = this.config.resizeTabs === true;
38653         ts.minTabWidth = this.config.minTabWidth || 40;
38654         ts.maxTabWidth = this.config.maxTabWidth || 250;
38655         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38656         ts.monitorResize = false;
38657         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38658         ts.bodyEl.addClass('roo-layout-tabs-body');
38659         this.panels.each(this.initPanelAsTab, this);
38660     },
38661
38662     initPanelAsTab : function(panel){
38663         var ti = this.tabs.addTab(
38664             panel.getEl().id,
38665             panel.getTitle(),
38666             null,
38667             this.config.closeOnTab && panel.isClosable(),
38668             panel.tpl
38669         );
38670         if(panel.tabTip !== undefined){
38671             ti.setTooltip(panel.tabTip);
38672         }
38673         ti.on("activate", function(){
38674               this.setActivePanel(panel);
38675         }, this);
38676         
38677         if(this.config.closeOnTab){
38678             ti.on("beforeclose", function(t, e){
38679                 e.cancel = true;
38680                 this.remove(panel);
38681             }, this);
38682         }
38683         
38684         panel.tabItem = ti;
38685         
38686         return ti;
38687     },
38688
38689     updatePanelTitle : function(panel, title)
38690     {
38691         if(this.activePanel == panel){
38692             this.updateTitle(title);
38693         }
38694         if(this.tabs){
38695             var ti = this.tabs.getTab(panel.getEl().id);
38696             ti.setText(title);
38697             if(panel.tabTip !== undefined){
38698                 ti.setTooltip(panel.tabTip);
38699             }
38700         }
38701     },
38702
38703     updateTitle : function(title){
38704         if(this.titleTextEl && !this.config.title){
38705             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38706         }
38707     },
38708
38709     setActivePanel : function(panel)
38710     {
38711         panel = this.getPanel(panel);
38712         if(this.activePanel && this.activePanel != panel){
38713             if(this.activePanel.setActiveState(false) === false){
38714                 return;
38715             }
38716         }
38717         this.activePanel = panel;
38718         panel.setActiveState(true);
38719         if(this.panelSize){
38720             panel.setSize(this.panelSize.width, this.panelSize.height);
38721         }
38722         if(this.closeBtn){
38723             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38724         }
38725         this.updateTitle(panel.getTitle());
38726         if(this.tabs){
38727             this.fireEvent("invalidated", this);
38728         }
38729         this.fireEvent("panelactivated", this, panel);
38730     },
38731
38732     /**
38733      * Shows the specified panel.
38734      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38735      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38736      */
38737     showPanel : function(panel)
38738     {
38739         panel = this.getPanel(panel);
38740         if(panel){
38741             if(this.tabs){
38742                 var tab = this.tabs.getTab(panel.getEl().id);
38743                 if(tab.isHidden()){
38744                     this.tabs.unhideTab(tab.id);
38745                 }
38746                 tab.activate();
38747             }else{
38748                 this.setActivePanel(panel);
38749             }
38750         }
38751         return panel;
38752     },
38753
38754     /**
38755      * Get the active panel for this region.
38756      * @return {Roo.ContentPanel} The active panel or null
38757      */
38758     getActivePanel : function(){
38759         return this.activePanel;
38760     },
38761
38762     validateVisibility : function(){
38763         if(this.panels.getCount() < 1){
38764             this.updateTitle("&#160;");
38765             this.closeBtn.hide();
38766             this.hide();
38767         }else{
38768             if(!this.isVisible()){
38769                 this.show();
38770             }
38771         }
38772     },
38773
38774     /**
38775      * Adds the passed ContentPanel(s) to this region.
38776      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38777      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38778      */
38779     add : function(panel)
38780     {
38781         if(arguments.length > 1){
38782             for(var i = 0, len = arguments.length; i < len; i++) {
38783                 this.add(arguments[i]);
38784             }
38785             return null;
38786         }
38787         
38788         // if we have not been rendered yet, then we can not really do much of this..
38789         if (!this.bodyEl) {
38790             this.unrendered_panels.push(panel);
38791             return panel;
38792         }
38793         
38794         
38795         
38796         
38797         if(this.hasPanel(panel)){
38798             this.showPanel(panel);
38799             return panel;
38800         }
38801         panel.setRegion(this);
38802         this.panels.add(panel);
38803        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38804             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38805             // and hide them... ???
38806             this.bodyEl.dom.appendChild(panel.getEl().dom);
38807             if(panel.background !== true){
38808                 this.setActivePanel(panel);
38809             }
38810             this.fireEvent("paneladded", this, panel);
38811             return panel;
38812         }
38813         */
38814         if(!this.tabs){
38815             this.initTabs();
38816         }else{
38817             this.initPanelAsTab(panel);
38818         }
38819         
38820         
38821         if(panel.background !== true){
38822             this.tabs.activate(panel.getEl().id);
38823         }
38824         this.fireEvent("paneladded", this, panel);
38825         return panel;
38826     },
38827
38828     /**
38829      * Hides the tab for the specified panel.
38830      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38831      */
38832     hidePanel : function(panel){
38833         if(this.tabs && (panel = this.getPanel(panel))){
38834             this.tabs.hideTab(panel.getEl().id);
38835         }
38836     },
38837
38838     /**
38839      * Unhides the tab for a previously hidden panel.
38840      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38841      */
38842     unhidePanel : function(panel){
38843         if(this.tabs && (panel = this.getPanel(panel))){
38844             this.tabs.unhideTab(panel.getEl().id);
38845         }
38846     },
38847
38848     clearPanels : function(){
38849         while(this.panels.getCount() > 0){
38850              this.remove(this.panels.first());
38851         }
38852     },
38853
38854     /**
38855      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38856      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38857      * @param {Boolean} preservePanel Overrides the config preservePanel option
38858      * @return {Roo.ContentPanel} The panel that was removed
38859      */
38860     remove : function(panel, preservePanel)
38861     {
38862         panel = this.getPanel(panel);
38863         if(!panel){
38864             return null;
38865         }
38866         var e = {};
38867         this.fireEvent("beforeremove", this, panel, e);
38868         if(e.cancel === true){
38869             return null;
38870         }
38871         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38872         var panelId = panel.getId();
38873         this.panels.removeKey(panelId);
38874         if(preservePanel){
38875             document.body.appendChild(panel.getEl().dom);
38876         }
38877         if(this.tabs){
38878             this.tabs.removeTab(panel.getEl().id);
38879         }else if (!preservePanel){
38880             this.bodyEl.dom.removeChild(panel.getEl().dom);
38881         }
38882         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38883             var p = this.panels.first();
38884             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38885             tempEl.appendChild(p.getEl().dom);
38886             this.bodyEl.update("");
38887             this.bodyEl.dom.appendChild(p.getEl().dom);
38888             tempEl = null;
38889             this.updateTitle(p.getTitle());
38890             this.tabs = null;
38891             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38892             this.setActivePanel(p);
38893         }
38894         panel.setRegion(null);
38895         if(this.activePanel == panel){
38896             this.activePanel = null;
38897         }
38898         if(this.config.autoDestroy !== false && preservePanel !== true){
38899             try{panel.destroy();}catch(e){}
38900         }
38901         this.fireEvent("panelremoved", this, panel);
38902         return panel;
38903     },
38904
38905     /**
38906      * Returns the TabPanel component used by this region
38907      * @return {Roo.TabPanel}
38908      */
38909     getTabs : function(){
38910         return this.tabs;
38911     },
38912
38913     createTool : function(parentEl, className){
38914         var btn = Roo.DomHelper.append(parentEl, {
38915             tag: "div",
38916             cls: "x-layout-tools-button",
38917             children: [ {
38918                 tag: "div",
38919                 cls: "roo-layout-tools-button-inner " + className,
38920                 html: "&#160;"
38921             }]
38922         }, true);
38923         btn.addClassOnOver("roo-layout-tools-button-over");
38924         return btn;
38925     }
38926 });/*
38927  * Based on:
38928  * Ext JS Library 1.1.1
38929  * Copyright(c) 2006-2007, Ext JS, LLC.
38930  *
38931  * Originally Released Under LGPL - original licence link has changed is not relivant.
38932  *
38933  * Fork - LGPL
38934  * <script type="text/javascript">
38935  */
38936  
38937
38938
38939 /**
38940  * @class Roo.SplitLayoutRegion
38941  * @extends Roo.LayoutRegion
38942  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38943  */
38944 Roo.bootstrap.layout.Split = function(config){
38945     this.cursor = config.cursor;
38946     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38947 };
38948
38949 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38950 {
38951     splitTip : "Drag to resize.",
38952     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38953     useSplitTips : false,
38954
38955     applyConfig : function(config){
38956         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38957     },
38958     
38959     onRender : function(ctr,pos) {
38960         
38961         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38962         if(!this.config.split){
38963             return;
38964         }
38965         if(!this.split){
38966             
38967             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38968                             tag: "div",
38969                             id: this.el.id + "-split",
38970                             cls: "roo-layout-split roo-layout-split-"+this.position,
38971                             html: "&#160;"
38972             });
38973             /** The SplitBar for this region 
38974             * @type Roo.SplitBar */
38975             // does not exist yet...
38976             Roo.log([this.position, this.orientation]);
38977             
38978             this.split = new Roo.bootstrap.SplitBar({
38979                 dragElement : splitEl,
38980                 resizingElement: this.el,
38981                 orientation : this.orientation
38982             });
38983             
38984             this.split.on("moved", this.onSplitMove, this);
38985             this.split.useShim = this.config.useShim === true;
38986             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38987             if(this.useSplitTips){
38988                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38989             }
38990             //if(config.collapsible){
38991             //    this.split.el.on("dblclick", this.collapse,  this);
38992             //}
38993         }
38994         if(typeof this.config.minSize != "undefined"){
38995             this.split.minSize = this.config.minSize;
38996         }
38997         if(typeof this.config.maxSize != "undefined"){
38998             this.split.maxSize = this.config.maxSize;
38999         }
39000         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39001             this.hideSplitter();
39002         }
39003         
39004     },
39005
39006     getHMaxSize : function(){
39007          var cmax = this.config.maxSize || 10000;
39008          var center = this.mgr.getRegion("center");
39009          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39010     },
39011
39012     getVMaxSize : function(){
39013          var cmax = this.config.maxSize || 10000;
39014          var center = this.mgr.getRegion("center");
39015          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39016     },
39017
39018     onSplitMove : function(split, newSize){
39019         this.fireEvent("resized", this, newSize);
39020     },
39021     
39022     /** 
39023      * Returns the {@link Roo.SplitBar} for this region.
39024      * @return {Roo.SplitBar}
39025      */
39026     getSplitBar : function(){
39027         return this.split;
39028     },
39029     
39030     hide : function(){
39031         this.hideSplitter();
39032         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39033     },
39034
39035     hideSplitter : function(){
39036         if(this.split){
39037             this.split.el.setLocation(-2000,-2000);
39038             this.split.el.hide();
39039         }
39040     },
39041
39042     show : function(){
39043         if(this.split){
39044             this.split.el.show();
39045         }
39046         Roo.bootstrap.layout.Split.superclass.show.call(this);
39047     },
39048     
39049     beforeSlide: function(){
39050         if(Roo.isGecko){// firefox overflow auto bug workaround
39051             this.bodyEl.clip();
39052             if(this.tabs) {
39053                 this.tabs.bodyEl.clip();
39054             }
39055             if(this.activePanel){
39056                 this.activePanel.getEl().clip();
39057                 
39058                 if(this.activePanel.beforeSlide){
39059                     this.activePanel.beforeSlide();
39060                 }
39061             }
39062         }
39063     },
39064     
39065     afterSlide : function(){
39066         if(Roo.isGecko){// firefox overflow auto bug workaround
39067             this.bodyEl.unclip();
39068             if(this.tabs) {
39069                 this.tabs.bodyEl.unclip();
39070             }
39071             if(this.activePanel){
39072                 this.activePanel.getEl().unclip();
39073                 if(this.activePanel.afterSlide){
39074                     this.activePanel.afterSlide();
39075                 }
39076             }
39077         }
39078     },
39079
39080     initAutoHide : function(){
39081         if(this.autoHide !== false){
39082             if(!this.autoHideHd){
39083                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39084                 this.autoHideHd = {
39085                     "mouseout": function(e){
39086                         if(!e.within(this.el, true)){
39087                             st.delay(500);
39088                         }
39089                     },
39090                     "mouseover" : function(e){
39091                         st.cancel();
39092                     },
39093                     scope : this
39094                 };
39095             }
39096             this.el.on(this.autoHideHd);
39097         }
39098     },
39099
39100     clearAutoHide : function(){
39101         if(this.autoHide !== false){
39102             this.el.un("mouseout", this.autoHideHd.mouseout);
39103             this.el.un("mouseover", this.autoHideHd.mouseover);
39104         }
39105     },
39106
39107     clearMonitor : function(){
39108         Roo.get(document).un("click", this.slideInIf, this);
39109     },
39110
39111     // these names are backwards but not changed for compat
39112     slideOut : function(){
39113         if(this.isSlid || this.el.hasActiveFx()){
39114             return;
39115         }
39116         this.isSlid = true;
39117         if(this.collapseBtn){
39118             this.collapseBtn.hide();
39119         }
39120         this.closeBtnState = this.closeBtn.getStyle('display');
39121         this.closeBtn.hide();
39122         if(this.stickBtn){
39123             this.stickBtn.show();
39124         }
39125         this.el.show();
39126         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39127         this.beforeSlide();
39128         this.el.setStyle("z-index", 10001);
39129         this.el.slideIn(this.getSlideAnchor(), {
39130             callback: function(){
39131                 this.afterSlide();
39132                 this.initAutoHide();
39133                 Roo.get(document).on("click", this.slideInIf, this);
39134                 this.fireEvent("slideshow", this);
39135             },
39136             scope: this,
39137             block: true
39138         });
39139     },
39140
39141     afterSlideIn : function(){
39142         this.clearAutoHide();
39143         this.isSlid = false;
39144         this.clearMonitor();
39145         this.el.setStyle("z-index", "");
39146         if(this.collapseBtn){
39147             this.collapseBtn.show();
39148         }
39149         this.closeBtn.setStyle('display', this.closeBtnState);
39150         if(this.stickBtn){
39151             this.stickBtn.hide();
39152         }
39153         this.fireEvent("slidehide", this);
39154     },
39155
39156     slideIn : function(cb){
39157         if(!this.isSlid || this.el.hasActiveFx()){
39158             Roo.callback(cb);
39159             return;
39160         }
39161         this.isSlid = false;
39162         this.beforeSlide();
39163         this.el.slideOut(this.getSlideAnchor(), {
39164             callback: function(){
39165                 this.el.setLeftTop(-10000, -10000);
39166                 this.afterSlide();
39167                 this.afterSlideIn();
39168                 Roo.callback(cb);
39169             },
39170             scope: this,
39171             block: true
39172         });
39173     },
39174     
39175     slideInIf : function(e){
39176         if(!e.within(this.el)){
39177             this.slideIn();
39178         }
39179     },
39180
39181     animateCollapse : function(){
39182         this.beforeSlide();
39183         this.el.setStyle("z-index", 20000);
39184         var anchor = this.getSlideAnchor();
39185         this.el.slideOut(anchor, {
39186             callback : function(){
39187                 this.el.setStyle("z-index", "");
39188                 this.collapsedEl.slideIn(anchor, {duration:.3});
39189                 this.afterSlide();
39190                 this.el.setLocation(-10000,-10000);
39191                 this.el.hide();
39192                 this.fireEvent("collapsed", this);
39193             },
39194             scope: this,
39195             block: true
39196         });
39197     },
39198
39199     animateExpand : function(){
39200         this.beforeSlide();
39201         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39202         this.el.setStyle("z-index", 20000);
39203         this.collapsedEl.hide({
39204             duration:.1
39205         });
39206         this.el.slideIn(this.getSlideAnchor(), {
39207             callback : function(){
39208                 this.el.setStyle("z-index", "");
39209                 this.afterSlide();
39210                 if(this.split){
39211                     this.split.el.show();
39212                 }
39213                 this.fireEvent("invalidated", this);
39214                 this.fireEvent("expanded", this);
39215             },
39216             scope: this,
39217             block: true
39218         });
39219     },
39220
39221     anchors : {
39222         "west" : "left",
39223         "east" : "right",
39224         "north" : "top",
39225         "south" : "bottom"
39226     },
39227
39228     sanchors : {
39229         "west" : "l",
39230         "east" : "r",
39231         "north" : "t",
39232         "south" : "b"
39233     },
39234
39235     canchors : {
39236         "west" : "tl-tr",
39237         "east" : "tr-tl",
39238         "north" : "tl-bl",
39239         "south" : "bl-tl"
39240     },
39241
39242     getAnchor : function(){
39243         return this.anchors[this.position];
39244     },
39245
39246     getCollapseAnchor : function(){
39247         return this.canchors[this.position];
39248     },
39249
39250     getSlideAnchor : function(){
39251         return this.sanchors[this.position];
39252     },
39253
39254     getAlignAdj : function(){
39255         var cm = this.cmargins;
39256         switch(this.position){
39257             case "west":
39258                 return [0, 0];
39259             break;
39260             case "east":
39261                 return [0, 0];
39262             break;
39263             case "north":
39264                 return [0, 0];
39265             break;
39266             case "south":
39267                 return [0, 0];
39268             break;
39269         }
39270     },
39271
39272     getExpandAdj : function(){
39273         var c = this.collapsedEl, cm = this.cmargins;
39274         switch(this.position){
39275             case "west":
39276                 return [-(cm.right+c.getWidth()+cm.left), 0];
39277             break;
39278             case "east":
39279                 return [cm.right+c.getWidth()+cm.left, 0];
39280             break;
39281             case "north":
39282                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39283             break;
39284             case "south":
39285                 return [0, cm.top+cm.bottom+c.getHeight()];
39286             break;
39287         }
39288     }
39289 });/*
39290  * Based on:
39291  * Ext JS Library 1.1.1
39292  * Copyright(c) 2006-2007, Ext JS, LLC.
39293  *
39294  * Originally Released Under LGPL - original licence link has changed is not relivant.
39295  *
39296  * Fork - LGPL
39297  * <script type="text/javascript">
39298  */
39299 /*
39300  * These classes are private internal classes
39301  */
39302 Roo.bootstrap.layout.Center = function(config){
39303     config.region = "center";
39304     Roo.bootstrap.layout.Region.call(this, config);
39305     this.visible = true;
39306     this.minWidth = config.minWidth || 20;
39307     this.minHeight = config.minHeight || 20;
39308 };
39309
39310 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39311     hide : function(){
39312         // center panel can't be hidden
39313     },
39314     
39315     show : function(){
39316         // center panel can't be hidden
39317     },
39318     
39319     getMinWidth: function(){
39320         return this.minWidth;
39321     },
39322     
39323     getMinHeight: function(){
39324         return this.minHeight;
39325     }
39326 });
39327
39328
39329
39330
39331  
39332
39333
39334
39335
39336
39337
39338 Roo.bootstrap.layout.North = function(config)
39339 {
39340     config.region = 'north';
39341     config.cursor = 'n-resize';
39342     
39343     Roo.bootstrap.layout.Split.call(this, config);
39344     
39345     
39346     if(this.split){
39347         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39348         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39349         this.split.el.addClass("roo-layout-split-v");
39350     }
39351     //var size = config.initialSize || config.height;
39352     //if(this.el && typeof size != "undefined"){
39353     //    this.el.setHeight(size);
39354     //}
39355 };
39356 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39357 {
39358     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39359      
39360      
39361     onRender : function(ctr, pos)
39362     {
39363         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39364         var size = this.config.initialSize || this.config.height;
39365         if(this.el && typeof size != "undefined"){
39366             this.el.setHeight(size);
39367         }
39368     
39369     },
39370     
39371     getBox : function(){
39372         if(this.collapsed){
39373             return this.collapsedEl.getBox();
39374         }
39375         var box = this.el.getBox();
39376         if(this.split){
39377             box.height += this.split.el.getHeight();
39378         }
39379         return box;
39380     },
39381     
39382     updateBox : function(box){
39383         if(this.split && !this.collapsed){
39384             box.height -= this.split.el.getHeight();
39385             this.split.el.setLeft(box.x);
39386             this.split.el.setTop(box.y+box.height);
39387             this.split.el.setWidth(box.width);
39388         }
39389         if(this.collapsed){
39390             this.updateBody(box.width, null);
39391         }
39392         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39393     }
39394 });
39395
39396
39397
39398
39399
39400 Roo.bootstrap.layout.South = function(config){
39401     config.region = 'south';
39402     config.cursor = 's-resize';
39403     Roo.bootstrap.layout.Split.call(this, config);
39404     if(this.split){
39405         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39406         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39407         this.split.el.addClass("roo-layout-split-v");
39408     }
39409     
39410 };
39411
39412 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39413     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39414     
39415     onRender : function(ctr, pos)
39416     {
39417         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39418         var size = this.config.initialSize || this.config.height;
39419         if(this.el && typeof size != "undefined"){
39420             this.el.setHeight(size);
39421         }
39422     
39423     },
39424     
39425     getBox : function(){
39426         if(this.collapsed){
39427             return this.collapsedEl.getBox();
39428         }
39429         var box = this.el.getBox();
39430         if(this.split){
39431             var sh = this.split.el.getHeight();
39432             box.height += sh;
39433             box.y -= sh;
39434         }
39435         return box;
39436     },
39437     
39438     updateBox : function(box){
39439         if(this.split && !this.collapsed){
39440             var sh = this.split.el.getHeight();
39441             box.height -= sh;
39442             box.y += sh;
39443             this.split.el.setLeft(box.x);
39444             this.split.el.setTop(box.y-sh);
39445             this.split.el.setWidth(box.width);
39446         }
39447         if(this.collapsed){
39448             this.updateBody(box.width, null);
39449         }
39450         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39451     }
39452 });
39453
39454 Roo.bootstrap.layout.East = function(config){
39455     config.region = "east";
39456     config.cursor = "e-resize";
39457     Roo.bootstrap.layout.Split.call(this, config);
39458     if(this.split){
39459         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39460         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39461         this.split.el.addClass("roo-layout-split-h");
39462     }
39463     
39464 };
39465 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39466     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39467     
39468     onRender : function(ctr, pos)
39469     {
39470         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471         var size = this.config.initialSize || this.config.width;
39472         if(this.el && typeof size != "undefined"){
39473             this.el.setWidth(size);
39474         }
39475     
39476     },
39477     
39478     getBox : function(){
39479         if(this.collapsed){
39480             return this.collapsedEl.getBox();
39481         }
39482         var box = this.el.getBox();
39483         if(this.split){
39484             var sw = this.split.el.getWidth();
39485             box.width += sw;
39486             box.x -= sw;
39487         }
39488         return box;
39489     },
39490
39491     updateBox : function(box){
39492         if(this.split && !this.collapsed){
39493             var sw = this.split.el.getWidth();
39494             box.width -= sw;
39495             this.split.el.setLeft(box.x);
39496             this.split.el.setTop(box.y);
39497             this.split.el.setHeight(box.height);
39498             box.x += sw;
39499         }
39500         if(this.collapsed){
39501             this.updateBody(null, box.height);
39502         }
39503         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39504     }
39505 });
39506
39507 Roo.bootstrap.layout.West = function(config){
39508     config.region = "west";
39509     config.cursor = "w-resize";
39510     
39511     Roo.bootstrap.layout.Split.call(this, config);
39512     if(this.split){
39513         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39514         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39515         this.split.el.addClass("roo-layout-split-h");
39516     }
39517     
39518 };
39519 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39520     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39521     
39522     onRender: function(ctr, pos)
39523     {
39524         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39525         var size = this.config.initialSize || this.config.width;
39526         if(typeof size != "undefined"){
39527             this.el.setWidth(size);
39528         }
39529     },
39530     
39531     getBox : function(){
39532         if(this.collapsed){
39533             return this.collapsedEl.getBox();
39534         }
39535         var box = this.el.getBox();
39536         if (box.width == 0) {
39537             box.width = this.config.width; // kludge?
39538         }
39539         if(this.split){
39540             box.width += this.split.el.getWidth();
39541         }
39542         return box;
39543     },
39544     
39545     updateBox : function(box){
39546         if(this.split && !this.collapsed){
39547             var sw = this.split.el.getWidth();
39548             box.width -= sw;
39549             this.split.el.setLeft(box.x+box.width);
39550             this.split.el.setTop(box.y);
39551             this.split.el.setHeight(box.height);
39552         }
39553         if(this.collapsed){
39554             this.updateBody(null, box.height);
39555         }
39556         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39557     }
39558 });Roo.namespace("Roo.bootstrap.panel");/*
39559  * Based on:
39560  * Ext JS Library 1.1.1
39561  * Copyright(c) 2006-2007, Ext JS, LLC.
39562  *
39563  * Originally Released Under LGPL - original licence link has changed is not relivant.
39564  *
39565  * Fork - LGPL
39566  * <script type="text/javascript">
39567  */
39568 /**
39569  * @class Roo.ContentPanel
39570  * @extends Roo.util.Observable
39571  * A basic ContentPanel element.
39572  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39573  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39574  * @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
39575  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39576  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39577  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39578  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39579  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39580  * @cfg {String} title          The title for this panel
39581  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39582  * @cfg {String} url            Calls {@link #setUrl} with this value
39583  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39584  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39585  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39586  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39587  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39588  * @cfg {Boolean} badges render the badges
39589  * @cfg {String} cls  extra classes to use  
39590  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39591
39592  * @constructor
39593  * Create a new ContentPanel.
39594  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39595  * @param {String/Object} config A string to set only the title or a config object
39596  * @param {String} content (optional) Set the HTML content for this panel
39597  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39598  */
39599 Roo.bootstrap.panel.Content = function( config){
39600     
39601     this.tpl = config.tpl || false;
39602     
39603     var el = config.el;
39604     var content = config.content;
39605
39606     if(config.autoCreate){ // xtype is available if this is called from factory
39607         el = Roo.id();
39608     }
39609     this.el = Roo.get(el);
39610     if(!this.el && config && config.autoCreate){
39611         if(typeof config.autoCreate == "object"){
39612             if(!config.autoCreate.id){
39613                 config.autoCreate.id = config.id||el;
39614             }
39615             this.el = Roo.DomHelper.append(document.body,
39616                         config.autoCreate, true);
39617         }else{
39618             var elcfg =  {
39619                 tag: "div",
39620                 cls: (config.cls || '') +
39621                     (config.background ? ' bg-' + config.background : '') +
39622                     " roo-layout-inactive-content",
39623                 id: config.id||el
39624             };
39625             if (config.iframe) {
39626                 elcfg.cn = [
39627                     {
39628                         tag : 'iframe',
39629                         style : 'border: 0px',
39630                         src : 'about:blank'
39631                     }
39632                 ];
39633             }
39634               
39635             if (config.html) {
39636                 elcfg.html = config.html;
39637                 
39638             }
39639                         
39640             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39641             if (config.iframe) {
39642                 this.iframeEl = this.el.select('iframe',true).first();
39643             }
39644             
39645         }
39646     } 
39647     this.closable = false;
39648     this.loaded = false;
39649     this.active = false;
39650    
39651       
39652     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39653         
39654         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39655         
39656         this.wrapEl = this.el; //this.el.wrap();
39657         var ti = [];
39658         if (config.toolbar.items) {
39659             ti = config.toolbar.items ;
39660             delete config.toolbar.items ;
39661         }
39662         
39663         var nitems = [];
39664         this.toolbar.render(this.wrapEl, 'before');
39665         for(var i =0;i < ti.length;i++) {
39666           //  Roo.log(['add child', items[i]]);
39667             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39668         }
39669         this.toolbar.items = nitems;
39670         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39671         delete config.toolbar;
39672         
39673     }
39674     /*
39675     // xtype created footer. - not sure if will work as we normally have to render first..
39676     if (this.footer && !this.footer.el && this.footer.xtype) {
39677         if (!this.wrapEl) {
39678             this.wrapEl = this.el.wrap();
39679         }
39680     
39681         this.footer.container = this.wrapEl.createChild();
39682          
39683         this.footer = Roo.factory(this.footer, Roo);
39684         
39685     }
39686     */
39687     
39688      if(typeof config == "string"){
39689         this.title = config;
39690     }else{
39691         Roo.apply(this, config);
39692     }
39693     
39694     if(this.resizeEl){
39695         this.resizeEl = Roo.get(this.resizeEl, true);
39696     }else{
39697         this.resizeEl = this.el;
39698     }
39699     // handle view.xtype
39700     
39701  
39702     
39703     
39704     this.addEvents({
39705         /**
39706          * @event activate
39707          * Fires when this panel is activated. 
39708          * @param {Roo.ContentPanel} this
39709          */
39710         "activate" : true,
39711         /**
39712          * @event deactivate
39713          * Fires when this panel is activated. 
39714          * @param {Roo.ContentPanel} this
39715          */
39716         "deactivate" : true,
39717
39718         /**
39719          * @event resize
39720          * Fires when this panel is resized if fitToFrame is true.
39721          * @param {Roo.ContentPanel} this
39722          * @param {Number} width The width after any component adjustments
39723          * @param {Number} height The height after any component adjustments
39724          */
39725         "resize" : true,
39726         
39727          /**
39728          * @event render
39729          * Fires when this tab is created
39730          * @param {Roo.ContentPanel} this
39731          */
39732         "render" : true
39733         
39734         
39735         
39736     });
39737     
39738
39739     
39740     
39741     if(this.autoScroll && !this.iframe){
39742         this.resizeEl.setStyle("overflow", "auto");
39743     } else {
39744         // fix randome scrolling
39745         //this.el.on('scroll', function() {
39746         //    Roo.log('fix random scolling');
39747         //    this.scrollTo('top',0); 
39748         //});
39749     }
39750     content = content || this.content;
39751     if(content){
39752         this.setContent(content);
39753     }
39754     if(config && config.url){
39755         this.setUrl(this.url, this.params, this.loadOnce);
39756     }
39757     
39758     
39759     
39760     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39761     
39762     if (this.view && typeof(this.view.xtype) != 'undefined') {
39763         this.view.el = this.el.appendChild(document.createElement("div"));
39764         this.view = Roo.factory(this.view); 
39765         this.view.render  &&  this.view.render(false, '');  
39766     }
39767     
39768     
39769     this.fireEvent('render', this);
39770 };
39771
39772 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39773     
39774     cls : '',
39775     background : '',
39776     
39777     tabTip : '',
39778     
39779     iframe : false,
39780     iframeEl : false,
39781     
39782     setRegion : function(region){
39783         this.region = region;
39784         this.setActiveClass(region && !this.background);
39785     },
39786     
39787     
39788     setActiveClass: function(state)
39789     {
39790         if(state){
39791            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39792            this.el.setStyle('position','relative');
39793         }else{
39794            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39795            this.el.setStyle('position', 'absolute');
39796         } 
39797     },
39798     
39799     /**
39800      * Returns the toolbar for this Panel if one was configured. 
39801      * @return {Roo.Toolbar} 
39802      */
39803     getToolbar : function(){
39804         return this.toolbar;
39805     },
39806     
39807     setActiveState : function(active)
39808     {
39809         this.active = active;
39810         this.setActiveClass(active);
39811         if(!active){
39812             if(this.fireEvent("deactivate", this) === false){
39813                 return false;
39814             }
39815             return true;
39816         }
39817         this.fireEvent("activate", this);
39818         return true;
39819     },
39820     /**
39821      * Updates this panel's element (not for iframe)
39822      * @param {String} content The new content
39823      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39824     */
39825     setContent : function(content, loadScripts){
39826         if (this.iframe) {
39827             return;
39828         }
39829         
39830         this.el.update(content, loadScripts);
39831     },
39832
39833     ignoreResize : function(w, h){
39834         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39835             return true;
39836         }else{
39837             this.lastSize = {width: w, height: h};
39838             return false;
39839         }
39840     },
39841     /**
39842      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39843      * @return {Roo.UpdateManager} The UpdateManager
39844      */
39845     getUpdateManager : function(){
39846         if (this.iframe) {
39847             return false;
39848         }
39849         return this.el.getUpdateManager();
39850     },
39851      /**
39852      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39853      * Does not work with IFRAME contents
39854      * @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:
39855 <pre><code>
39856 panel.load({
39857     url: "your-url.php",
39858     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39859     callback: yourFunction,
39860     scope: yourObject, //(optional scope)
39861     discardUrl: false,
39862     nocache: false,
39863     text: "Loading...",
39864     timeout: 30,
39865     scripts: false
39866 });
39867 </code></pre>
39868      
39869      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39870      * 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.
39871      * @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}
39872      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39873      * @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.
39874      * @return {Roo.ContentPanel} this
39875      */
39876     load : function(){
39877         
39878         if (this.iframe) {
39879             return this;
39880         }
39881         
39882         var um = this.el.getUpdateManager();
39883         um.update.apply(um, arguments);
39884         return this;
39885     },
39886
39887
39888     /**
39889      * 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.
39890      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39891      * @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)
39892      * @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)
39893      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39894      */
39895     setUrl : function(url, params, loadOnce){
39896         if (this.iframe) {
39897             this.iframeEl.dom.src = url;
39898             return false;
39899         }
39900         
39901         if(this.refreshDelegate){
39902             this.removeListener("activate", this.refreshDelegate);
39903         }
39904         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39905         this.on("activate", this.refreshDelegate);
39906         return this.el.getUpdateManager();
39907     },
39908     
39909     _handleRefresh : function(url, params, loadOnce){
39910         if(!loadOnce || !this.loaded){
39911             var updater = this.el.getUpdateManager();
39912             updater.update(url, params, this._setLoaded.createDelegate(this));
39913         }
39914     },
39915     
39916     _setLoaded : function(){
39917         this.loaded = true;
39918     }, 
39919     
39920     /**
39921      * Returns this panel's id
39922      * @return {String} 
39923      */
39924     getId : function(){
39925         return this.el.id;
39926     },
39927     
39928     /** 
39929      * Returns this panel's element - used by regiosn to add.
39930      * @return {Roo.Element} 
39931      */
39932     getEl : function(){
39933         return this.wrapEl || this.el;
39934     },
39935     
39936    
39937     
39938     adjustForComponents : function(width, height)
39939     {
39940         //Roo.log('adjustForComponents ');
39941         if(this.resizeEl != this.el){
39942             width -= this.el.getFrameWidth('lr');
39943             height -= this.el.getFrameWidth('tb');
39944         }
39945         if(this.toolbar){
39946             var te = this.toolbar.getEl();
39947             te.setWidth(width);
39948             height -= te.getHeight();
39949         }
39950         if(this.footer){
39951             var te = this.footer.getEl();
39952             te.setWidth(width);
39953             height -= te.getHeight();
39954         }
39955         
39956         
39957         if(this.adjustments){
39958             width += this.adjustments[0];
39959             height += this.adjustments[1];
39960         }
39961         return {"width": width, "height": height};
39962     },
39963     
39964     setSize : function(width, height){
39965         if(this.fitToFrame && !this.ignoreResize(width, height)){
39966             if(this.fitContainer && this.resizeEl != this.el){
39967                 this.el.setSize(width, height);
39968             }
39969             var size = this.adjustForComponents(width, height);
39970             if (this.iframe) {
39971                 this.iframeEl.setSize(width,height);
39972             }
39973             
39974             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39975             this.fireEvent('resize', this, size.width, size.height);
39976             
39977             
39978         }
39979     },
39980     
39981     /**
39982      * Returns this panel's title
39983      * @return {String} 
39984      */
39985     getTitle : function(){
39986         
39987         if (typeof(this.title) != 'object') {
39988             return this.title;
39989         }
39990         
39991         var t = '';
39992         for (var k in this.title) {
39993             if (!this.title.hasOwnProperty(k)) {
39994                 continue;
39995             }
39996             
39997             if (k.indexOf('-') >= 0) {
39998                 var s = k.split('-');
39999                 for (var i = 0; i<s.length; i++) {
40000                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40001                 }
40002             } else {
40003                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40004             }
40005         }
40006         return t;
40007     },
40008     
40009     /**
40010      * Set this panel's title
40011      * @param {String} title
40012      */
40013     setTitle : function(title){
40014         this.title = title;
40015         if(this.region){
40016             this.region.updatePanelTitle(this, title);
40017         }
40018     },
40019     
40020     /**
40021      * Returns true is this panel was configured to be closable
40022      * @return {Boolean} 
40023      */
40024     isClosable : function(){
40025         return this.closable;
40026     },
40027     
40028     beforeSlide : function(){
40029         this.el.clip();
40030         this.resizeEl.clip();
40031     },
40032     
40033     afterSlide : function(){
40034         this.el.unclip();
40035         this.resizeEl.unclip();
40036     },
40037     
40038     /**
40039      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40040      *   Will fail silently if the {@link #setUrl} method has not been called.
40041      *   This does not activate the panel, just updates its content.
40042      */
40043     refresh : function(){
40044         if(this.refreshDelegate){
40045            this.loaded = false;
40046            this.refreshDelegate();
40047         }
40048     },
40049     
40050     /**
40051      * Destroys this panel
40052      */
40053     destroy : function(){
40054         this.el.removeAllListeners();
40055         var tempEl = document.createElement("span");
40056         tempEl.appendChild(this.el.dom);
40057         tempEl.innerHTML = "";
40058         this.el.remove();
40059         this.el = null;
40060     },
40061     
40062     /**
40063      * form - if the content panel contains a form - this is a reference to it.
40064      * @type {Roo.form.Form}
40065      */
40066     form : false,
40067     /**
40068      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40069      *    This contains a reference to it.
40070      * @type {Roo.View}
40071      */
40072     view : false,
40073     
40074       /**
40075      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40076      * <pre><code>
40077
40078 layout.addxtype({
40079        xtype : 'Form',
40080        items: [ .... ]
40081    }
40082 );
40083
40084 </code></pre>
40085      * @param {Object} cfg Xtype definition of item to add.
40086      */
40087     
40088     
40089     getChildContainer: function () {
40090         return this.getEl();
40091     }
40092     
40093     
40094     /*
40095         var  ret = new Roo.factory(cfg);
40096         return ret;
40097         
40098         
40099         // add form..
40100         if (cfg.xtype.match(/^Form$/)) {
40101             
40102             var el;
40103             //if (this.footer) {
40104             //    el = this.footer.container.insertSibling(false, 'before');
40105             //} else {
40106                 el = this.el.createChild();
40107             //}
40108
40109             this.form = new  Roo.form.Form(cfg);
40110             
40111             
40112             if ( this.form.allItems.length) {
40113                 this.form.render(el.dom);
40114             }
40115             return this.form;
40116         }
40117         // should only have one of theses..
40118         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40119             // views.. should not be just added - used named prop 'view''
40120             
40121             cfg.el = this.el.appendChild(document.createElement("div"));
40122             // factory?
40123             
40124             var ret = new Roo.factory(cfg);
40125              
40126              ret.render && ret.render(false, ''); // render blank..
40127             this.view = ret;
40128             return ret;
40129         }
40130         return false;
40131     }
40132     \*/
40133 });
40134  
40135 /**
40136  * @class Roo.bootstrap.panel.Grid
40137  * @extends Roo.bootstrap.panel.Content
40138  * @constructor
40139  * Create a new GridPanel.
40140  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40141  * @param {Object} config A the config object
40142   
40143  */
40144
40145
40146
40147 Roo.bootstrap.panel.Grid = function(config)
40148 {
40149     
40150       
40151     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40152         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40153
40154     config.el = this.wrapper;
40155     //this.el = this.wrapper;
40156     
40157       if (config.container) {
40158         // ctor'ed from a Border/panel.grid
40159         
40160         
40161         this.wrapper.setStyle("overflow", "hidden");
40162         this.wrapper.addClass('roo-grid-container');
40163
40164     }
40165     
40166     
40167     if(config.toolbar){
40168         var tool_el = this.wrapper.createChild();    
40169         this.toolbar = Roo.factory(config.toolbar);
40170         var ti = [];
40171         if (config.toolbar.items) {
40172             ti = config.toolbar.items ;
40173             delete config.toolbar.items ;
40174         }
40175         
40176         var nitems = [];
40177         this.toolbar.render(tool_el);
40178         for(var i =0;i < ti.length;i++) {
40179           //  Roo.log(['add child', items[i]]);
40180             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40181         }
40182         this.toolbar.items = nitems;
40183         
40184         delete config.toolbar;
40185     }
40186     
40187     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40188     config.grid.scrollBody = true;;
40189     config.grid.monitorWindowResize = false; // turn off autosizing
40190     config.grid.autoHeight = false;
40191     config.grid.autoWidth = false;
40192     
40193     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40194     
40195     if (config.background) {
40196         // render grid on panel activation (if panel background)
40197         this.on('activate', function(gp) {
40198             if (!gp.grid.rendered) {
40199                 gp.grid.render(this.wrapper);
40200                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40201             }
40202         });
40203             
40204     } else {
40205         this.grid.render(this.wrapper);
40206         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40207
40208     }
40209     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40210     // ??? needed ??? config.el = this.wrapper;
40211     
40212     
40213     
40214   
40215     // xtype created footer. - not sure if will work as we normally have to render first..
40216     if (this.footer && !this.footer.el && this.footer.xtype) {
40217         
40218         var ctr = this.grid.getView().getFooterPanel(true);
40219         this.footer.dataSource = this.grid.dataSource;
40220         this.footer = Roo.factory(this.footer, Roo);
40221         this.footer.render(ctr);
40222         
40223     }
40224     
40225     
40226     
40227     
40228      
40229 };
40230
40231 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40232     getId : function(){
40233         return this.grid.id;
40234     },
40235     
40236     /**
40237      * Returns the grid for this panel
40238      * @return {Roo.bootstrap.Table} 
40239      */
40240     getGrid : function(){
40241         return this.grid;    
40242     },
40243     
40244     setSize : function(width, height){
40245         if(!this.ignoreResize(width, height)){
40246             var grid = this.grid;
40247             var size = this.adjustForComponents(width, height);
40248             // tfoot is not a footer?
40249           
40250             
40251             var gridel = grid.getGridEl();
40252             gridel.setSize(size.width, size.height);
40253             
40254             var tbd = grid.getGridEl().select('tbody', true).first();
40255             var thd = grid.getGridEl().select('thead',true).first();
40256             var tbf= grid.getGridEl().select('tfoot', true).first();
40257
40258             if (tbf) {
40259                 size.height -= tbf.getHeight();
40260             }
40261             if (thd) {
40262                 size.height -= thd.getHeight();
40263             }
40264             
40265             tbd.setSize(size.width, size.height );
40266             // this is for the account management tab -seems to work there.
40267             var thd = grid.getGridEl().select('thead',true).first();
40268             //if (tbd) {
40269             //    tbd.setSize(size.width, size.height - thd.getHeight());
40270             //}
40271              
40272             grid.autoSize();
40273         }
40274     },
40275      
40276     
40277     
40278     beforeSlide : function(){
40279         this.grid.getView().scroller.clip();
40280     },
40281     
40282     afterSlide : function(){
40283         this.grid.getView().scroller.unclip();
40284     },
40285     
40286     destroy : function(){
40287         this.grid.destroy();
40288         delete this.grid;
40289         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40290     }
40291 });
40292
40293 /**
40294  * @class Roo.bootstrap.panel.Nest
40295  * @extends Roo.bootstrap.panel.Content
40296  * @constructor
40297  * Create a new Panel, that can contain a layout.Border.
40298  * 
40299  * 
40300  * @param {Roo.BorderLayout} layout The layout for this panel
40301  * @param {String/Object} config A string to set only the title or a config object
40302  */
40303 Roo.bootstrap.panel.Nest = function(config)
40304 {
40305     // construct with only one argument..
40306     /* FIXME - implement nicer consturctors
40307     if (layout.layout) {
40308         config = layout;
40309         layout = config.layout;
40310         delete config.layout;
40311     }
40312     if (layout.xtype && !layout.getEl) {
40313         // then layout needs constructing..
40314         layout = Roo.factory(layout, Roo);
40315     }
40316     */
40317     
40318     config.el =  config.layout.getEl();
40319     
40320     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40321     
40322     config.layout.monitorWindowResize = false; // turn off autosizing
40323     this.layout = config.layout;
40324     this.layout.getEl().addClass("roo-layout-nested-layout");
40325     this.layout.parent = this;
40326     
40327     
40328     
40329     
40330 };
40331
40332 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40333
40334     setSize : function(width, height){
40335         if(!this.ignoreResize(width, height)){
40336             var size = this.adjustForComponents(width, height);
40337             var el = this.layout.getEl();
40338             if (size.height < 1) {
40339                 el.setWidth(size.width);   
40340             } else {
40341                 el.setSize(size.width, size.height);
40342             }
40343             var touch = el.dom.offsetWidth;
40344             this.layout.layout();
40345             // ie requires a double layout on the first pass
40346             if(Roo.isIE && !this.initialized){
40347                 this.initialized = true;
40348                 this.layout.layout();
40349             }
40350         }
40351     },
40352     
40353     // activate all subpanels if not currently active..
40354     
40355     setActiveState : function(active){
40356         this.active = active;
40357         this.setActiveClass(active);
40358         
40359         if(!active){
40360             this.fireEvent("deactivate", this);
40361             return;
40362         }
40363         
40364         this.fireEvent("activate", this);
40365         // not sure if this should happen before or after..
40366         if (!this.layout) {
40367             return; // should not happen..
40368         }
40369         var reg = false;
40370         for (var r in this.layout.regions) {
40371             reg = this.layout.getRegion(r);
40372             if (reg.getActivePanel()) {
40373                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40374                 reg.setActivePanel(reg.getActivePanel());
40375                 continue;
40376             }
40377             if (!reg.panels.length) {
40378                 continue;
40379             }
40380             reg.showPanel(reg.getPanel(0));
40381         }
40382         
40383         
40384         
40385         
40386     },
40387     
40388     /**
40389      * Returns the nested BorderLayout for this panel
40390      * @return {Roo.BorderLayout} 
40391      */
40392     getLayout : function(){
40393         return this.layout;
40394     },
40395     
40396      /**
40397      * Adds a xtype elements to the layout of the nested panel
40398      * <pre><code>
40399
40400 panel.addxtype({
40401        xtype : 'ContentPanel',
40402        region: 'west',
40403        items: [ .... ]
40404    }
40405 );
40406
40407 panel.addxtype({
40408         xtype : 'NestedLayoutPanel',
40409         region: 'west',
40410         layout: {
40411            center: { },
40412            west: { }   
40413         },
40414         items : [ ... list of content panels or nested layout panels.. ]
40415    }
40416 );
40417 </code></pre>
40418      * @param {Object} cfg Xtype definition of item to add.
40419      */
40420     addxtype : function(cfg) {
40421         return this.layout.addxtype(cfg);
40422     
40423     }
40424 });/*
40425  * Based on:
40426  * Ext JS Library 1.1.1
40427  * Copyright(c) 2006-2007, Ext JS, LLC.
40428  *
40429  * Originally Released Under LGPL - original licence link has changed is not relivant.
40430  *
40431  * Fork - LGPL
40432  * <script type="text/javascript">
40433  */
40434 /**
40435  * @class Roo.TabPanel
40436  * @extends Roo.util.Observable
40437  * A lightweight tab container.
40438  * <br><br>
40439  * Usage:
40440  * <pre><code>
40441 // basic tabs 1, built from existing content
40442 var tabs = new Roo.TabPanel("tabs1");
40443 tabs.addTab("script", "View Script");
40444 tabs.addTab("markup", "View Markup");
40445 tabs.activate("script");
40446
40447 // more advanced tabs, built from javascript
40448 var jtabs = new Roo.TabPanel("jtabs");
40449 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40450
40451 // set up the UpdateManager
40452 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40453 var updater = tab2.getUpdateManager();
40454 updater.setDefaultUrl("ajax1.htm");
40455 tab2.on('activate', updater.refresh, updater, true);
40456
40457 // Use setUrl for Ajax loading
40458 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40459 tab3.setUrl("ajax2.htm", null, true);
40460
40461 // Disabled tab
40462 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40463 tab4.disable();
40464
40465 jtabs.activate("jtabs-1");
40466  * </code></pre>
40467  * @constructor
40468  * Create a new TabPanel.
40469  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40470  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40471  */
40472 Roo.bootstrap.panel.Tabs = function(config){
40473     /**
40474     * The container element for this TabPanel.
40475     * @type Roo.Element
40476     */
40477     this.el = Roo.get(config.el);
40478     delete config.el;
40479     if(config){
40480         if(typeof config == "boolean"){
40481             this.tabPosition = config ? "bottom" : "top";
40482         }else{
40483             Roo.apply(this, config);
40484         }
40485     }
40486     
40487     if(this.tabPosition == "bottom"){
40488         // if tabs are at the bottom = create the body first.
40489         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40490         this.el.addClass("roo-tabs-bottom");
40491     }
40492     // next create the tabs holders
40493     
40494     if (this.tabPosition == "west"){
40495         
40496         var reg = this.region; // fake it..
40497         while (reg) {
40498             if (!reg.mgr.parent) {
40499                 break;
40500             }
40501             reg = reg.mgr.parent.region;
40502         }
40503         Roo.log("got nest?");
40504         Roo.log(reg);
40505         if (reg.mgr.getRegion('west')) {
40506             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40507             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40508             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40509             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40510             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40511         
40512             
40513         }
40514         
40515         
40516     } else {
40517      
40518         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40519         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40520         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40521         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40522     }
40523     
40524     
40525     if(Roo.isIE){
40526         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40527     }
40528     
40529     // finally - if tabs are at the top, then create the body last..
40530     if(this.tabPosition != "bottom"){
40531         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40532          * @type Roo.Element
40533          */
40534         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40535         this.el.addClass("roo-tabs-top");
40536     }
40537     this.items = [];
40538
40539     this.bodyEl.setStyle("position", "relative");
40540
40541     this.active = null;
40542     this.activateDelegate = this.activate.createDelegate(this);
40543
40544     this.addEvents({
40545         /**
40546          * @event tabchange
40547          * Fires when the active tab changes
40548          * @param {Roo.TabPanel} this
40549          * @param {Roo.TabPanelItem} activePanel The new active tab
40550          */
40551         "tabchange": true,
40552         /**
40553          * @event beforetabchange
40554          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40555          * @param {Roo.TabPanel} this
40556          * @param {Object} e Set cancel to true on this object to cancel the tab change
40557          * @param {Roo.TabPanelItem} tab The tab being changed to
40558          */
40559         "beforetabchange" : true
40560     });
40561
40562     Roo.EventManager.onWindowResize(this.onResize, this);
40563     this.cpad = this.el.getPadding("lr");
40564     this.hiddenCount = 0;
40565
40566
40567     // toolbar on the tabbar support...
40568     if (this.toolbar) {
40569         alert("no toolbar support yet");
40570         this.toolbar  = false;
40571         /*
40572         var tcfg = this.toolbar;
40573         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40574         this.toolbar = new Roo.Toolbar(tcfg);
40575         if (Roo.isSafari) {
40576             var tbl = tcfg.container.child('table', true);
40577             tbl.setAttribute('width', '100%');
40578         }
40579         */
40580         
40581     }
40582    
40583
40584
40585     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40586 };
40587
40588 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40589     /*
40590      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40591      */
40592     tabPosition : "top",
40593     /*
40594      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40595      */
40596     currentTabWidth : 0,
40597     /*
40598      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40599      */
40600     minTabWidth : 40,
40601     /*
40602      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40603      */
40604     maxTabWidth : 250,
40605     /*
40606      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40607      */
40608     preferredTabWidth : 175,
40609     /*
40610      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40611      */
40612     resizeTabs : false,
40613     /*
40614      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40615      */
40616     monitorResize : true,
40617     /*
40618      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40619      */
40620     toolbar : false,  // set by caller..
40621     
40622     region : false, /// set by caller
40623     
40624     disableTooltips : true, // not used yet...
40625
40626     /**
40627      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40628      * @param {String} id The id of the div to use <b>or create</b>
40629      * @param {String} text The text for the tab
40630      * @param {String} content (optional) Content to put in the TabPanelItem body
40631      * @param {Boolean} closable (optional) True to create a close icon on the tab
40632      * @return {Roo.TabPanelItem} The created TabPanelItem
40633      */
40634     addTab : function(id, text, content, closable, tpl)
40635     {
40636         var item = new Roo.bootstrap.panel.TabItem({
40637             panel: this,
40638             id : id,
40639             text : text,
40640             closable : closable,
40641             tpl : tpl
40642         });
40643         this.addTabItem(item);
40644         if(content){
40645             item.setContent(content);
40646         }
40647         return item;
40648     },
40649
40650     /**
40651      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40652      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40653      * @return {Roo.TabPanelItem}
40654      */
40655     getTab : function(id){
40656         return this.items[id];
40657     },
40658
40659     /**
40660      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40661      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40662      */
40663     hideTab : function(id){
40664         var t = this.items[id];
40665         if(!t.isHidden()){
40666            t.setHidden(true);
40667            this.hiddenCount++;
40668            this.autoSizeTabs();
40669         }
40670     },
40671
40672     /**
40673      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40674      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40675      */
40676     unhideTab : function(id){
40677         var t = this.items[id];
40678         if(t.isHidden()){
40679            t.setHidden(false);
40680            this.hiddenCount--;
40681            this.autoSizeTabs();
40682         }
40683     },
40684
40685     /**
40686      * Adds an existing {@link Roo.TabPanelItem}.
40687      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40688      */
40689     addTabItem : function(item)
40690     {
40691         this.items[item.id] = item;
40692         this.items.push(item);
40693         this.autoSizeTabs();
40694       //  if(this.resizeTabs){
40695     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40696   //         this.autoSizeTabs();
40697 //        }else{
40698 //            item.autoSize();
40699        // }
40700     },
40701
40702     /**
40703      * Removes a {@link Roo.TabPanelItem}.
40704      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40705      */
40706     removeTab : function(id){
40707         var items = this.items;
40708         var tab = items[id];
40709         if(!tab) { return; }
40710         var index = items.indexOf(tab);
40711         if(this.active == tab && items.length > 1){
40712             var newTab = this.getNextAvailable(index);
40713             if(newTab) {
40714                 newTab.activate();
40715             }
40716         }
40717         this.stripEl.dom.removeChild(tab.pnode.dom);
40718         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40719             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40720         }
40721         items.splice(index, 1);
40722         delete this.items[tab.id];
40723         tab.fireEvent("close", tab);
40724         tab.purgeListeners();
40725         this.autoSizeTabs();
40726     },
40727
40728     getNextAvailable : function(start){
40729         var items = this.items;
40730         var index = start;
40731         // look for a next tab that will slide over to
40732         // replace the one being removed
40733         while(index < items.length){
40734             var item = items[++index];
40735             if(item && !item.isHidden()){
40736                 return item;
40737             }
40738         }
40739         // if one isn't found select the previous tab (on the left)
40740         index = start;
40741         while(index >= 0){
40742             var item = items[--index];
40743             if(item && !item.isHidden()){
40744                 return item;
40745             }
40746         }
40747         return null;
40748     },
40749
40750     /**
40751      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40752      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40753      */
40754     disableTab : function(id){
40755         var tab = this.items[id];
40756         if(tab && this.active != tab){
40757             tab.disable();
40758         }
40759     },
40760
40761     /**
40762      * Enables a {@link Roo.TabPanelItem} that is disabled.
40763      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40764      */
40765     enableTab : function(id){
40766         var tab = this.items[id];
40767         tab.enable();
40768     },
40769
40770     /**
40771      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40772      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40773      * @return {Roo.TabPanelItem} The TabPanelItem.
40774      */
40775     activate : function(id)
40776     {
40777         //Roo.log('activite:'  + id);
40778         
40779         var tab = this.items[id];
40780         if(!tab){
40781             return null;
40782         }
40783         if(tab == this.active || tab.disabled){
40784             return tab;
40785         }
40786         var e = {};
40787         this.fireEvent("beforetabchange", this, e, tab);
40788         if(e.cancel !== true && !tab.disabled){
40789             if(this.active){
40790                 this.active.hide();
40791             }
40792             this.active = this.items[id];
40793             this.active.show();
40794             this.fireEvent("tabchange", this, this.active);
40795         }
40796         return tab;
40797     },
40798
40799     /**
40800      * Gets the active {@link Roo.TabPanelItem}.
40801      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40802      */
40803     getActiveTab : function(){
40804         return this.active;
40805     },
40806
40807     /**
40808      * Updates the tab body element to fit the height of the container element
40809      * for overflow scrolling
40810      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40811      */
40812     syncHeight : function(targetHeight){
40813         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40814         var bm = this.bodyEl.getMargins();
40815         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40816         this.bodyEl.setHeight(newHeight);
40817         return newHeight;
40818     },
40819
40820     onResize : function(){
40821         if(this.monitorResize){
40822             this.autoSizeTabs();
40823         }
40824     },
40825
40826     /**
40827      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40828      */
40829     beginUpdate : function(){
40830         this.updating = true;
40831     },
40832
40833     /**
40834      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40835      */
40836     endUpdate : function(){
40837         this.updating = false;
40838         this.autoSizeTabs();
40839     },
40840
40841     /**
40842      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40843      */
40844     autoSizeTabs : function()
40845     {
40846         var count = this.items.length;
40847         var vcount = count - this.hiddenCount;
40848         
40849         if (vcount < 2) {
40850             this.stripEl.hide();
40851         } else {
40852             this.stripEl.show();
40853         }
40854         
40855         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40856             return;
40857         }
40858         
40859         
40860         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40861         var availWidth = Math.floor(w / vcount);
40862         var b = this.stripBody;
40863         if(b.getWidth() > w){
40864             var tabs = this.items;
40865             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40866             if(availWidth < this.minTabWidth){
40867                 /*if(!this.sleft){    // incomplete scrolling code
40868                     this.createScrollButtons();
40869                 }
40870                 this.showScroll();
40871                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40872             }
40873         }else{
40874             if(this.currentTabWidth < this.preferredTabWidth){
40875                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40876             }
40877         }
40878     },
40879
40880     /**
40881      * Returns the number of tabs in this TabPanel.
40882      * @return {Number}
40883      */
40884      getCount : function(){
40885          return this.items.length;
40886      },
40887
40888     /**
40889      * Resizes all the tabs to the passed width
40890      * @param {Number} The new width
40891      */
40892     setTabWidth : function(width){
40893         this.currentTabWidth = width;
40894         for(var i = 0, len = this.items.length; i < len; i++) {
40895                 if(!this.items[i].isHidden()) {
40896                 this.items[i].setWidth(width);
40897             }
40898         }
40899     },
40900
40901     /**
40902      * Destroys this TabPanel
40903      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40904      */
40905     destroy : function(removeEl){
40906         Roo.EventManager.removeResizeListener(this.onResize, this);
40907         for(var i = 0, len = this.items.length; i < len; i++){
40908             this.items[i].purgeListeners();
40909         }
40910         if(removeEl === true){
40911             this.el.update("");
40912             this.el.remove();
40913         }
40914     },
40915     
40916     createStrip : function(container)
40917     {
40918         var strip = document.createElement("nav");
40919         strip.className = Roo.bootstrap.version == 4 ?
40920             "navbar-light bg-light" : 
40921             "navbar navbar-default"; //"x-tabs-wrap";
40922         container.appendChild(strip);
40923         return strip;
40924     },
40925     
40926     createStripList : function(strip)
40927     {
40928         // div wrapper for retard IE
40929         // returns the "tr" element.
40930         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40931         //'<div class="x-tabs-strip-wrap">'+
40932           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40933           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40934         return strip.firstChild; //.firstChild.firstChild.firstChild;
40935     },
40936     createBody : function(container)
40937     {
40938         var body = document.createElement("div");
40939         Roo.id(body, "tab-body");
40940         //Roo.fly(body).addClass("x-tabs-body");
40941         Roo.fly(body).addClass("tab-content");
40942         container.appendChild(body);
40943         return body;
40944     },
40945     createItemBody :function(bodyEl, id){
40946         var body = Roo.getDom(id);
40947         if(!body){
40948             body = document.createElement("div");
40949             body.id = id;
40950         }
40951         //Roo.fly(body).addClass("x-tabs-item-body");
40952         Roo.fly(body).addClass("tab-pane");
40953          bodyEl.insertBefore(body, bodyEl.firstChild);
40954         return body;
40955     },
40956     /** @private */
40957     createStripElements :  function(stripEl, text, closable, tpl)
40958     {
40959         var td = document.createElement("li"); // was td..
40960         td.className = 'nav-item';
40961         
40962         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40963         
40964         
40965         stripEl.appendChild(td);
40966         /*if(closable){
40967             td.className = "x-tabs-closable";
40968             if(!this.closeTpl){
40969                 this.closeTpl = new Roo.Template(
40970                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40971                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40972                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40973                 );
40974             }
40975             var el = this.closeTpl.overwrite(td, {"text": text});
40976             var close = el.getElementsByTagName("div")[0];
40977             var inner = el.getElementsByTagName("em")[0];
40978             return {"el": el, "close": close, "inner": inner};
40979         } else {
40980         */
40981         // not sure what this is..
40982 //            if(!this.tabTpl){
40983                 //this.tabTpl = new Roo.Template(
40984                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40985                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40986                 //);
40987 //                this.tabTpl = new Roo.Template(
40988 //                   '<a href="#">' +
40989 //                   '<span unselectable="on"' +
40990 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40991 //                            ' >{text}</span></a>'
40992 //                );
40993 //                
40994 //            }
40995
40996
40997             var template = tpl || this.tabTpl || false;
40998             
40999             if(!template){
41000                 template =  new Roo.Template(
41001                         Roo.bootstrap.version == 4 ? 
41002                             (
41003                                 '<a class="nav-link" href="#" unselectable="on"' +
41004                                      (this.disableTooltips ? '' : ' title="{text}"') +
41005                                      ' >{text}</a>'
41006                             ) : (
41007                                 '<a class="nav-link" href="#">' +
41008                                 '<span unselectable="on"' +
41009                                          (this.disableTooltips ? '' : ' title="{text}"') +
41010                                     ' >{text}</span></a>'
41011                             )
41012                 );
41013             }
41014             
41015             switch (typeof(template)) {
41016                 case 'object' :
41017                     break;
41018                 case 'string' :
41019                     template = new Roo.Template(template);
41020                     break;
41021                 default :
41022                     break;
41023             }
41024             
41025             var el = template.overwrite(td, {"text": text});
41026             
41027             var inner = el.getElementsByTagName("span")[0];
41028             
41029             return {"el": el, "inner": inner};
41030             
41031     }
41032         
41033     
41034 });
41035
41036 /**
41037  * @class Roo.TabPanelItem
41038  * @extends Roo.util.Observable
41039  * Represents an individual item (tab plus body) in a TabPanel.
41040  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41041  * @param {String} id The id of this TabPanelItem
41042  * @param {String} text The text for the tab of this TabPanelItem
41043  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41044  */
41045 Roo.bootstrap.panel.TabItem = function(config){
41046     /**
41047      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41048      * @type Roo.TabPanel
41049      */
41050     this.tabPanel = config.panel;
41051     /**
41052      * The id for this TabPanelItem
41053      * @type String
41054      */
41055     this.id = config.id;
41056     /** @private */
41057     this.disabled = false;
41058     /** @private */
41059     this.text = config.text;
41060     /** @private */
41061     this.loaded = false;
41062     this.closable = config.closable;
41063
41064     /**
41065      * The body element for this TabPanelItem.
41066      * @type Roo.Element
41067      */
41068     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41069     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41070     this.bodyEl.setStyle("display", "block");
41071     this.bodyEl.setStyle("zoom", "1");
41072     //this.hideAction();
41073
41074     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41075     /** @private */
41076     this.el = Roo.get(els.el);
41077     this.inner = Roo.get(els.inner, true);
41078      this.textEl = Roo.bootstrap.version == 4 ?
41079         this.el : Roo.get(this.el.dom.firstChild, true);
41080
41081     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41082     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41083
41084     
41085 //    this.el.on("mousedown", this.onTabMouseDown, this);
41086     this.el.on("click", this.onTabClick, this);
41087     /** @private */
41088     if(config.closable){
41089         var c = Roo.get(els.close, true);
41090         c.dom.title = this.closeText;
41091         c.addClassOnOver("close-over");
41092         c.on("click", this.closeClick, this);
41093      }
41094
41095     this.addEvents({
41096          /**
41097          * @event activate
41098          * Fires when this tab becomes the active tab.
41099          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41100          * @param {Roo.TabPanelItem} this
41101          */
41102         "activate": true,
41103         /**
41104          * @event beforeclose
41105          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41106          * @param {Roo.TabPanelItem} this
41107          * @param {Object} e Set cancel to true on this object to cancel the close.
41108          */
41109         "beforeclose": true,
41110         /**
41111          * @event close
41112          * Fires when this tab is closed.
41113          * @param {Roo.TabPanelItem} this
41114          */
41115          "close": true,
41116         /**
41117          * @event deactivate
41118          * Fires when this tab is no longer the active tab.
41119          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41120          * @param {Roo.TabPanelItem} this
41121          */
41122          "deactivate" : true
41123     });
41124     this.hidden = false;
41125
41126     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41127 };
41128
41129 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41130            {
41131     purgeListeners : function(){
41132        Roo.util.Observable.prototype.purgeListeners.call(this);
41133        this.el.removeAllListeners();
41134     },
41135     /**
41136      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41137      */
41138     show : function(){
41139         this.status_node.addClass("active");
41140         this.showAction();
41141         if(Roo.isOpera){
41142             this.tabPanel.stripWrap.repaint();
41143         }
41144         this.fireEvent("activate", this.tabPanel, this);
41145     },
41146
41147     /**
41148      * Returns true if this tab is the active tab.
41149      * @return {Boolean}
41150      */
41151     isActive : function(){
41152         return this.tabPanel.getActiveTab() == this;
41153     },
41154
41155     /**
41156      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41157      */
41158     hide : function(){
41159         this.status_node.removeClass("active");
41160         this.hideAction();
41161         this.fireEvent("deactivate", this.tabPanel, this);
41162     },
41163
41164     hideAction : function(){
41165         this.bodyEl.hide();
41166         this.bodyEl.setStyle("position", "absolute");
41167         this.bodyEl.setLeft("-20000px");
41168         this.bodyEl.setTop("-20000px");
41169     },
41170
41171     showAction : function(){
41172         this.bodyEl.setStyle("position", "relative");
41173         this.bodyEl.setTop("");
41174         this.bodyEl.setLeft("");
41175         this.bodyEl.show();
41176     },
41177
41178     /**
41179      * Set the tooltip for the tab.
41180      * @param {String} tooltip The tab's tooltip
41181      */
41182     setTooltip : function(text){
41183         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41184             this.textEl.dom.qtip = text;
41185             this.textEl.dom.removeAttribute('title');
41186         }else{
41187             this.textEl.dom.title = text;
41188         }
41189     },
41190
41191     onTabClick : function(e){
41192         e.preventDefault();
41193         this.tabPanel.activate(this.id);
41194     },
41195
41196     onTabMouseDown : function(e){
41197         e.preventDefault();
41198         this.tabPanel.activate(this.id);
41199     },
41200 /*
41201     getWidth : function(){
41202         return this.inner.getWidth();
41203     },
41204
41205     setWidth : function(width){
41206         var iwidth = width - this.linode.getPadding("lr");
41207         this.inner.setWidth(iwidth);
41208         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41209         this.linode.setWidth(width);
41210     },
41211 */
41212     /**
41213      * Show or hide the tab
41214      * @param {Boolean} hidden True to hide or false to show.
41215      */
41216     setHidden : function(hidden){
41217         this.hidden = hidden;
41218         this.linode.setStyle("display", hidden ? "none" : "");
41219     },
41220
41221     /**
41222      * Returns true if this tab is "hidden"
41223      * @return {Boolean}
41224      */
41225     isHidden : function(){
41226         return this.hidden;
41227     },
41228
41229     /**
41230      * Returns the text for this tab
41231      * @return {String}
41232      */
41233     getText : function(){
41234         return this.text;
41235     },
41236     /*
41237     autoSize : function(){
41238         //this.el.beginMeasure();
41239         this.textEl.setWidth(1);
41240         /*
41241          *  #2804 [new] Tabs in Roojs
41242          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41243          */
41244         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41245         //this.el.endMeasure();
41246     //},
41247
41248     /**
41249      * Sets the text for the tab (Note: this also sets the tooltip text)
41250      * @param {String} text The tab's text and tooltip
41251      */
41252     setText : function(text){
41253         this.text = text;
41254         this.textEl.update(text);
41255         this.setTooltip(text);
41256         //if(!this.tabPanel.resizeTabs){
41257         //    this.autoSize();
41258         //}
41259     },
41260     /**
41261      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41262      */
41263     activate : function(){
41264         this.tabPanel.activate(this.id);
41265     },
41266
41267     /**
41268      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41269      */
41270     disable : function(){
41271         if(this.tabPanel.active != this){
41272             this.disabled = true;
41273             this.status_node.addClass("disabled");
41274         }
41275     },
41276
41277     /**
41278      * Enables this TabPanelItem if it was previously disabled.
41279      */
41280     enable : function(){
41281         this.disabled = false;
41282         this.status_node.removeClass("disabled");
41283     },
41284
41285     /**
41286      * Sets the content for this TabPanelItem.
41287      * @param {String} content The content
41288      * @param {Boolean} loadScripts true to look for and load scripts
41289      */
41290     setContent : function(content, loadScripts){
41291         this.bodyEl.update(content, loadScripts);
41292     },
41293
41294     /**
41295      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41296      * @return {Roo.UpdateManager} The UpdateManager
41297      */
41298     getUpdateManager : function(){
41299         return this.bodyEl.getUpdateManager();
41300     },
41301
41302     /**
41303      * Set a URL to be used to load the content for this TabPanelItem.
41304      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41305      * @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)
41306      * @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)
41307      * @return {Roo.UpdateManager} The UpdateManager
41308      */
41309     setUrl : function(url, params, loadOnce){
41310         if(this.refreshDelegate){
41311             this.un('activate', this.refreshDelegate);
41312         }
41313         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41314         this.on("activate", this.refreshDelegate);
41315         return this.bodyEl.getUpdateManager();
41316     },
41317
41318     /** @private */
41319     _handleRefresh : function(url, params, loadOnce){
41320         if(!loadOnce || !this.loaded){
41321             var updater = this.bodyEl.getUpdateManager();
41322             updater.update(url, params, this._setLoaded.createDelegate(this));
41323         }
41324     },
41325
41326     /**
41327      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41328      *   Will fail silently if the setUrl method has not been called.
41329      *   This does not activate the panel, just updates its content.
41330      */
41331     refresh : function(){
41332         if(this.refreshDelegate){
41333            this.loaded = false;
41334            this.refreshDelegate();
41335         }
41336     },
41337
41338     /** @private */
41339     _setLoaded : function(){
41340         this.loaded = true;
41341     },
41342
41343     /** @private */
41344     closeClick : function(e){
41345         var o = {};
41346         e.stopEvent();
41347         this.fireEvent("beforeclose", this, o);
41348         if(o.cancel !== true){
41349             this.tabPanel.removeTab(this.id);
41350         }
41351     },
41352     /**
41353      * The text displayed in the tooltip for the close icon.
41354      * @type String
41355      */
41356     closeText : "Close this tab"
41357 });
41358 /**
41359 *    This script refer to:
41360 *    Title: International Telephone Input
41361 *    Author: Jack O'Connor
41362 *    Code version:  v12.1.12
41363 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41364 **/
41365
41366 Roo.bootstrap.PhoneInputData = function() {
41367     var d = [
41368       [
41369         "Afghanistan (‫افغانستان‬‎)",
41370         "af",
41371         "93"
41372       ],
41373       [
41374         "Albania (Shqipëri)",
41375         "al",
41376         "355"
41377       ],
41378       [
41379         "Algeria (‫الجزائر‬‎)",
41380         "dz",
41381         "213"
41382       ],
41383       [
41384         "American Samoa",
41385         "as",
41386         "1684"
41387       ],
41388       [
41389         "Andorra",
41390         "ad",
41391         "376"
41392       ],
41393       [
41394         "Angola",
41395         "ao",
41396         "244"
41397       ],
41398       [
41399         "Anguilla",
41400         "ai",
41401         "1264"
41402       ],
41403       [
41404         "Antigua and Barbuda",
41405         "ag",
41406         "1268"
41407       ],
41408       [
41409         "Argentina",
41410         "ar",
41411         "54"
41412       ],
41413       [
41414         "Armenia (Հայաստան)",
41415         "am",
41416         "374"
41417       ],
41418       [
41419         "Aruba",
41420         "aw",
41421         "297"
41422       ],
41423       [
41424         "Australia",
41425         "au",
41426         "61",
41427         0
41428       ],
41429       [
41430         "Austria (Österreich)",
41431         "at",
41432         "43"
41433       ],
41434       [
41435         "Azerbaijan (Azərbaycan)",
41436         "az",
41437         "994"
41438       ],
41439       [
41440         "Bahamas",
41441         "bs",
41442         "1242"
41443       ],
41444       [
41445         "Bahrain (‫البحرين‬‎)",
41446         "bh",
41447         "973"
41448       ],
41449       [
41450         "Bangladesh (বাংলাদেশ)",
41451         "bd",
41452         "880"
41453       ],
41454       [
41455         "Barbados",
41456         "bb",
41457         "1246"
41458       ],
41459       [
41460         "Belarus (Беларусь)",
41461         "by",
41462         "375"
41463       ],
41464       [
41465         "Belgium (België)",
41466         "be",
41467         "32"
41468       ],
41469       [
41470         "Belize",
41471         "bz",
41472         "501"
41473       ],
41474       [
41475         "Benin (Bénin)",
41476         "bj",
41477         "229"
41478       ],
41479       [
41480         "Bermuda",
41481         "bm",
41482         "1441"
41483       ],
41484       [
41485         "Bhutan (འབྲུག)",
41486         "bt",
41487         "975"
41488       ],
41489       [
41490         "Bolivia",
41491         "bo",
41492         "591"
41493       ],
41494       [
41495         "Bosnia and Herzegovina (Босна и Херцеговина)",
41496         "ba",
41497         "387"
41498       ],
41499       [
41500         "Botswana",
41501         "bw",
41502         "267"
41503       ],
41504       [
41505         "Brazil (Brasil)",
41506         "br",
41507         "55"
41508       ],
41509       [
41510         "British Indian Ocean Territory",
41511         "io",
41512         "246"
41513       ],
41514       [
41515         "British Virgin Islands",
41516         "vg",
41517         "1284"
41518       ],
41519       [
41520         "Brunei",
41521         "bn",
41522         "673"
41523       ],
41524       [
41525         "Bulgaria (България)",
41526         "bg",
41527         "359"
41528       ],
41529       [
41530         "Burkina Faso",
41531         "bf",
41532         "226"
41533       ],
41534       [
41535         "Burundi (Uburundi)",
41536         "bi",
41537         "257"
41538       ],
41539       [
41540         "Cambodia (កម្ពុជា)",
41541         "kh",
41542         "855"
41543       ],
41544       [
41545         "Cameroon (Cameroun)",
41546         "cm",
41547         "237"
41548       ],
41549       [
41550         "Canada",
41551         "ca",
41552         "1",
41553         1,
41554         ["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"]
41555       ],
41556       [
41557         "Cape Verde (Kabu Verdi)",
41558         "cv",
41559         "238"
41560       ],
41561       [
41562         "Caribbean Netherlands",
41563         "bq",
41564         "599",
41565         1
41566       ],
41567       [
41568         "Cayman Islands",
41569         "ky",
41570         "1345"
41571       ],
41572       [
41573         "Central African Republic (République centrafricaine)",
41574         "cf",
41575         "236"
41576       ],
41577       [
41578         "Chad (Tchad)",
41579         "td",
41580         "235"
41581       ],
41582       [
41583         "Chile",
41584         "cl",
41585         "56"
41586       ],
41587       [
41588         "China (中国)",
41589         "cn",
41590         "86"
41591       ],
41592       [
41593         "Christmas Island",
41594         "cx",
41595         "61",
41596         2
41597       ],
41598       [
41599         "Cocos (Keeling) Islands",
41600         "cc",
41601         "61",
41602         1
41603       ],
41604       [
41605         "Colombia",
41606         "co",
41607         "57"
41608       ],
41609       [
41610         "Comoros (‫جزر القمر‬‎)",
41611         "km",
41612         "269"
41613       ],
41614       [
41615         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41616         "cd",
41617         "243"
41618       ],
41619       [
41620         "Congo (Republic) (Congo-Brazzaville)",
41621         "cg",
41622         "242"
41623       ],
41624       [
41625         "Cook Islands",
41626         "ck",
41627         "682"
41628       ],
41629       [
41630         "Costa Rica",
41631         "cr",
41632         "506"
41633       ],
41634       [
41635         "Côte d’Ivoire",
41636         "ci",
41637         "225"
41638       ],
41639       [
41640         "Croatia (Hrvatska)",
41641         "hr",
41642         "385"
41643       ],
41644       [
41645         "Cuba",
41646         "cu",
41647         "53"
41648       ],
41649       [
41650         "Curaçao",
41651         "cw",
41652         "599",
41653         0
41654       ],
41655       [
41656         "Cyprus (Κύπρος)",
41657         "cy",
41658         "357"
41659       ],
41660       [
41661         "Czech Republic (Česká republika)",
41662         "cz",
41663         "420"
41664       ],
41665       [
41666         "Denmark (Danmark)",
41667         "dk",
41668         "45"
41669       ],
41670       [
41671         "Djibouti",
41672         "dj",
41673         "253"
41674       ],
41675       [
41676         "Dominica",
41677         "dm",
41678         "1767"
41679       ],
41680       [
41681         "Dominican Republic (República Dominicana)",
41682         "do",
41683         "1",
41684         2,
41685         ["809", "829", "849"]
41686       ],
41687       [
41688         "Ecuador",
41689         "ec",
41690         "593"
41691       ],
41692       [
41693         "Egypt (‫مصر‬‎)",
41694         "eg",
41695         "20"
41696       ],
41697       [
41698         "El Salvador",
41699         "sv",
41700         "503"
41701       ],
41702       [
41703         "Equatorial Guinea (Guinea Ecuatorial)",
41704         "gq",
41705         "240"
41706       ],
41707       [
41708         "Eritrea",
41709         "er",
41710         "291"
41711       ],
41712       [
41713         "Estonia (Eesti)",
41714         "ee",
41715         "372"
41716       ],
41717       [
41718         "Ethiopia",
41719         "et",
41720         "251"
41721       ],
41722       [
41723         "Falkland Islands (Islas Malvinas)",
41724         "fk",
41725         "500"
41726       ],
41727       [
41728         "Faroe Islands (Føroyar)",
41729         "fo",
41730         "298"
41731       ],
41732       [
41733         "Fiji",
41734         "fj",
41735         "679"
41736       ],
41737       [
41738         "Finland (Suomi)",
41739         "fi",
41740         "358",
41741         0
41742       ],
41743       [
41744         "France",
41745         "fr",
41746         "33"
41747       ],
41748       [
41749         "French Guiana (Guyane française)",
41750         "gf",
41751         "594"
41752       ],
41753       [
41754         "French Polynesia (Polynésie française)",
41755         "pf",
41756         "689"
41757       ],
41758       [
41759         "Gabon",
41760         "ga",
41761         "241"
41762       ],
41763       [
41764         "Gambia",
41765         "gm",
41766         "220"
41767       ],
41768       [
41769         "Georgia (საქართველო)",
41770         "ge",
41771         "995"
41772       ],
41773       [
41774         "Germany (Deutschland)",
41775         "de",
41776         "49"
41777       ],
41778       [
41779         "Ghana (Gaana)",
41780         "gh",
41781         "233"
41782       ],
41783       [
41784         "Gibraltar",
41785         "gi",
41786         "350"
41787       ],
41788       [
41789         "Greece (Ελλάδα)",
41790         "gr",
41791         "30"
41792       ],
41793       [
41794         "Greenland (Kalaallit Nunaat)",
41795         "gl",
41796         "299"
41797       ],
41798       [
41799         "Grenada",
41800         "gd",
41801         "1473"
41802       ],
41803       [
41804         "Guadeloupe",
41805         "gp",
41806         "590",
41807         0
41808       ],
41809       [
41810         "Guam",
41811         "gu",
41812         "1671"
41813       ],
41814       [
41815         "Guatemala",
41816         "gt",
41817         "502"
41818       ],
41819       [
41820         "Guernsey",
41821         "gg",
41822         "44",
41823         1
41824       ],
41825       [
41826         "Guinea (Guinée)",
41827         "gn",
41828         "224"
41829       ],
41830       [
41831         "Guinea-Bissau (Guiné Bissau)",
41832         "gw",
41833         "245"
41834       ],
41835       [
41836         "Guyana",
41837         "gy",
41838         "592"
41839       ],
41840       [
41841         "Haiti",
41842         "ht",
41843         "509"
41844       ],
41845       [
41846         "Honduras",
41847         "hn",
41848         "504"
41849       ],
41850       [
41851         "Hong Kong (香港)",
41852         "hk",
41853         "852"
41854       ],
41855       [
41856         "Hungary (Magyarország)",
41857         "hu",
41858         "36"
41859       ],
41860       [
41861         "Iceland (Ísland)",
41862         "is",
41863         "354"
41864       ],
41865       [
41866         "India (भारत)",
41867         "in",
41868         "91"
41869       ],
41870       [
41871         "Indonesia",
41872         "id",
41873         "62"
41874       ],
41875       [
41876         "Iran (‫ایران‬‎)",
41877         "ir",
41878         "98"
41879       ],
41880       [
41881         "Iraq (‫العراق‬‎)",
41882         "iq",
41883         "964"
41884       ],
41885       [
41886         "Ireland",
41887         "ie",
41888         "353"
41889       ],
41890       [
41891         "Isle of Man",
41892         "im",
41893         "44",
41894         2
41895       ],
41896       [
41897         "Israel (‫ישראל‬‎)",
41898         "il",
41899         "972"
41900       ],
41901       [
41902         "Italy (Italia)",
41903         "it",
41904         "39",
41905         0
41906       ],
41907       [
41908         "Jamaica",
41909         "jm",
41910         "1876"
41911       ],
41912       [
41913         "Japan (日本)",
41914         "jp",
41915         "81"
41916       ],
41917       [
41918         "Jersey",
41919         "je",
41920         "44",
41921         3
41922       ],
41923       [
41924         "Jordan (‫الأردن‬‎)",
41925         "jo",
41926         "962"
41927       ],
41928       [
41929         "Kazakhstan (Казахстан)",
41930         "kz",
41931         "7",
41932         1
41933       ],
41934       [
41935         "Kenya",
41936         "ke",
41937         "254"
41938       ],
41939       [
41940         "Kiribati",
41941         "ki",
41942         "686"
41943       ],
41944       [
41945         "Kosovo",
41946         "xk",
41947         "383"
41948       ],
41949       [
41950         "Kuwait (‫الكويت‬‎)",
41951         "kw",
41952         "965"
41953       ],
41954       [
41955         "Kyrgyzstan (Кыргызстан)",
41956         "kg",
41957         "996"
41958       ],
41959       [
41960         "Laos (ລາວ)",
41961         "la",
41962         "856"
41963       ],
41964       [
41965         "Latvia (Latvija)",
41966         "lv",
41967         "371"
41968       ],
41969       [
41970         "Lebanon (‫لبنان‬‎)",
41971         "lb",
41972         "961"
41973       ],
41974       [
41975         "Lesotho",
41976         "ls",
41977         "266"
41978       ],
41979       [
41980         "Liberia",
41981         "lr",
41982         "231"
41983       ],
41984       [
41985         "Libya (‫ليبيا‬‎)",
41986         "ly",
41987         "218"
41988       ],
41989       [
41990         "Liechtenstein",
41991         "li",
41992         "423"
41993       ],
41994       [
41995         "Lithuania (Lietuva)",
41996         "lt",
41997         "370"
41998       ],
41999       [
42000         "Luxembourg",
42001         "lu",
42002         "352"
42003       ],
42004       [
42005         "Macau (澳門)",
42006         "mo",
42007         "853"
42008       ],
42009       [
42010         "Macedonia (FYROM) (Македонија)",
42011         "mk",
42012         "389"
42013       ],
42014       [
42015         "Madagascar (Madagasikara)",
42016         "mg",
42017         "261"
42018       ],
42019       [
42020         "Malawi",
42021         "mw",
42022         "265"
42023       ],
42024       [
42025         "Malaysia",
42026         "my",
42027         "60"
42028       ],
42029       [
42030         "Maldives",
42031         "mv",
42032         "960"
42033       ],
42034       [
42035         "Mali",
42036         "ml",
42037         "223"
42038       ],
42039       [
42040         "Malta",
42041         "mt",
42042         "356"
42043       ],
42044       [
42045         "Marshall Islands",
42046         "mh",
42047         "692"
42048       ],
42049       [
42050         "Martinique",
42051         "mq",
42052         "596"
42053       ],
42054       [
42055         "Mauritania (‫موريتانيا‬‎)",
42056         "mr",
42057         "222"
42058       ],
42059       [
42060         "Mauritius (Moris)",
42061         "mu",
42062         "230"
42063       ],
42064       [
42065         "Mayotte",
42066         "yt",
42067         "262",
42068         1
42069       ],
42070       [
42071         "Mexico (México)",
42072         "mx",
42073         "52"
42074       ],
42075       [
42076         "Micronesia",
42077         "fm",
42078         "691"
42079       ],
42080       [
42081         "Moldova (Republica Moldova)",
42082         "md",
42083         "373"
42084       ],
42085       [
42086         "Monaco",
42087         "mc",
42088         "377"
42089       ],
42090       [
42091         "Mongolia (Монгол)",
42092         "mn",
42093         "976"
42094       ],
42095       [
42096         "Montenegro (Crna Gora)",
42097         "me",
42098         "382"
42099       ],
42100       [
42101         "Montserrat",
42102         "ms",
42103         "1664"
42104       ],
42105       [
42106         "Morocco (‫المغرب‬‎)",
42107         "ma",
42108         "212",
42109         0
42110       ],
42111       [
42112         "Mozambique (Moçambique)",
42113         "mz",
42114         "258"
42115       ],
42116       [
42117         "Myanmar (Burma) (မြန်မာ)",
42118         "mm",
42119         "95"
42120       ],
42121       [
42122         "Namibia (Namibië)",
42123         "na",
42124         "264"
42125       ],
42126       [
42127         "Nauru",
42128         "nr",
42129         "674"
42130       ],
42131       [
42132         "Nepal (नेपाल)",
42133         "np",
42134         "977"
42135       ],
42136       [
42137         "Netherlands (Nederland)",
42138         "nl",
42139         "31"
42140       ],
42141       [
42142         "New Caledonia (Nouvelle-Calédonie)",
42143         "nc",
42144         "687"
42145       ],
42146       [
42147         "New Zealand",
42148         "nz",
42149         "64"
42150       ],
42151       [
42152         "Nicaragua",
42153         "ni",
42154         "505"
42155       ],
42156       [
42157         "Niger (Nijar)",
42158         "ne",
42159         "227"
42160       ],
42161       [
42162         "Nigeria",
42163         "ng",
42164         "234"
42165       ],
42166       [
42167         "Niue",
42168         "nu",
42169         "683"
42170       ],
42171       [
42172         "Norfolk Island",
42173         "nf",
42174         "672"
42175       ],
42176       [
42177         "North Korea (조선 민주주의 인민 공화국)",
42178         "kp",
42179         "850"
42180       ],
42181       [
42182         "Northern Mariana Islands",
42183         "mp",
42184         "1670"
42185       ],
42186       [
42187         "Norway (Norge)",
42188         "no",
42189         "47",
42190         0
42191       ],
42192       [
42193         "Oman (‫عُمان‬‎)",
42194         "om",
42195         "968"
42196       ],
42197       [
42198         "Pakistan (‫پاکستان‬‎)",
42199         "pk",
42200         "92"
42201       ],
42202       [
42203         "Palau",
42204         "pw",
42205         "680"
42206       ],
42207       [
42208         "Palestine (‫فلسطين‬‎)",
42209         "ps",
42210         "970"
42211       ],
42212       [
42213         "Panama (Panamá)",
42214         "pa",
42215         "507"
42216       ],
42217       [
42218         "Papua New Guinea",
42219         "pg",
42220         "675"
42221       ],
42222       [
42223         "Paraguay",
42224         "py",
42225         "595"
42226       ],
42227       [
42228         "Peru (Perú)",
42229         "pe",
42230         "51"
42231       ],
42232       [
42233         "Philippines",
42234         "ph",
42235         "63"
42236       ],
42237       [
42238         "Poland (Polska)",
42239         "pl",
42240         "48"
42241       ],
42242       [
42243         "Portugal",
42244         "pt",
42245         "351"
42246       ],
42247       [
42248         "Puerto Rico",
42249         "pr",
42250         "1",
42251         3,
42252         ["787", "939"]
42253       ],
42254       [
42255         "Qatar (‫قطر‬‎)",
42256         "qa",
42257         "974"
42258       ],
42259       [
42260         "Réunion (La Réunion)",
42261         "re",
42262         "262",
42263         0
42264       ],
42265       [
42266         "Romania (România)",
42267         "ro",
42268         "40"
42269       ],
42270       [
42271         "Russia (Россия)",
42272         "ru",
42273         "7",
42274         0
42275       ],
42276       [
42277         "Rwanda",
42278         "rw",
42279         "250"
42280       ],
42281       [
42282         "Saint Barthélemy",
42283         "bl",
42284         "590",
42285         1
42286       ],
42287       [
42288         "Saint Helena",
42289         "sh",
42290         "290"
42291       ],
42292       [
42293         "Saint Kitts and Nevis",
42294         "kn",
42295         "1869"
42296       ],
42297       [
42298         "Saint Lucia",
42299         "lc",
42300         "1758"
42301       ],
42302       [
42303         "Saint Martin (Saint-Martin (partie française))",
42304         "mf",
42305         "590",
42306         2
42307       ],
42308       [
42309         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42310         "pm",
42311         "508"
42312       ],
42313       [
42314         "Saint Vincent and the Grenadines",
42315         "vc",
42316         "1784"
42317       ],
42318       [
42319         "Samoa",
42320         "ws",
42321         "685"
42322       ],
42323       [
42324         "San Marino",
42325         "sm",
42326         "378"
42327       ],
42328       [
42329         "São Tomé and Príncipe (São Tomé e Príncipe)",
42330         "st",
42331         "239"
42332       ],
42333       [
42334         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42335         "sa",
42336         "966"
42337       ],
42338       [
42339         "Senegal (Sénégal)",
42340         "sn",
42341         "221"
42342       ],
42343       [
42344         "Serbia (Србија)",
42345         "rs",
42346         "381"
42347       ],
42348       [
42349         "Seychelles",
42350         "sc",
42351         "248"
42352       ],
42353       [
42354         "Sierra Leone",
42355         "sl",
42356         "232"
42357       ],
42358       [
42359         "Singapore",
42360         "sg",
42361         "65"
42362       ],
42363       [
42364         "Sint Maarten",
42365         "sx",
42366         "1721"
42367       ],
42368       [
42369         "Slovakia (Slovensko)",
42370         "sk",
42371         "421"
42372       ],
42373       [
42374         "Slovenia (Slovenija)",
42375         "si",
42376         "386"
42377       ],
42378       [
42379         "Solomon Islands",
42380         "sb",
42381         "677"
42382       ],
42383       [
42384         "Somalia (Soomaaliya)",
42385         "so",
42386         "252"
42387       ],
42388       [
42389         "South Africa",
42390         "za",
42391         "27"
42392       ],
42393       [
42394         "South Korea (대한민국)",
42395         "kr",
42396         "82"
42397       ],
42398       [
42399         "South Sudan (‫جنوب السودان‬‎)",
42400         "ss",
42401         "211"
42402       ],
42403       [
42404         "Spain (España)",
42405         "es",
42406         "34"
42407       ],
42408       [
42409         "Sri Lanka (ශ්‍රී ලංකාව)",
42410         "lk",
42411         "94"
42412       ],
42413       [
42414         "Sudan (‫السودان‬‎)",
42415         "sd",
42416         "249"
42417       ],
42418       [
42419         "Suriname",
42420         "sr",
42421         "597"
42422       ],
42423       [
42424         "Svalbard and Jan Mayen",
42425         "sj",
42426         "47",
42427         1
42428       ],
42429       [
42430         "Swaziland",
42431         "sz",
42432         "268"
42433       ],
42434       [
42435         "Sweden (Sverige)",
42436         "se",
42437         "46"
42438       ],
42439       [
42440         "Switzerland (Schweiz)",
42441         "ch",
42442         "41"
42443       ],
42444       [
42445         "Syria (‫سوريا‬‎)",
42446         "sy",
42447         "963"
42448       ],
42449       [
42450         "Taiwan (台灣)",
42451         "tw",
42452         "886"
42453       ],
42454       [
42455         "Tajikistan",
42456         "tj",
42457         "992"
42458       ],
42459       [
42460         "Tanzania",
42461         "tz",
42462         "255"
42463       ],
42464       [
42465         "Thailand (ไทย)",
42466         "th",
42467         "66"
42468       ],
42469       [
42470         "Timor-Leste",
42471         "tl",
42472         "670"
42473       ],
42474       [
42475         "Togo",
42476         "tg",
42477         "228"
42478       ],
42479       [
42480         "Tokelau",
42481         "tk",
42482         "690"
42483       ],
42484       [
42485         "Tonga",
42486         "to",
42487         "676"
42488       ],
42489       [
42490         "Trinidad and Tobago",
42491         "tt",
42492         "1868"
42493       ],
42494       [
42495         "Tunisia (‫تونس‬‎)",
42496         "tn",
42497         "216"
42498       ],
42499       [
42500         "Turkey (Türkiye)",
42501         "tr",
42502         "90"
42503       ],
42504       [
42505         "Turkmenistan",
42506         "tm",
42507         "993"
42508       ],
42509       [
42510         "Turks and Caicos Islands",
42511         "tc",
42512         "1649"
42513       ],
42514       [
42515         "Tuvalu",
42516         "tv",
42517         "688"
42518       ],
42519       [
42520         "U.S. Virgin Islands",
42521         "vi",
42522         "1340"
42523       ],
42524       [
42525         "Uganda",
42526         "ug",
42527         "256"
42528       ],
42529       [
42530         "Ukraine (Україна)",
42531         "ua",
42532         "380"
42533       ],
42534       [
42535         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42536         "ae",
42537         "971"
42538       ],
42539       [
42540         "United Kingdom",
42541         "gb",
42542         "44",
42543         0
42544       ],
42545       [
42546         "United States",
42547         "us",
42548         "1",
42549         0
42550       ],
42551       [
42552         "Uruguay",
42553         "uy",
42554         "598"
42555       ],
42556       [
42557         "Uzbekistan (Oʻzbekiston)",
42558         "uz",
42559         "998"
42560       ],
42561       [
42562         "Vanuatu",
42563         "vu",
42564         "678"
42565       ],
42566       [
42567         "Vatican City (Città del Vaticano)",
42568         "va",
42569         "39",
42570         1
42571       ],
42572       [
42573         "Venezuela",
42574         "ve",
42575         "58"
42576       ],
42577       [
42578         "Vietnam (Việt Nam)",
42579         "vn",
42580         "84"
42581       ],
42582       [
42583         "Wallis and Futuna (Wallis-et-Futuna)",
42584         "wf",
42585         "681"
42586       ],
42587       [
42588         "Western Sahara (‫الصحراء الغربية‬‎)",
42589         "eh",
42590         "212",
42591         1
42592       ],
42593       [
42594         "Yemen (‫اليمن‬‎)",
42595         "ye",
42596         "967"
42597       ],
42598       [
42599         "Zambia",
42600         "zm",
42601         "260"
42602       ],
42603       [
42604         "Zimbabwe",
42605         "zw",
42606         "263"
42607       ],
42608       [
42609         "Åland Islands",
42610         "ax",
42611         "358",
42612         1
42613       ]
42614   ];
42615   
42616   return d;
42617 }/**
42618 *    This script refer to:
42619 *    Title: International Telephone Input
42620 *    Author: Jack O'Connor
42621 *    Code version:  v12.1.12
42622 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42623 **/
42624
42625 /**
42626  * @class Roo.bootstrap.PhoneInput
42627  * @extends Roo.bootstrap.TriggerField
42628  * An input with International dial-code selection
42629  
42630  * @cfg {String} defaultDialCode default '+852'
42631  * @cfg {Array} preferedCountries default []
42632   
42633  * @constructor
42634  * Create a new PhoneInput.
42635  * @param {Object} config Configuration options
42636  */
42637
42638 Roo.bootstrap.PhoneInput = function(config) {
42639     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42640 };
42641
42642 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42643         
42644         listWidth: undefined,
42645         
42646         selectedClass: 'active',
42647         
42648         invalidClass : "has-warning",
42649         
42650         validClass: 'has-success',
42651         
42652         allowed: '0123456789',
42653         
42654         max_length: 15,
42655         
42656         /**
42657          * @cfg {String} defaultDialCode The default dial code when initializing the input
42658          */
42659         defaultDialCode: '+852',
42660         
42661         /**
42662          * @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
42663          */
42664         preferedCountries: false,
42665         
42666         getAutoCreate : function()
42667         {
42668             var data = Roo.bootstrap.PhoneInputData();
42669             var align = this.labelAlign || this.parentLabelAlign();
42670             var id = Roo.id();
42671             
42672             this.allCountries = [];
42673             this.dialCodeMapping = [];
42674             
42675             for (var i = 0; i < data.length; i++) {
42676               var c = data[i];
42677               this.allCountries[i] = {
42678                 name: c[0],
42679                 iso2: c[1],
42680                 dialCode: c[2],
42681                 priority: c[3] || 0,
42682                 areaCodes: c[4] || null
42683               };
42684               this.dialCodeMapping[c[2]] = {
42685                   name: c[0],
42686                   iso2: c[1],
42687                   priority: c[3] || 0,
42688                   areaCodes: c[4] || null
42689               };
42690             }
42691             
42692             var cfg = {
42693                 cls: 'form-group',
42694                 cn: []
42695             };
42696             
42697             var input =  {
42698                 tag: 'input',
42699                 id : id,
42700                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42701                 maxlength: this.max_length,
42702                 cls : 'form-control tel-input',
42703                 autocomplete: 'new-password'
42704             };
42705             
42706             var hiddenInput = {
42707                 tag: 'input',
42708                 type: 'hidden',
42709                 cls: 'hidden-tel-input'
42710             };
42711             
42712             if (this.name) {
42713                 hiddenInput.name = this.name;
42714             }
42715             
42716             if (this.disabled) {
42717                 input.disabled = true;
42718             }
42719             
42720             var flag_container = {
42721                 tag: 'div',
42722                 cls: 'flag-box',
42723                 cn: [
42724                     {
42725                         tag: 'div',
42726                         cls: 'flag'
42727                     },
42728                     {
42729                         tag: 'div',
42730                         cls: 'caret'
42731                     }
42732                 ]
42733             };
42734             
42735             var box = {
42736                 tag: 'div',
42737                 cls: this.hasFeedback ? 'has-feedback' : '',
42738                 cn: [
42739                     hiddenInput,
42740                     input,
42741                     {
42742                         tag: 'input',
42743                         cls: 'dial-code-holder',
42744                         disabled: true
42745                     }
42746                 ]
42747             };
42748             
42749             var container = {
42750                 cls: 'roo-select2-container input-group',
42751                 cn: [
42752                     flag_container,
42753                     box
42754                 ]
42755             };
42756             
42757             if (this.fieldLabel.length) {
42758                 var indicator = {
42759                     tag: 'i',
42760                     tooltip: 'This field is required'
42761                 };
42762                 
42763                 var label = {
42764                     tag: 'label',
42765                     'for':  id,
42766                     cls: 'control-label',
42767                     cn: []
42768                 };
42769                 
42770                 var label_text = {
42771                     tag: 'span',
42772                     html: this.fieldLabel
42773                 };
42774                 
42775                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42776                 label.cn = [
42777                     indicator,
42778                     label_text
42779                 ];
42780                 
42781                 if(this.indicatorpos == 'right') {
42782                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42783                     label.cn = [
42784                         label_text,
42785                         indicator
42786                     ];
42787                 }
42788                 
42789                 if(align == 'left') {
42790                     container = {
42791                         tag: 'div',
42792                         cn: [
42793                             container
42794                         ]
42795                     };
42796                     
42797                     if(this.labelWidth > 12){
42798                         label.style = "width: " + this.labelWidth + 'px';
42799                     }
42800                     if(this.labelWidth < 13 && this.labelmd == 0){
42801                         this.labelmd = this.labelWidth;
42802                     }
42803                     if(this.labellg > 0){
42804                         label.cls += ' col-lg-' + this.labellg;
42805                         input.cls += ' col-lg-' + (12 - this.labellg);
42806                     }
42807                     if(this.labelmd > 0){
42808                         label.cls += ' col-md-' + this.labelmd;
42809                         container.cls += ' col-md-' + (12 - this.labelmd);
42810                     }
42811                     if(this.labelsm > 0){
42812                         label.cls += ' col-sm-' + this.labelsm;
42813                         container.cls += ' col-sm-' + (12 - this.labelsm);
42814                     }
42815                     if(this.labelxs > 0){
42816                         label.cls += ' col-xs-' + this.labelxs;
42817                         container.cls += ' col-xs-' + (12 - this.labelxs);
42818                     }
42819                 }
42820             }
42821             
42822             cfg.cn = [
42823                 label,
42824                 container
42825             ];
42826             
42827             var settings = this;
42828             
42829             ['xs','sm','md','lg'].map(function(size){
42830                 if (settings[size]) {
42831                     cfg.cls += ' col-' + size + '-' + settings[size];
42832                 }
42833             });
42834             
42835             this.store = new Roo.data.Store({
42836                 proxy : new Roo.data.MemoryProxy({}),
42837                 reader : new Roo.data.JsonReader({
42838                     fields : [
42839                         {
42840                             'name' : 'name',
42841                             'type' : 'string'
42842                         },
42843                         {
42844                             'name' : 'iso2',
42845                             'type' : 'string'
42846                         },
42847                         {
42848                             'name' : 'dialCode',
42849                             'type' : 'string'
42850                         },
42851                         {
42852                             'name' : 'priority',
42853                             'type' : 'string'
42854                         },
42855                         {
42856                             'name' : 'areaCodes',
42857                             'type' : 'string'
42858                         }
42859                     ]
42860                 })
42861             });
42862             
42863             if(!this.preferedCountries) {
42864                 this.preferedCountries = [
42865                     'hk',
42866                     'gb',
42867                     'us'
42868                 ];
42869             }
42870             
42871             var p = this.preferedCountries.reverse();
42872             
42873             if(p) {
42874                 for (var i = 0; i < p.length; i++) {
42875                     for (var j = 0; j < this.allCountries.length; j++) {
42876                         if(this.allCountries[j].iso2 == p[i]) {
42877                             var t = this.allCountries[j];
42878                             this.allCountries.splice(j,1);
42879                             this.allCountries.unshift(t);
42880                         }
42881                     } 
42882                 }
42883             }
42884             
42885             this.store.proxy.data = {
42886                 success: true,
42887                 data: this.allCountries
42888             };
42889             
42890             return cfg;
42891         },
42892         
42893         initEvents : function()
42894         {
42895             this.createList();
42896             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42897             
42898             this.indicator = this.indicatorEl();
42899             this.flag = this.flagEl();
42900             this.dialCodeHolder = this.dialCodeHolderEl();
42901             
42902             this.trigger = this.el.select('div.flag-box',true).first();
42903             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42904             
42905             var _this = this;
42906             
42907             (function(){
42908                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42909                 _this.list.setWidth(lw);
42910             }).defer(100);
42911             
42912             this.list.on('mouseover', this.onViewOver, this);
42913             this.list.on('mousemove', this.onViewMove, this);
42914             this.inputEl().on("keyup", this.onKeyUp, this);
42915             this.inputEl().on("keypress", this.onKeyPress, this);
42916             
42917             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42918
42919             this.view = new Roo.View(this.list, this.tpl, {
42920                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42921             });
42922             
42923             this.view.on('click', this.onViewClick, this);
42924             this.setValue(this.defaultDialCode);
42925         },
42926         
42927         onTriggerClick : function(e)
42928         {
42929             Roo.log('trigger click');
42930             if(this.disabled){
42931                 return;
42932             }
42933             
42934             if(this.isExpanded()){
42935                 this.collapse();
42936                 this.hasFocus = false;
42937             }else {
42938                 this.store.load({});
42939                 this.hasFocus = true;
42940                 this.expand();
42941             }
42942         },
42943         
42944         isExpanded : function()
42945         {
42946             return this.list.isVisible();
42947         },
42948         
42949         collapse : function()
42950         {
42951             if(!this.isExpanded()){
42952                 return;
42953             }
42954             this.list.hide();
42955             Roo.get(document).un('mousedown', this.collapseIf, this);
42956             Roo.get(document).un('mousewheel', this.collapseIf, this);
42957             this.fireEvent('collapse', this);
42958             this.validate();
42959         },
42960         
42961         expand : function()
42962         {
42963             Roo.log('expand');
42964
42965             if(this.isExpanded() || !this.hasFocus){
42966                 return;
42967             }
42968             
42969             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42970             this.list.setWidth(lw);
42971             
42972             this.list.show();
42973             this.restrictHeight();
42974             
42975             Roo.get(document).on('mousedown', this.collapseIf, this);
42976             Roo.get(document).on('mousewheel', this.collapseIf, this);
42977             
42978             this.fireEvent('expand', this);
42979         },
42980         
42981         restrictHeight : function()
42982         {
42983             this.list.alignTo(this.inputEl(), this.listAlign);
42984             this.list.alignTo(this.inputEl(), this.listAlign);
42985         },
42986         
42987         onViewOver : function(e, t)
42988         {
42989             if(this.inKeyMode){
42990                 return;
42991             }
42992             var item = this.view.findItemFromChild(t);
42993             
42994             if(item){
42995                 var index = this.view.indexOf(item);
42996                 this.select(index, false);
42997             }
42998         },
42999
43000         // private
43001         onViewClick : function(view, doFocus, el, e)
43002         {
43003             var index = this.view.getSelectedIndexes()[0];
43004             
43005             var r = this.store.getAt(index);
43006             
43007             if(r){
43008                 this.onSelect(r, index);
43009             }
43010             if(doFocus !== false && !this.blockFocus){
43011                 this.inputEl().focus();
43012             }
43013         },
43014         
43015         onViewMove : function(e, t)
43016         {
43017             this.inKeyMode = false;
43018         },
43019         
43020         select : function(index, scrollIntoView)
43021         {
43022             this.selectedIndex = index;
43023             this.view.select(index);
43024             if(scrollIntoView !== false){
43025                 var el = this.view.getNode(index);
43026                 if(el){
43027                     this.list.scrollChildIntoView(el, false);
43028                 }
43029             }
43030         },
43031         
43032         createList : function()
43033         {
43034             this.list = Roo.get(document.body).createChild({
43035                 tag: 'ul',
43036                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43037                 style: 'display:none'
43038             });
43039             
43040             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43041         },
43042         
43043         collapseIf : function(e)
43044         {
43045             var in_combo  = e.within(this.el);
43046             var in_list =  e.within(this.list);
43047             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43048             
43049             if (in_combo || in_list || is_list) {
43050                 return;
43051             }
43052             this.collapse();
43053         },
43054         
43055         onSelect : function(record, index)
43056         {
43057             if(this.fireEvent('beforeselect', this, record, index) !== false){
43058                 
43059                 this.setFlagClass(record.data.iso2);
43060                 this.setDialCode(record.data.dialCode);
43061                 this.hasFocus = false;
43062                 this.collapse();
43063                 this.fireEvent('select', this, record, index);
43064             }
43065         },
43066         
43067         flagEl : function()
43068         {
43069             var flag = this.el.select('div.flag',true).first();
43070             if(!flag){
43071                 return false;
43072             }
43073             return flag;
43074         },
43075         
43076         dialCodeHolderEl : function()
43077         {
43078             var d = this.el.select('input.dial-code-holder',true).first();
43079             if(!d){
43080                 return false;
43081             }
43082             return d;
43083         },
43084         
43085         setDialCode : function(v)
43086         {
43087             this.dialCodeHolder.dom.value = '+'+v;
43088         },
43089         
43090         setFlagClass : function(n)
43091         {
43092             this.flag.dom.className = 'flag '+n;
43093         },
43094         
43095         getValue : function()
43096         {
43097             var v = this.inputEl().getValue();
43098             if(this.dialCodeHolder) {
43099                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43100             }
43101             return v;
43102         },
43103         
43104         setValue : function(v)
43105         {
43106             var d = this.getDialCode(v);
43107             
43108             //invalid dial code
43109             if(v.length == 0 || !d || d.length == 0) {
43110                 if(this.rendered){
43111                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43112                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43113                 }
43114                 return;
43115             }
43116             
43117             //valid dial code
43118             this.setFlagClass(this.dialCodeMapping[d].iso2);
43119             this.setDialCode(d);
43120             this.inputEl().dom.value = v.replace('+'+d,'');
43121             this.hiddenEl().dom.value = this.getValue();
43122             
43123             this.validate();
43124         },
43125         
43126         getDialCode : function(v)
43127         {
43128             v = v ||  '';
43129             
43130             if (v.length == 0) {
43131                 return this.dialCodeHolder.dom.value;
43132             }
43133             
43134             var dialCode = "";
43135             if (v.charAt(0) != "+") {
43136                 return false;
43137             }
43138             var numericChars = "";
43139             for (var i = 1; i < v.length; i++) {
43140               var c = v.charAt(i);
43141               if (!isNaN(c)) {
43142                 numericChars += c;
43143                 if (this.dialCodeMapping[numericChars]) {
43144                   dialCode = v.substr(1, i);
43145                 }
43146                 if (numericChars.length == 4) {
43147                   break;
43148                 }
43149               }
43150             }
43151             return dialCode;
43152         },
43153         
43154         reset : function()
43155         {
43156             this.setValue(this.defaultDialCode);
43157             this.validate();
43158         },
43159         
43160         hiddenEl : function()
43161         {
43162             return this.el.select('input.hidden-tel-input',true).first();
43163         },
43164         
43165         // after setting val
43166         onKeyUp : function(e){
43167             this.setValue(this.getValue());
43168         },
43169         
43170         onKeyPress : function(e){
43171             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43172                 e.stopEvent();
43173             }
43174         }
43175         
43176 });
43177 /**
43178  * @class Roo.bootstrap.MoneyField
43179  * @extends Roo.bootstrap.ComboBox
43180  * Bootstrap MoneyField class
43181  * 
43182  * @constructor
43183  * Create a new MoneyField.
43184  * @param {Object} config Configuration options
43185  */
43186
43187 Roo.bootstrap.MoneyField = function(config) {
43188     
43189     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43190     
43191 };
43192
43193 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43194     
43195     /**
43196      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43197      */
43198     allowDecimals : true,
43199     /**
43200      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43201      */
43202     decimalSeparator : ".",
43203     /**
43204      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43205      */
43206     decimalPrecision : 0,
43207     /**
43208      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43209      */
43210     allowNegative : true,
43211     /**
43212      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43213      */
43214     allowZero: true,
43215     /**
43216      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43217      */
43218     minValue : Number.NEGATIVE_INFINITY,
43219     /**
43220      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43221      */
43222     maxValue : Number.MAX_VALUE,
43223     /**
43224      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43225      */
43226     minText : "The minimum value for this field is {0}",
43227     /**
43228      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43229      */
43230     maxText : "The maximum value for this field is {0}",
43231     /**
43232      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43233      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43234      */
43235     nanText : "{0} is not a valid number",
43236     /**
43237      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43238      */
43239     castInt : true,
43240     /**
43241      * @cfg {String} defaults currency of the MoneyField
43242      * value should be in lkey
43243      */
43244     defaultCurrency : false,
43245     /**
43246      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43247      */
43248     thousandsDelimiter : false,
43249     /**
43250      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43251      */
43252     max_length: false,
43253     
43254     inputlg : 9,
43255     inputmd : 9,
43256     inputsm : 9,
43257     inputxs : 6,
43258     
43259     store : false,
43260     
43261     getAutoCreate : function()
43262     {
43263         var align = this.labelAlign || this.parentLabelAlign();
43264         
43265         var id = Roo.id();
43266
43267         var cfg = {
43268             cls: 'form-group',
43269             cn: []
43270         };
43271
43272         var input =  {
43273             tag: 'input',
43274             id : id,
43275             cls : 'form-control roo-money-amount-input',
43276             autocomplete: 'new-password'
43277         };
43278         
43279         var hiddenInput = {
43280             tag: 'input',
43281             type: 'hidden',
43282             id: Roo.id(),
43283             cls: 'hidden-number-input'
43284         };
43285         
43286         if(this.max_length) {
43287             input.maxlength = this.max_length; 
43288         }
43289         
43290         if (this.name) {
43291             hiddenInput.name = this.name;
43292         }
43293
43294         if (this.disabled) {
43295             input.disabled = true;
43296         }
43297
43298         var clg = 12 - this.inputlg;
43299         var cmd = 12 - this.inputmd;
43300         var csm = 12 - this.inputsm;
43301         var cxs = 12 - this.inputxs;
43302         
43303         var container = {
43304             tag : 'div',
43305             cls : 'row roo-money-field',
43306             cn : [
43307                 {
43308                     tag : 'div',
43309                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43310                     cn : [
43311                         {
43312                             tag : 'div',
43313                             cls: 'roo-select2-container input-group',
43314                             cn: [
43315                                 {
43316                                     tag : 'input',
43317                                     cls : 'form-control roo-money-currency-input',
43318                                     autocomplete: 'new-password',
43319                                     readOnly : 1,
43320                                     name : this.currencyName
43321                                 },
43322                                 {
43323                                     tag :'span',
43324                                     cls : 'input-group-addon',
43325                                     cn : [
43326                                         {
43327                                             tag: 'span',
43328                                             cls: 'caret'
43329                                         }
43330                                     ]
43331                                 }
43332                             ]
43333                         }
43334                     ]
43335                 },
43336                 {
43337                     tag : 'div',
43338                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43339                     cn : [
43340                         {
43341                             tag: 'div',
43342                             cls: this.hasFeedback ? 'has-feedback' : '',
43343                             cn: [
43344                                 input
43345                             ]
43346                         }
43347                     ]
43348                 }
43349             ]
43350             
43351         };
43352         
43353         if (this.fieldLabel.length) {
43354             var indicator = {
43355                 tag: 'i',
43356                 tooltip: 'This field is required'
43357             };
43358
43359             var label = {
43360                 tag: 'label',
43361                 'for':  id,
43362                 cls: 'control-label',
43363                 cn: []
43364             };
43365
43366             var label_text = {
43367                 tag: 'span',
43368                 html: this.fieldLabel
43369             };
43370
43371             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43372             label.cn = [
43373                 indicator,
43374                 label_text
43375             ];
43376
43377             if(this.indicatorpos == 'right') {
43378                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43379                 label.cn = [
43380                     label_text,
43381                     indicator
43382                 ];
43383             }
43384
43385             if(align == 'left') {
43386                 container = {
43387                     tag: 'div',
43388                     cn: [
43389                         container
43390                     ]
43391                 };
43392
43393                 if(this.labelWidth > 12){
43394                     label.style = "width: " + this.labelWidth + 'px';
43395                 }
43396                 if(this.labelWidth < 13 && this.labelmd == 0){
43397                     this.labelmd = this.labelWidth;
43398                 }
43399                 if(this.labellg > 0){
43400                     label.cls += ' col-lg-' + this.labellg;
43401                     input.cls += ' col-lg-' + (12 - this.labellg);
43402                 }
43403                 if(this.labelmd > 0){
43404                     label.cls += ' col-md-' + this.labelmd;
43405                     container.cls += ' col-md-' + (12 - this.labelmd);
43406                 }
43407                 if(this.labelsm > 0){
43408                     label.cls += ' col-sm-' + this.labelsm;
43409                     container.cls += ' col-sm-' + (12 - this.labelsm);
43410                 }
43411                 if(this.labelxs > 0){
43412                     label.cls += ' col-xs-' + this.labelxs;
43413                     container.cls += ' col-xs-' + (12 - this.labelxs);
43414                 }
43415             }
43416         }
43417
43418         cfg.cn = [
43419             label,
43420             container,
43421             hiddenInput
43422         ];
43423         
43424         var settings = this;
43425
43426         ['xs','sm','md','lg'].map(function(size){
43427             if (settings[size]) {
43428                 cfg.cls += ' col-' + size + '-' + settings[size];
43429             }
43430         });
43431         
43432         return cfg;
43433     },
43434     
43435     initEvents : function()
43436     {
43437         this.indicator = this.indicatorEl();
43438         
43439         this.initCurrencyEvent();
43440         
43441         this.initNumberEvent();
43442     },
43443     
43444     initCurrencyEvent : function()
43445     {
43446         if (!this.store) {
43447             throw "can not find store for combo";
43448         }
43449         
43450         this.store = Roo.factory(this.store, Roo.data);
43451         this.store.parent = this;
43452         
43453         this.createList();
43454         
43455         this.triggerEl = this.el.select('.input-group-addon', true).first();
43456         
43457         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43458         
43459         var _this = this;
43460         
43461         (function(){
43462             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43463             _this.list.setWidth(lw);
43464         }).defer(100);
43465         
43466         this.list.on('mouseover', this.onViewOver, this);
43467         this.list.on('mousemove', this.onViewMove, this);
43468         this.list.on('scroll', this.onViewScroll, this);
43469         
43470         if(!this.tpl){
43471             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43472         }
43473         
43474         this.view = new Roo.View(this.list, this.tpl, {
43475             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43476         });
43477         
43478         this.view.on('click', this.onViewClick, this);
43479         
43480         this.store.on('beforeload', this.onBeforeLoad, this);
43481         this.store.on('load', this.onLoad, this);
43482         this.store.on('loadexception', this.onLoadException, this);
43483         
43484         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43485             "up" : function(e){
43486                 this.inKeyMode = true;
43487                 this.selectPrev();
43488             },
43489
43490             "down" : function(e){
43491                 if(!this.isExpanded()){
43492                     this.onTriggerClick();
43493                 }else{
43494                     this.inKeyMode = true;
43495                     this.selectNext();
43496                 }
43497             },
43498
43499             "enter" : function(e){
43500                 this.collapse();
43501                 
43502                 if(this.fireEvent("specialkey", this, e)){
43503                     this.onViewClick(false);
43504                 }
43505                 
43506                 return true;
43507             },
43508
43509             "esc" : function(e){
43510                 this.collapse();
43511             },
43512
43513             "tab" : function(e){
43514                 this.collapse();
43515                 
43516                 if(this.fireEvent("specialkey", this, e)){
43517                     this.onViewClick(false);
43518                 }
43519                 
43520                 return true;
43521             },
43522
43523             scope : this,
43524
43525             doRelay : function(foo, bar, hname){
43526                 if(hname == 'down' || this.scope.isExpanded()){
43527                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43528                 }
43529                 return true;
43530             },
43531
43532             forceKeyDown: true
43533         });
43534         
43535         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43536         
43537     },
43538     
43539     initNumberEvent : function(e)
43540     {
43541         this.inputEl().on("keydown" , this.fireKey,  this);
43542         this.inputEl().on("focus", this.onFocus,  this);
43543         this.inputEl().on("blur", this.onBlur,  this);
43544         
43545         this.inputEl().relayEvent('keyup', this);
43546         
43547         if(this.indicator){
43548             this.indicator.addClass('invisible');
43549         }
43550  
43551         this.originalValue = this.getValue();
43552         
43553         if(this.validationEvent == 'keyup'){
43554             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43555             this.inputEl().on('keyup', this.filterValidation, this);
43556         }
43557         else if(this.validationEvent !== false){
43558             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43559         }
43560         
43561         if(this.selectOnFocus){
43562             this.on("focus", this.preFocus, this);
43563             
43564         }
43565         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43566             this.inputEl().on("keypress", this.filterKeys, this);
43567         } else {
43568             this.inputEl().relayEvent('keypress', this);
43569         }
43570         
43571         var allowed = "0123456789";
43572         
43573         if(this.allowDecimals){
43574             allowed += this.decimalSeparator;
43575         }
43576         
43577         if(this.allowNegative){
43578             allowed += "-";
43579         }
43580         
43581         if(this.thousandsDelimiter) {
43582             allowed += ",";
43583         }
43584         
43585         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43586         
43587         var keyPress = function(e){
43588             
43589             var k = e.getKey();
43590             
43591             var c = e.getCharCode();
43592             
43593             if(
43594                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43595                     allowed.indexOf(String.fromCharCode(c)) === -1
43596             ){
43597                 e.stopEvent();
43598                 return;
43599             }
43600             
43601             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43602                 return;
43603             }
43604             
43605             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43606                 e.stopEvent();
43607             }
43608         };
43609         
43610         this.inputEl().on("keypress", keyPress, this);
43611         
43612     },
43613     
43614     onTriggerClick : function(e)
43615     {   
43616         if(this.disabled){
43617             return;
43618         }
43619         
43620         this.page = 0;
43621         this.loadNext = false;
43622         
43623         if(this.isExpanded()){
43624             this.collapse();
43625             return;
43626         }
43627         
43628         this.hasFocus = true;
43629         
43630         if(this.triggerAction == 'all') {
43631             this.doQuery(this.allQuery, true);
43632             return;
43633         }
43634         
43635         this.doQuery(this.getRawValue());
43636     },
43637     
43638     getCurrency : function()
43639     {   
43640         var v = this.currencyEl().getValue();
43641         
43642         return v;
43643     },
43644     
43645     restrictHeight : function()
43646     {
43647         this.list.alignTo(this.currencyEl(), this.listAlign);
43648         this.list.alignTo(this.currencyEl(), this.listAlign);
43649     },
43650     
43651     onViewClick : function(view, doFocus, el, e)
43652     {
43653         var index = this.view.getSelectedIndexes()[0];
43654         
43655         var r = this.store.getAt(index);
43656         
43657         if(r){
43658             this.onSelect(r, index);
43659         }
43660     },
43661     
43662     onSelect : function(record, index){
43663         
43664         if(this.fireEvent('beforeselect', this, record, index) !== false){
43665         
43666             this.setFromCurrencyData(index > -1 ? record.data : false);
43667             
43668             this.collapse();
43669             
43670             this.fireEvent('select', this, record, index);
43671         }
43672     },
43673     
43674     setFromCurrencyData : function(o)
43675     {
43676         var currency = '';
43677         
43678         this.lastCurrency = o;
43679         
43680         if (this.currencyField) {
43681             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43682         } else {
43683             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43684         }
43685         
43686         this.lastSelectionText = currency;
43687         
43688         //setting default currency
43689         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43690             this.setCurrency(this.defaultCurrency);
43691             return;
43692         }
43693         
43694         this.setCurrency(currency);
43695     },
43696     
43697     setFromData : function(o)
43698     {
43699         var c = {};
43700         
43701         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43702         
43703         this.setFromCurrencyData(c);
43704         
43705         var value = '';
43706         
43707         if (this.name) {
43708             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43709         } else {
43710             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43711         }
43712         
43713         this.setValue(value);
43714         
43715     },
43716     
43717     setCurrency : function(v)
43718     {   
43719         this.currencyValue = v;
43720         
43721         if(this.rendered){
43722             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43723             this.validate();
43724         }
43725     },
43726     
43727     setValue : function(v)
43728     {
43729         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43730         
43731         this.value = v;
43732         
43733         if(this.rendered){
43734             
43735             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43736             
43737             this.inputEl().dom.value = (v == '') ? '' :
43738                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43739             
43740             if(!this.allowZero && v === '0') {
43741                 this.hiddenEl().dom.value = '';
43742                 this.inputEl().dom.value = '';
43743             }
43744             
43745             this.validate();
43746         }
43747     },
43748     
43749     getRawValue : function()
43750     {
43751         var v = this.inputEl().getValue();
43752         
43753         return v;
43754     },
43755     
43756     getValue : function()
43757     {
43758         return this.fixPrecision(this.parseValue(this.getRawValue()));
43759     },
43760     
43761     parseValue : function(value)
43762     {
43763         if(this.thousandsDelimiter) {
43764             value += "";
43765             r = new RegExp(",", "g");
43766             value = value.replace(r, "");
43767         }
43768         
43769         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43770         return isNaN(value) ? '' : value;
43771         
43772     },
43773     
43774     fixPrecision : function(value)
43775     {
43776         if(this.thousandsDelimiter) {
43777             value += "";
43778             r = new RegExp(",", "g");
43779             value = value.replace(r, "");
43780         }
43781         
43782         var nan = isNaN(value);
43783         
43784         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43785             return nan ? '' : value;
43786         }
43787         return parseFloat(value).toFixed(this.decimalPrecision);
43788     },
43789     
43790     decimalPrecisionFcn : function(v)
43791     {
43792         return Math.floor(v);
43793     },
43794     
43795     validateValue : function(value)
43796     {
43797         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43798             return false;
43799         }
43800         
43801         var num = this.parseValue(value);
43802         
43803         if(isNaN(num)){
43804             this.markInvalid(String.format(this.nanText, value));
43805             return false;
43806         }
43807         
43808         if(num < this.minValue){
43809             this.markInvalid(String.format(this.minText, this.minValue));
43810             return false;
43811         }
43812         
43813         if(num > this.maxValue){
43814             this.markInvalid(String.format(this.maxText, this.maxValue));
43815             return false;
43816         }
43817         
43818         return true;
43819     },
43820     
43821     validate : function()
43822     {
43823         if(this.disabled || this.allowBlank){
43824             this.markValid();
43825             return true;
43826         }
43827         
43828         var currency = this.getCurrency();
43829         
43830         if(this.validateValue(this.getRawValue()) && currency.length){
43831             this.markValid();
43832             return true;
43833         }
43834         
43835         this.markInvalid();
43836         return false;
43837     },
43838     
43839     getName: function()
43840     {
43841         return this.name;
43842     },
43843     
43844     beforeBlur : function()
43845     {
43846         if(!this.castInt){
43847             return;
43848         }
43849         
43850         var v = this.parseValue(this.getRawValue());
43851         
43852         if(v || v == 0){
43853             this.setValue(v);
43854         }
43855     },
43856     
43857     onBlur : function()
43858     {
43859         this.beforeBlur();
43860         
43861         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43862             //this.el.removeClass(this.focusClass);
43863         }
43864         
43865         this.hasFocus = false;
43866         
43867         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43868             this.validate();
43869         }
43870         
43871         var v = this.getValue();
43872         
43873         if(String(v) !== String(this.startValue)){
43874             this.fireEvent('change', this, v, this.startValue);
43875         }
43876         
43877         this.fireEvent("blur", this);
43878     },
43879     
43880     inputEl : function()
43881     {
43882         return this.el.select('.roo-money-amount-input', true).first();
43883     },
43884     
43885     currencyEl : function()
43886     {
43887         return this.el.select('.roo-money-currency-input', true).first();
43888     },
43889     
43890     hiddenEl : function()
43891     {
43892         return this.el.select('input.hidden-number-input',true).first();
43893     }
43894     
43895 });/**
43896  * @class Roo.bootstrap.BezierSignature
43897  * @extends Roo.bootstrap.Component
43898  * Bootstrap BezierSignature class
43899  * This script refer to:
43900  *    Title: Signature Pad
43901  *    Author: szimek
43902  *    Availability: https://github.com/szimek/signature_pad
43903  *
43904  * @constructor
43905  * Create a new BezierSignature
43906  * @param {Object} config The config object
43907  */
43908
43909 Roo.bootstrap.BezierSignature = function(config){
43910     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43911     this.addEvents({
43912         "resize" : true
43913     });
43914 };
43915
43916 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43917 {
43918      
43919     curve_data: [],
43920     
43921     is_empty: true,
43922     
43923     mouse_btn_down: true,
43924     
43925     /**
43926      * @cfg {int} canvas height
43927      */
43928     canvas_height: '200px',
43929     
43930     /**
43931      * @cfg {float|function} Radius of a single dot.
43932      */ 
43933     dot_size: false,
43934     
43935     /**
43936      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43937      */
43938     min_width: 0.5,
43939     
43940     /**
43941      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43942      */
43943     max_width: 2.5,
43944     
43945     /**
43946      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43947      */
43948     throttle: 16,
43949     
43950     /**
43951      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43952      */
43953     min_distance: 5,
43954     
43955     /**
43956      * @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.
43957      */
43958     bg_color: 'rgba(0, 0, 0, 0)',
43959     
43960     /**
43961      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43962      */
43963     dot_color: 'black',
43964     
43965     /**
43966      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43967      */ 
43968     velocity_filter_weight: 0.7,
43969     
43970     /**
43971      * @cfg {function} Callback when stroke begin. 
43972      */
43973     onBegin: false,
43974     
43975     /**
43976      * @cfg {function} Callback when stroke end.
43977      */
43978     onEnd: false,
43979     
43980     getAutoCreate : function()
43981     {
43982         var cls = 'roo-signature column';
43983         
43984         if(this.cls){
43985             cls += ' ' + this.cls;
43986         }
43987         
43988         var col_sizes = [
43989             'lg',
43990             'md',
43991             'sm',
43992             'xs'
43993         ];
43994         
43995         for(var i = 0; i < col_sizes.length; i++) {
43996             if(this[col_sizes[i]]) {
43997                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43998             }
43999         }
44000         
44001         var cfg = {
44002             tag: 'div',
44003             cls: cls,
44004             cn: [
44005                 {
44006                     tag: 'div',
44007                     cls: 'roo-signature-body',
44008                     cn: [
44009                         {
44010                             tag: 'canvas',
44011                             cls: 'roo-signature-body-canvas',
44012                             height: this.canvas_height,
44013                             width: this.canvas_width
44014                         }
44015                     ]
44016                 },
44017                 {
44018                     tag: 'input',
44019                     type: 'file',
44020                     style: 'display: none'
44021                 }
44022             ]
44023         };
44024         
44025         return cfg;
44026     },
44027     
44028     initEvents: function() 
44029     {
44030         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44031         
44032         var canvas = this.canvasEl();
44033         
44034         // mouse && touch event swapping...
44035         canvas.dom.style.touchAction = 'none';
44036         canvas.dom.style.msTouchAction = 'none';
44037         
44038         this.mouse_btn_down = false;
44039         canvas.on('mousedown', this._handleMouseDown, this);
44040         canvas.on('mousemove', this._handleMouseMove, this);
44041         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44042         
44043         if (window.PointerEvent) {
44044             canvas.on('pointerdown', this._handleMouseDown, this);
44045             canvas.on('pointermove', this._handleMouseMove, this);
44046             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44047         }
44048         
44049         if ('ontouchstart' in window) {
44050             canvas.on('touchstart', this._handleTouchStart, this);
44051             canvas.on('touchmove', this._handleTouchMove, this);
44052             canvas.on('touchend', this._handleTouchEnd, this);
44053         }
44054         
44055         Roo.EventManager.onWindowResize(this.resize, this, true);
44056         
44057         // file input event
44058         this.fileEl().on('change', this.uploadImage, this);
44059         
44060         this.clear();
44061         
44062         this.resize();
44063     },
44064     
44065     resize: function(){
44066         
44067         var canvas = this.canvasEl().dom;
44068         var ctx = this.canvasElCtx();
44069         var img_data = false;
44070         
44071         if(canvas.width > 0) {
44072             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44073         }
44074         // setting canvas width will clean img data
44075         canvas.width = 0;
44076         
44077         var style = window.getComputedStyle ? 
44078             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44079             
44080         var padding_left = parseInt(style.paddingLeft) || 0;
44081         var padding_right = parseInt(style.paddingRight) || 0;
44082         
44083         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44084         
44085         if(img_data) {
44086             ctx.putImageData(img_data, 0, 0);
44087         }
44088     },
44089     
44090     _handleMouseDown: function(e)
44091     {
44092         if (e.browserEvent.which === 1) {
44093             this.mouse_btn_down = true;
44094             this.strokeBegin(e);
44095         }
44096     },
44097     
44098     _handleMouseMove: function (e)
44099     {
44100         if (this.mouse_btn_down) {
44101             this.strokeMoveUpdate(e);
44102         }
44103     },
44104     
44105     _handleMouseUp: function (e)
44106     {
44107         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44108             this.mouse_btn_down = false;
44109             this.strokeEnd(e);
44110         }
44111     },
44112     
44113     _handleTouchStart: function (e) {
44114         
44115         e.preventDefault();
44116         if (e.browserEvent.targetTouches.length === 1) {
44117             // var touch = e.browserEvent.changedTouches[0];
44118             // this.strokeBegin(touch);
44119             
44120              this.strokeBegin(e); // assume e catching the correct xy...
44121         }
44122     },
44123     
44124     _handleTouchMove: function (e) {
44125         e.preventDefault();
44126         // var touch = event.targetTouches[0];
44127         // _this._strokeMoveUpdate(touch);
44128         this.strokeMoveUpdate(e);
44129     },
44130     
44131     _handleTouchEnd: function (e) {
44132         var wasCanvasTouched = e.target === this.canvasEl().dom;
44133         if (wasCanvasTouched) {
44134             e.preventDefault();
44135             // var touch = event.changedTouches[0];
44136             // _this._strokeEnd(touch);
44137             this.strokeEnd(e);
44138         }
44139     },
44140     
44141     reset: function () {
44142         this._lastPoints = [];
44143         this._lastVelocity = 0;
44144         this._lastWidth = (this.min_width + this.max_width) / 2;
44145         this.canvasElCtx().fillStyle = this.dot_color;
44146     },
44147     
44148     strokeMoveUpdate: function(e)
44149     {
44150         this.strokeUpdate(e);
44151         
44152         if (this.throttle) {
44153             this.throttleStroke(this.strokeUpdate, this.throttle);
44154         }
44155         else {
44156             this.strokeUpdate(e);
44157         }
44158     },
44159     
44160     strokeBegin: function(e)
44161     {
44162         var newPointGroup = {
44163             color: this.dot_color,
44164             points: []
44165         };
44166         
44167         if (typeof this.onBegin === 'function') {
44168             this.onBegin(e);
44169         }
44170         
44171         this.curve_data.push(newPointGroup);
44172         this.reset();
44173         this.strokeUpdate(e);
44174     },
44175     
44176     strokeUpdate: function(e)
44177     {
44178         var rect = this.canvasEl().dom.getBoundingClientRect();
44179         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44180         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44181         var lastPoints = lastPointGroup.points;
44182         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44183         var isLastPointTooClose = lastPoint
44184             ? point.distanceTo(lastPoint) <= this.min_distance
44185             : false;
44186         var color = lastPointGroup.color;
44187         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44188             var curve = this.addPoint(point);
44189             if (!lastPoint) {
44190                 this.drawDot({color: color, point: point});
44191             }
44192             else if (curve) {
44193                 this.drawCurve({color: color, curve: curve});
44194             }
44195             lastPoints.push({
44196                 time: point.time,
44197                 x: point.x,
44198                 y: point.y
44199             });
44200         }
44201     },
44202     
44203     strokeEnd: function(e)
44204     {
44205         this.strokeUpdate(e);
44206         if (typeof this.onEnd === 'function') {
44207             this.onEnd(e);
44208         }
44209     },
44210     
44211     addPoint:  function (point) {
44212         var _lastPoints = this._lastPoints;
44213         _lastPoints.push(point);
44214         if (_lastPoints.length > 2) {
44215             if (_lastPoints.length === 3) {
44216                 _lastPoints.unshift(_lastPoints[0]);
44217             }
44218             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44219             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44220             _lastPoints.shift();
44221             return curve;
44222         }
44223         return null;
44224     },
44225     
44226     calculateCurveWidths: function (startPoint, endPoint) {
44227         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44228             (1 - this.velocity_filter_weight) * this._lastVelocity;
44229
44230         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44231         var widths = {
44232             end: newWidth,
44233             start: this._lastWidth
44234         };
44235         
44236         this._lastVelocity = velocity;
44237         this._lastWidth = newWidth;
44238         return widths;
44239     },
44240     
44241     drawDot: function (_a) {
44242         var color = _a.color, point = _a.point;
44243         var ctx = this.canvasElCtx();
44244         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44245         ctx.beginPath();
44246         this.drawCurveSegment(point.x, point.y, width);
44247         ctx.closePath();
44248         ctx.fillStyle = color;
44249         ctx.fill();
44250     },
44251     
44252     drawCurve: function (_a) {
44253         var color = _a.color, curve = _a.curve;
44254         var ctx = this.canvasElCtx();
44255         var widthDelta = curve.endWidth - curve.startWidth;
44256         var drawSteps = Math.floor(curve.length()) * 2;
44257         ctx.beginPath();
44258         ctx.fillStyle = color;
44259         for (var i = 0; i < drawSteps; i += 1) {
44260         var t = i / drawSteps;
44261         var tt = t * t;
44262         var ttt = tt * t;
44263         var u = 1 - t;
44264         var uu = u * u;
44265         var uuu = uu * u;
44266         var x = uuu * curve.startPoint.x;
44267         x += 3 * uu * t * curve.control1.x;
44268         x += 3 * u * tt * curve.control2.x;
44269         x += ttt * curve.endPoint.x;
44270         var y = uuu * curve.startPoint.y;
44271         y += 3 * uu * t * curve.control1.y;
44272         y += 3 * u * tt * curve.control2.y;
44273         y += ttt * curve.endPoint.y;
44274         var width = curve.startWidth + ttt * widthDelta;
44275         this.drawCurveSegment(x, y, width);
44276         }
44277         ctx.closePath();
44278         ctx.fill();
44279     },
44280     
44281     drawCurveSegment: function (x, y, width) {
44282         var ctx = this.canvasElCtx();
44283         ctx.moveTo(x, y);
44284         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44285         this.is_empty = false;
44286     },
44287     
44288     clear: function()
44289     {
44290         var ctx = this.canvasElCtx();
44291         var canvas = this.canvasEl().dom;
44292         ctx.fillStyle = this.bg_color;
44293         ctx.clearRect(0, 0, canvas.width, canvas.height);
44294         ctx.fillRect(0, 0, canvas.width, canvas.height);
44295         this.curve_data = [];
44296         this.reset();
44297         this.is_empty = true;
44298     },
44299     
44300     fileEl: function()
44301     {
44302         return  this.el.select('input',true).first();
44303     },
44304     
44305     canvasEl: function()
44306     {
44307         return this.el.select('canvas',true).first();
44308     },
44309     
44310     canvasElCtx: function()
44311     {
44312         return this.el.select('canvas',true).first().dom.getContext('2d');
44313     },
44314     
44315     getImage: function(type)
44316     {
44317         if(this.is_empty) {
44318             return false;
44319         }
44320         
44321         // encryption ?
44322         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44323     },
44324     
44325     drawFromImage: function(img_src)
44326     {
44327         var img = new Image();
44328         
44329         img.onload = function(){
44330             this.canvasElCtx().drawImage(img, 0, 0);
44331         }.bind(this);
44332         
44333         img.src = img_src;
44334         
44335         this.is_empty = false;
44336     },
44337     
44338     selectImage: function()
44339     {
44340         this.fileEl().dom.click();
44341     },
44342     
44343     uploadImage: function(e)
44344     {
44345         var reader = new FileReader();
44346         
44347         reader.onload = function(e){
44348             var img = new Image();
44349             img.onload = function(){
44350                 this.reset();
44351                 this.canvasElCtx().drawImage(img, 0, 0);
44352             }.bind(this);
44353             img.src = e.target.result;
44354         }.bind(this);
44355         
44356         reader.readAsDataURL(e.target.files[0]);
44357     },
44358     
44359     // Bezier Point Constructor
44360     Point: (function () {
44361         function Point(x, y, time) {
44362             this.x = x;
44363             this.y = y;
44364             this.time = time || Date.now();
44365         }
44366         Point.prototype.distanceTo = function (start) {
44367             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44368         };
44369         Point.prototype.equals = function (other) {
44370             return this.x === other.x && this.y === other.y && this.time === other.time;
44371         };
44372         Point.prototype.velocityFrom = function (start) {
44373             return this.time !== start.time
44374             ? this.distanceTo(start) / (this.time - start.time)
44375             : 0;
44376         };
44377         return Point;
44378     }()),
44379     
44380     
44381     // Bezier Constructor
44382     Bezier: (function () {
44383         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44384             this.startPoint = startPoint;
44385             this.control2 = control2;
44386             this.control1 = control1;
44387             this.endPoint = endPoint;
44388             this.startWidth = startWidth;
44389             this.endWidth = endWidth;
44390         }
44391         Bezier.fromPoints = function (points, widths, scope) {
44392             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44393             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44394             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44395         };
44396         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44397             var dx1 = s1.x - s2.x;
44398             var dy1 = s1.y - s2.y;
44399             var dx2 = s2.x - s3.x;
44400             var dy2 = s2.y - s3.y;
44401             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44402             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44403             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44404             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44405             var dxm = m1.x - m2.x;
44406             var dym = m1.y - m2.y;
44407             var k = l2 / (l1 + l2);
44408             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44409             var tx = s2.x - cm.x;
44410             var ty = s2.y - cm.y;
44411             return {
44412                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44413                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44414             };
44415         };
44416         Bezier.prototype.length = function () {
44417             var steps = 10;
44418             var length = 0;
44419             var px;
44420             var py;
44421             for (var i = 0; i <= steps; i += 1) {
44422                 var t = i / steps;
44423                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44424                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44425                 if (i > 0) {
44426                     var xdiff = cx - px;
44427                     var ydiff = cy - py;
44428                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44429                 }
44430                 px = cx;
44431                 py = cy;
44432             }
44433             return length;
44434         };
44435         Bezier.prototype.point = function (t, start, c1, c2, end) {
44436             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44437             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44438             + (3.0 * c2 * (1.0 - t) * t * t)
44439             + (end * t * t * t);
44440         };
44441         return Bezier;
44442     }()),
44443     
44444     throttleStroke: function(fn, wait) {
44445       if (wait === void 0) { wait = 250; }
44446       var previous = 0;
44447       var timeout = null;
44448       var result;
44449       var storedContext;
44450       var storedArgs;
44451       var later = function () {
44452           previous = Date.now();
44453           timeout = null;
44454           result = fn.apply(storedContext, storedArgs);
44455           if (!timeout) {
44456               storedContext = null;
44457               storedArgs = [];
44458           }
44459       };
44460       return function wrapper() {
44461           var args = [];
44462           for (var _i = 0; _i < arguments.length; _i++) {
44463               args[_i] = arguments[_i];
44464           }
44465           var now = Date.now();
44466           var remaining = wait - (now - previous);
44467           storedContext = this;
44468           storedArgs = args;
44469           if (remaining <= 0 || remaining > wait) {
44470               if (timeout) {
44471                   clearTimeout(timeout);
44472                   timeout = null;
44473               }
44474               previous = now;
44475               result = fn.apply(storedContext, storedArgs);
44476               if (!timeout) {
44477                   storedContext = null;
44478                   storedArgs = [];
44479               }
44480           }
44481           else if (!timeout) {
44482               timeout = window.setTimeout(later, remaining);
44483           }
44484           return result;
44485       };
44486   }
44487   
44488 });
44489
44490  
44491
44492