ab51f93701114303b1f7418ae954f3bcd9b3111e
[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['margin' + (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         
2523         var dom = move_card.el.dom;
2524         dom.parentNode.removeChild(dom);
2525         dom.style.width = ''; // clear with - which is set by drag.
2526         
2527         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528             var cardel = next_to_card.el.dom;
2529             
2530             if (position == 'above' ) {
2531                 cardel.parentNode.insertBefore(dom, cardel);
2532             } else if (cardel.nextSibling) {
2533                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534             } else {
2535                 cardel.parentNode.append(dom);
2536             }
2537         } else {
2538             // card container???
2539             this.containerEl.dom.append(dom);
2540         }
2541         
2542         //FIXME HANDLE card = true 
2543         
2544         // add this to the correct place in items.
2545         
2546         
2547         
2548         // remove Card from items.
2549         
2550         var old_parent = move_card.parent();
2551         
2552         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553         
2554         if (this.items.length) {
2555             var nitems = [];
2556             //Roo.log([info.items_n, info.position, this.items.length]);
2557             for (var i =0; i < this.items.length; i++) {
2558                 if (i == to_items_n && position == 'above') {
2559                     nitems.push(move_card);
2560                 }
2561                 nitems.push(this.items[i]);
2562                 if (i == to_items_n && position == 'below') {
2563                     nitems.push(move_card);
2564                 }
2565             }
2566             this.items = nitems;
2567             Roo.log(this.items);
2568         } else {
2569             this.items.push(move_card);
2570         }
2571         
2572         move_card.parentId = this.id;
2573         
2574         return true;
2575         
2576         
2577     },
2578     
2579     
2580     /**    Decide whether to drop above or below a View node. */
2581     getDropPoint : function(e, n, dd)
2582     {
2583         if (dd) {
2584              return false;
2585         }
2586         if (n == this.containerEl.dom) {
2587             return "above";
2588         }
2589         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590         var c = t + (b - t) / 2;
2591         var y = Roo.lib.Event.getPageY(e);
2592         if(y <= c) {
2593             return "above";
2594         }else{
2595             return "below";
2596         }
2597     },
2598     onToggleCollapse : function(e)
2599         {
2600         if (this.collapsed) {
2601             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602             this.collapsableEl.addClass('show');
2603             this.collapsed = false;
2604             return;
2605         }
2606         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607         this.collapsableEl.removeClass('show');
2608         this.collapsed = true;
2609         
2610     
2611     },
2612     
2613     onToggleRotate : function(e)
2614     {
2615         this.collapsableEl.removeClass('show');
2616         this.footerEl.removeClass('d-none');
2617         this.el.removeClass('roo-card-rotated');
2618         this.el.removeClass('d-none');
2619         if (this.rotated) {
2620             
2621             this.collapsableEl.addClass('show');
2622             this.rotated = false;
2623             this.fireEvent('rotate', this, this.rotated);
2624             return;
2625         }
2626         this.el.addClass('roo-card-rotated');
2627         this.footerEl.addClass('d-none');
2628         this.el.select('.roo-collapsable').removeClass('show');
2629         
2630         this.rotated = true;
2631         this.fireEvent('rotate', this, this.rotated);
2632     
2633     },
2634     
2635     dropPlaceHolder: function (action, info, data)
2636     {
2637         if (this.dropEl === false) {
2638             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2639             cls : 'd-none'
2640             },true);
2641         }
2642         this.dropEl.removeClass(['d-none', 'd-block']);        
2643         if (action == 'hide') {
2644             
2645             this.dropEl.addClass('d-none');
2646             return;
2647         }
2648         // FIXME - info.card == true!!!
2649         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650         
2651         if (info.card !== true) {
2652             var cardel = info.card.el.dom;
2653             
2654             if (info.position == 'above') {
2655                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656             } else if (cardel.nextSibling) {
2657                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658             } else {
2659                 cardel.parentNode.append(this.dropEl.dom);
2660             }
2661         } else {
2662             // card container???
2663             this.containerEl.dom.append(this.dropEl.dom);
2664         }
2665         
2666         this.dropEl.addClass('d-block roo-card-dropzone');
2667         
2668         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2669         
2670         
2671     
2672     
2673     
2674     },
2675     setHeaderText: function(html)
2676     {
2677         this.headerContainerEl.dom.innerHTML = html;
2678     }
2679
2680     
2681 });
2682
2683 /*
2684  * - LGPL
2685  *
2686  * Card header - holder for the card header elements.
2687  * 
2688  */
2689
2690 /**
2691  * @class Roo.bootstrap.CardHeader
2692  * @extends Roo.bootstrap.Element
2693  * Bootstrap CardHeader class
2694  * @constructor
2695  * Create a new Card Header - that you can embed children into
2696  * @param {Object} config The config object
2697  */
2698
2699 Roo.bootstrap.CardHeader = function(config){
2700     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2701 };
2702
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2704     
2705     
2706     container_method : 'getCardHeader' 
2707     
2708      
2709     
2710     
2711    
2712 });
2713
2714  
2715
2716  /*
2717  * - LGPL
2718  *
2719  * Card footer - holder for the card footer elements.
2720  * 
2721  */
2722
2723 /**
2724  * @class Roo.bootstrap.CardFooter
2725  * @extends Roo.bootstrap.Element
2726  * Bootstrap CardFooter class
2727  * @constructor
2728  * Create a new Card Footer - that you can embed children into
2729  * @param {Object} config The config object
2730  */
2731
2732 Roo.bootstrap.CardFooter = function(config){
2733     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2734 };
2735
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2737     
2738     
2739     container_method : 'getCardFooter' 
2740     
2741      
2742     
2743     
2744    
2745 });
2746
2747  
2748
2749  /*
2750  * - LGPL
2751  *
2752  * Card header - holder for the card header elements.
2753  * 
2754  */
2755
2756 /**
2757  * @class Roo.bootstrap.CardImageTop
2758  * @extends Roo.bootstrap.Element
2759  * Bootstrap CardImageTop class
2760  * @constructor
2761  * Create a new Card Image Top container
2762  * @param {Object} config The config object
2763  */
2764
2765 Roo.bootstrap.CardImageTop = function(config){
2766     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2767 };
2768
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2770     
2771    
2772     container_method : 'getCardImageTop' 
2773     
2774      
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * image
2785  * 
2786  */
2787
2788
2789 /**
2790  * @class Roo.bootstrap.Img
2791  * @extends Roo.bootstrap.Component
2792  * Bootstrap Img class
2793  * @cfg {Boolean} imgResponsive false | true
2794  * @cfg {String} border rounded | circle | thumbnail
2795  * @cfg {String} src image source
2796  * @cfg {String} alt image alternative text
2797  * @cfg {String} href a tag href
2798  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799  * @cfg {String} xsUrl xs image source
2800  * @cfg {String} smUrl sm image source
2801  * @cfg {String} mdUrl md image source
2802  * @cfg {String} lgUrl lg image source
2803  * 
2804  * @constructor
2805  * Create a new Input
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.Img = function(config){
2810     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2811     
2812     this.addEvents({
2813         // img events
2814         /**
2815          * @event click
2816          * The img click event for the img.
2817          * @param {Roo.EventObject} e
2818          */
2819         "click" : true
2820     });
2821 };
2822
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2824     
2825     imgResponsive: true,
2826     border: '',
2827     src: 'about:blank',
2828     href: false,
2829     target: false,
2830     xsUrl: '',
2831     smUrl: '',
2832     mdUrl: '',
2833     lgUrl: '',
2834
2835     getAutoCreate : function()
2836     {   
2837         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838             return this.createSingleImg();
2839         }
2840         
2841         var cfg = {
2842             tag: 'div',
2843             cls: 'roo-image-responsive-group',
2844             cn: []
2845         };
2846         var _this = this;
2847         
2848         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849             
2850             if(!_this[size + 'Url']){
2851                 return;
2852             }
2853             
2854             var img = {
2855                 tag: 'img',
2856                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857                 html: _this.html || cfg.html,
2858                 src: _this[size + 'Url']
2859             };
2860             
2861             img.cls += ' roo-image-responsive-' + size;
2862             
2863             var s = ['xs', 'sm', 'md', 'lg'];
2864             
2865             s.splice(s.indexOf(size), 1);
2866             
2867             Roo.each(s, function(ss){
2868                 img.cls += ' hidden-' + ss;
2869             });
2870             
2871             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872                 cfg.cls += ' img-' + _this.border;
2873             }
2874             
2875             if(_this.alt){
2876                 cfg.alt = _this.alt;
2877             }
2878             
2879             if(_this.href){
2880                 var a = {
2881                     tag: 'a',
2882                     href: _this.href,
2883                     cn: [
2884                         img
2885                     ]
2886                 };
2887
2888                 if(this.target){
2889                     a.target = _this.target;
2890                 }
2891             }
2892             
2893             cfg.cn.push((_this.href) ? a : img);
2894             
2895         });
2896         
2897         return cfg;
2898     },
2899     
2900     createSingleImg : function()
2901     {
2902         var cfg = {
2903             tag: 'img',
2904             cls: (this.imgResponsive) ? 'img-responsive' : '',
2905             html : null,
2906             src : 'about:blank'  // just incase src get's set to undefined?!?
2907         };
2908         
2909         cfg.html = this.html || cfg.html;
2910         
2911         cfg.src = this.src || cfg.src;
2912         
2913         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914             cfg.cls += ' img-' + this.border;
2915         }
2916         
2917         if(this.alt){
2918             cfg.alt = this.alt;
2919         }
2920         
2921         if(this.href){
2922             var a = {
2923                 tag: 'a',
2924                 href: this.href,
2925                 cn: [
2926                     cfg
2927                 ]
2928             };
2929             
2930             if(this.target){
2931                 a.target = this.target;
2932             }
2933             
2934         }
2935         
2936         return (this.href) ? a : cfg;
2937     },
2938     
2939     initEvents: function() 
2940     {
2941         if(!this.href){
2942             this.el.on('click', this.onClick, this);
2943         }
2944         
2945     },
2946     
2947     onClick : function(e)
2948     {
2949         Roo.log('img onclick');
2950         this.fireEvent('click', this, e);
2951     },
2952     /**
2953      * Sets the url of the image - used to update it
2954      * @param {String} url the url of the image
2955      */
2956     
2957     setSrc : function(url)
2958     {
2959         this.src =  url;
2960         
2961         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962             this.el.dom.src =  url;
2963             return;
2964         }
2965         
2966         this.el.select('img', true).first().dom.src =  url;
2967     }
2968     
2969     
2970    
2971 });
2972
2973  /*
2974  * - LGPL
2975  *
2976  * image
2977  * 
2978  */
2979
2980
2981 /**
2982  * @class Roo.bootstrap.Link
2983  * @extends Roo.bootstrap.Component
2984  * Bootstrap Link Class
2985  * @cfg {String} alt image alternative text
2986  * @cfg {String} href a tag href
2987  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988  * @cfg {String} html the content of the link.
2989  * @cfg {String} anchor name for the anchor link
2990  * @cfg {String} fa - favicon
2991
2992  * @cfg {Boolean} preventDefault (true | false) default false
2993
2994  * 
2995  * @constructor
2996  * Create a new Input
2997  * @param {Object} config The config object
2998  */
2999
3000 Roo.bootstrap.Link = function(config){
3001     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3002     
3003     this.addEvents({
3004         // img events
3005         /**
3006          * @event click
3007          * The img click event for the img.
3008          * @param {Roo.EventObject} e
3009          */
3010         "click" : true
3011     });
3012 };
3013
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3015     
3016     href: false,
3017     target: false,
3018     preventDefault: false,
3019     anchor : false,
3020     alt : false,
3021     fa: false,
3022
3023
3024     getAutoCreate : function()
3025     {
3026         var html = this.html || '';
3027         
3028         if (this.fa !== false) {
3029             html = '<i class="fa fa-' + this.fa + '"></i>';
3030         }
3031         var cfg = {
3032             tag: 'a'
3033         };
3034         // anchor's do not require html/href...
3035         if (this.anchor === false) {
3036             cfg.html = html;
3037             cfg.href = this.href || '#';
3038         } else {
3039             cfg.name = this.anchor;
3040             if (this.html !== false || this.fa !== false) {
3041                 cfg.html = html;
3042             }
3043             if (this.href !== false) {
3044                 cfg.href = this.href;
3045             }
3046         }
3047         
3048         if(this.alt !== false){
3049             cfg.alt = this.alt;
3050         }
3051         
3052         
3053         if(this.target !== false) {
3054             cfg.target = this.target;
3055         }
3056         
3057         return cfg;
3058     },
3059     
3060     initEvents: function() {
3061         
3062         if(!this.href || this.preventDefault){
3063             this.el.on('click', this.onClick, this);
3064         }
3065     },
3066     
3067     onClick : function(e)
3068     {
3069         if(this.preventDefault){
3070             e.preventDefault();
3071         }
3072         //Roo.log('img onclick');
3073         this.fireEvent('click', this, e);
3074     }
3075    
3076 });
3077
3078  /*
3079  * - LGPL
3080  *
3081  * header
3082  * 
3083  */
3084
3085 /**
3086  * @class Roo.bootstrap.Header
3087  * @extends Roo.bootstrap.Component
3088  * Bootstrap Header class
3089  * @cfg {String} html content of header
3090  * @cfg {Number} level (1|2|3|4|5|6) default 1
3091  * 
3092  * @constructor
3093  * Create a new Header
3094  * @param {Object} config The config object
3095  */
3096
3097
3098 Roo.bootstrap.Header  = function(config){
3099     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3103     
3104     //href : false,
3105     html : false,
3106     level : 1,
3107     
3108     
3109     
3110     getAutoCreate : function(){
3111         
3112         
3113         
3114         var cfg = {
3115             tag: 'h' + (1 *this.level),
3116             html: this.html || ''
3117         } ;
3118         
3119         return cfg;
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * Based on:
3128  * Ext JS Library 1.1.1
3129  * Copyright(c) 2006-2007, Ext JS, LLC.
3130  *
3131  * Originally Released Under LGPL - original licence link has changed is not relivant.
3132  *
3133  * Fork - LGPL
3134  * <script type="text/javascript">
3135  */
3136  
3137 /**
3138  * @class Roo.bootstrap.MenuMgr
3139  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3140  * @singleton
3141  */
3142 Roo.bootstrap.MenuMgr = function(){
3143    var menus, active, groups = {}, attached = false, lastShow = new Date();
3144
3145    // private - called when first menu is created
3146    function init(){
3147        menus = {};
3148        active = new Roo.util.MixedCollection();
3149        Roo.get(document).addKeyListener(27, function(){
3150            if(active.length > 0){
3151                hideAll();
3152            }
3153        });
3154    }
3155
3156    // private
3157    function hideAll(){
3158        if(active && active.length > 0){
3159            var c = active.clone();
3160            c.each(function(m){
3161                m.hide();
3162            });
3163        }
3164    }
3165
3166    // private
3167    function onHide(m){
3168        active.remove(m);
3169        if(active.length < 1){
3170            Roo.get(document).un("mouseup", onMouseDown);
3171             
3172            attached = false;
3173        }
3174    }
3175
3176    // private
3177    function onShow(m){
3178        var last = active.last();
3179        lastShow = new Date();
3180        active.add(m);
3181        if(!attached){
3182           Roo.get(document).on("mouseup", onMouseDown);
3183            
3184            attached = true;
3185        }
3186        if(m.parentMenu){
3187           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188           m.parentMenu.activeChild = m;
3189        }else if(last && last.isVisible()){
3190           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3191        }
3192    }
3193
3194    // private
3195    function onBeforeHide(m){
3196        if(m.activeChild){
3197            m.activeChild.hide();
3198        }
3199        if(m.autoHideTimer){
3200            clearTimeout(m.autoHideTimer);
3201            delete m.autoHideTimer;
3202        }
3203    }
3204
3205    // private
3206    function onBeforeShow(m){
3207        var pm = m.parentMenu;
3208        if(!pm && !m.allowOtherMenus){
3209            hideAll();
3210        }else if(pm && pm.activeChild && active != m){
3211            pm.activeChild.hide();
3212        }
3213    }
3214
3215    // private this should really trigger on mouseup..
3216    function onMouseDown(e){
3217         Roo.log("on Mouse Up");
3218         
3219         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220             Roo.log("MenuManager hideAll");
3221             hideAll();
3222             e.stopEvent();
3223         }
3224         
3225         
3226    }
3227
3228    // private
3229    function onBeforeCheck(mi, state){
3230        if(state){
3231            var g = groups[mi.group];
3232            for(var i = 0, l = g.length; i < l; i++){
3233                if(g[i] != mi){
3234                    g[i].setChecked(false);
3235                }
3236            }
3237        }
3238    }
3239
3240    return {
3241
3242        /**
3243         * Hides all menus that are currently visible
3244         */
3245        hideAll : function(){
3246             hideAll();  
3247        },
3248
3249        // private
3250        register : function(menu){
3251            if(!menus){
3252                init();
3253            }
3254            menus[menu.id] = menu;
3255            menu.on("beforehide", onBeforeHide);
3256            menu.on("hide", onHide);
3257            menu.on("beforeshow", onBeforeShow);
3258            menu.on("show", onShow);
3259            var g = menu.group;
3260            if(g && menu.events["checkchange"]){
3261                if(!groups[g]){
3262                    groups[g] = [];
3263                }
3264                groups[g].push(menu);
3265                menu.on("checkchange", onCheck);
3266            }
3267        },
3268
3269         /**
3270          * Returns a {@link Roo.menu.Menu} object
3271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272          * be used to generate and return a new Menu instance.
3273          */
3274        get : function(menu){
3275            if(typeof menu == "string"){ // menu id
3276                return menus[menu];
3277            }else if(menu.events){  // menu instance
3278                return menu;
3279            }
3280            /*else if(typeof menu.length == 'number'){ // array of menu items?
3281                return new Roo.bootstrap.Menu({items:menu});
3282            }else{ // otherwise, must be a config
3283                return new Roo.bootstrap.Menu(menu);
3284            }
3285            */
3286            return false;
3287        },
3288
3289        // private
3290        unregister : function(menu){
3291            delete menus[menu.id];
3292            menu.un("beforehide", onBeforeHide);
3293            menu.un("hide", onHide);
3294            menu.un("beforeshow", onBeforeShow);
3295            menu.un("show", onShow);
3296            var g = menu.group;
3297            if(g && menu.events["checkchange"]){
3298                groups[g].remove(menu);
3299                menu.un("checkchange", onCheck);
3300            }
3301        },
3302
3303        // private
3304        registerCheckable : function(menuItem){
3305            var g = menuItem.group;
3306            if(g){
3307                if(!groups[g]){
3308                    groups[g] = [];
3309                }
3310                groups[g].push(menuItem);
3311                menuItem.on("beforecheckchange", onBeforeCheck);
3312            }
3313        },
3314
3315        // private
3316        unregisterCheckable : function(menuItem){
3317            var g = menuItem.group;
3318            if(g){
3319                groups[g].remove(menuItem);
3320                menuItem.un("beforecheckchange", onBeforeCheck);
3321            }
3322        }
3323    };
3324 }();/*
3325  * - LGPL
3326  *
3327  * menu
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Menu
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Menu class - container for MenuItems
3335  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3337  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3338  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3339  * 
3340  * @constructor
3341  * Create a new Menu
3342  * @param {Object} config The config object
3343  */
3344
3345
3346 Roo.bootstrap.Menu = function(config){
3347     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348     if (this.registerMenu && this.type != 'treeview')  {
3349         Roo.bootstrap.MenuMgr.register(this);
3350     }
3351     
3352     
3353     this.addEvents({
3354         /**
3355          * @event beforeshow
3356          * Fires before this menu is displayed (return false to block)
3357          * @param {Roo.menu.Menu} this
3358          */
3359         beforeshow : true,
3360         /**
3361          * @event beforehide
3362          * Fires before this menu is hidden (return false to block)
3363          * @param {Roo.menu.Menu} this
3364          */
3365         beforehide : true,
3366         /**
3367          * @event show
3368          * Fires after this menu is displayed
3369          * @param {Roo.menu.Menu} this
3370          */
3371         show : true,
3372         /**
3373          * @event hide
3374          * Fires after this menu is hidden
3375          * @param {Roo.menu.Menu} this
3376          */
3377         hide : true,
3378         /**
3379          * @event click
3380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381          * @param {Roo.menu.Menu} this
3382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383          * @param {Roo.EventObject} e
3384          */
3385         click : true,
3386         /**
3387          * @event mouseover
3388          * Fires when the mouse is hovering over this menu
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.EventObject} e
3391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3392          */
3393         mouseover : true,
3394         /**
3395          * @event mouseout
3396          * Fires when the mouse exits this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseout : true,
3402         /**
3403          * @event itemclick
3404          * Fires when a menu item contained in this menu is clicked
3405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406          * @param {Roo.EventObject} e
3407          */
3408         itemclick: true
3409     });
3410     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3411 };
3412
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3414     
3415    /// html : false,
3416     //align : '',
3417     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3418     type: false,
3419     /**
3420      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421      */
3422     registerMenu : true,
3423     
3424     menuItems :false, // stores the menu items..
3425     
3426     hidden:true,
3427         
3428     parentMenu : false,
3429     
3430     stopEvent : true,
3431     
3432     isLink : false,
3433     
3434     getChildContainer : function() {
3435         return this.el;  
3436     },
3437     
3438     getAutoCreate : function(){
3439          
3440         //if (['right'].indexOf(this.align)!==-1) {
3441         //    cfg.cn[1].cls += ' pull-right'
3442         //}
3443         
3444         
3445         var cfg = {
3446             tag : 'ul',
3447             cls : 'dropdown-menu' ,
3448             style : 'z-index:1000'
3449             
3450         };
3451         
3452         if (this.type === 'submenu') {
3453             cfg.cls = 'submenu active';
3454         }
3455         if (this.type === 'treeview') {
3456             cfg.cls = 'treeview-menu';
3457         }
3458         
3459         return cfg;
3460     },
3461     initEvents : function() {
3462         
3463        // Roo.log("ADD event");
3464        // Roo.log(this.triggerEl.dom);
3465         
3466         this.triggerEl.on('click', this.onTriggerClick, this);
3467         
3468         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3469         
3470         
3471         if (this.triggerEl.hasClass('nav-item')) {
3472             // dropdown toggle on the 'a' in BS4?
3473             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474         } else {
3475             this.triggerEl.addClass('dropdown-toggle');
3476         }
3477         if (Roo.isTouch) {
3478             this.el.on('touchstart'  , this.onTouch, this);
3479         }
3480         this.el.on('click' , this.onClick, this);
3481
3482         this.el.on("mouseover", this.onMouseOver, this);
3483         this.el.on("mouseout", this.onMouseOut, this);
3484         
3485     },
3486     
3487     findTargetItem : function(e)
3488     {
3489         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3490         if(!t){
3491             return false;
3492         }
3493         //Roo.log(t);         Roo.log(t.id);
3494         if(t && t.id){
3495             //Roo.log(this.menuitems);
3496             return this.menuitems.get(t.id);
3497             
3498             //return this.items.get(t.menuItemId);
3499         }
3500         
3501         return false;
3502     },
3503     
3504     onTouch : function(e) 
3505     {
3506         Roo.log("menu.onTouch");
3507         //e.stopEvent(); this make the user popdown broken
3508         this.onClick(e);
3509     },
3510     
3511     onClick : function(e)
3512     {
3513         Roo.log("menu.onClick");
3514         
3515         var t = this.findTargetItem(e);
3516         if(!t || t.isContainer){
3517             return;
3518         }
3519         Roo.log(e);
3520         /*
3521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3522             if(t == this.activeItem && t.shouldDeactivate(e)){
3523                 this.activeItem.deactivate();
3524                 delete this.activeItem;
3525                 return;
3526             }
3527             if(t.canActivate){
3528                 this.setActiveItem(t, true);
3529             }
3530             return;
3531             
3532             
3533         }
3534         */
3535        
3536         Roo.log('pass click event');
3537         
3538         t.onClick(e);
3539         
3540         this.fireEvent("click", this, t, e);
3541         
3542         var _this = this;
3543         
3544         if(!t.href.length || t.href == '#'){
3545             (function() { _this.hide(); }).defer(100);
3546         }
3547         
3548     },
3549     
3550     onMouseOver : function(e){
3551         var t  = this.findTargetItem(e);
3552         //Roo.log(t);
3553         //if(t){
3554         //    if(t.canActivate && !t.disabled){
3555         //        this.setActiveItem(t, true);
3556         //    }
3557         //}
3558         
3559         this.fireEvent("mouseover", this, e, t);
3560     },
3561     isVisible : function(){
3562         return !this.hidden;
3563     },
3564     onMouseOut : function(e){
3565         var t  = this.findTargetItem(e);
3566         
3567         //if(t ){
3568         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3569         //        this.activeItem.deactivate();
3570         //        delete this.activeItem;
3571         //    }
3572         //}
3573         this.fireEvent("mouseout", this, e, t);
3574     },
3575     
3576     
3577     /**
3578      * Displays this menu relative to another element
3579      * @param {String/HTMLElement/Roo.Element} element The element to align to
3580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581      * the element (defaults to this.defaultAlign)
3582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583      */
3584     show : function(el, pos, parentMenu)
3585     {
3586         if (false === this.fireEvent("beforeshow", this)) {
3587             Roo.log("show canceled");
3588             return;
3589         }
3590         this.parentMenu = parentMenu;
3591         if(!this.el){
3592             this.render();
3593         }
3594         
3595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3596     },
3597      /**
3598      * Displays this menu at a specific xy position
3599      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601      */
3602     showAt : function(xy, parentMenu, /* private: */_e){
3603         this.parentMenu = parentMenu;
3604         if(!this.el){
3605             this.render();
3606         }
3607         if(_e !== false){
3608             this.fireEvent("beforeshow", this);
3609             //xy = this.el.adjustForConstraints(xy);
3610         }
3611         
3612         //this.el.show();
3613         this.hideMenuItems();
3614         this.hidden = false;
3615         this.triggerEl.addClass('open');
3616         this.el.addClass('show');
3617         
3618         // reassign x when hitting right
3619         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3621         }
3622         
3623         // reassign y when hitting bottom
3624         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3626         }
3627         
3628         // but the list may align on trigger left or trigger top... should it be a properity?
3629         
3630         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3631             this.el.setXY(xy);
3632         }
3633         
3634         this.focus();
3635         this.fireEvent("show", this);
3636     },
3637     
3638     focus : function(){
3639         return;
3640         if(!this.hidden){
3641             this.doFocus.defer(50, this);
3642         }
3643     },
3644
3645     doFocus : function(){
3646         if(!this.hidden){
3647             this.focusEl.focus();
3648         }
3649     },
3650
3651     /**
3652      * Hides this menu and optionally all parent menus
3653      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654      */
3655     hide : function(deep)
3656     {
3657         if (false === this.fireEvent("beforehide", this)) {
3658             Roo.log("hide canceled");
3659             return;
3660         }
3661         this.hideMenuItems();
3662         if(this.el && this.isVisible()){
3663            
3664             if(this.activeItem){
3665                 this.activeItem.deactivate();
3666                 this.activeItem = null;
3667             }
3668             this.triggerEl.removeClass('open');;
3669             this.el.removeClass('show');
3670             this.hidden = true;
3671             this.fireEvent("hide", this);
3672         }
3673         if(deep === true && this.parentMenu){
3674             this.parentMenu.hide(true);
3675         }
3676     },
3677     
3678     onTriggerClick : function(e)
3679     {
3680         Roo.log('trigger click');
3681         
3682         var target = e.getTarget();
3683         
3684         Roo.log(target.nodeName.toLowerCase());
3685         
3686         if(target.nodeName.toLowerCase() === 'i'){
3687             e.preventDefault();
3688         }
3689         
3690     },
3691     
3692     onTriggerPress  : function(e)
3693     {
3694         Roo.log('trigger press');
3695         //Roo.log(e.getTarget());
3696        // Roo.log(this.triggerEl.dom);
3697        
3698         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699         var pel = Roo.get(e.getTarget());
3700         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701             Roo.log('is treeview or dropdown?');
3702             return;
3703         }
3704         
3705         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3706             return;
3707         }
3708         
3709         if (this.isVisible()) {
3710             Roo.log('hide');
3711             this.hide();
3712         } else {
3713             Roo.log('show');
3714             this.show(this.triggerEl, '?', false);
3715         }
3716         
3717         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3718             e.stopEvent();
3719         }
3720         
3721     },
3722        
3723     
3724     hideMenuItems : function()
3725     {
3726         Roo.log("hide Menu Items");
3727         if (!this.el) { 
3728             return;
3729         }
3730         
3731         this.el.select('.open',true).each(function(aa) {
3732             
3733             aa.removeClass('open');
3734          
3735         });
3736     },
3737     addxtypeChild : function (tree, cntr) {
3738         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739           
3740         this.menuitems.add(comp);
3741         return comp;
3742
3743     },
3744     getEl : function()
3745     {
3746         Roo.log(this.el);
3747         return this.el;
3748     },
3749     
3750     clear : function()
3751     {
3752         this.getEl().dom.innerHTML = '';
3753         this.menuitems.clear();
3754     }
3755 });
3756
3757  
3758  /*
3759  * - LGPL
3760  *
3761  * menu item
3762  * 
3763  */
3764
3765
3766 /**
3767  * @class Roo.bootstrap.MenuItem
3768  * @extends Roo.bootstrap.Component
3769  * Bootstrap MenuItem class
3770  * @cfg {String} html the menu label
3771  * @cfg {String} href the link
3772  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3775  * @cfg {String} fa favicon to show on left of menu item.
3776  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new MenuItem
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.MenuItem = function(config){
3786     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3787     this.addEvents({
3788         // raw events
3789         /**
3790          * @event click
3791          * The raw click event for the entire grid.
3792          * @param {Roo.bootstrap.MenuItem} this
3793          * @param {Roo.EventObject} e
3794          */
3795         "click" : true
3796     });
3797 };
3798
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3800     
3801     href : false,
3802     html : false,
3803     preventDefault: false,
3804     isContainer : false,
3805     active : false,
3806     fa: false,
3807     
3808     getAutoCreate : function(){
3809         
3810         if(this.isContainer){
3811             return {
3812                 tag: 'li',
3813                 cls: 'dropdown-menu-item '
3814             };
3815         }
3816         var ctag = {
3817             tag: 'span',
3818             html: 'Link'
3819         };
3820         
3821         var anc = {
3822             tag : 'a',
3823             cls : 'dropdown-item',
3824             href : '#',
3825             cn : [  ]
3826         };
3827         
3828         if (this.fa !== false) {
3829             anc.cn.push({
3830                 tag : 'i',
3831                 cls : 'fa fa-' + this.fa
3832             });
3833         }
3834         
3835         anc.cn.push(ctag);
3836         
3837         
3838         var cfg= {
3839             tag: 'li',
3840             cls: 'dropdown-menu-item',
3841             cn: [ anc ]
3842         };
3843         if (this.parent().type == 'treeview') {
3844             cfg.cls = 'treeview-menu';
3845         }
3846         if (this.active) {
3847             cfg.cls += ' active';
3848         }
3849         
3850         
3851         
3852         anc.href = this.href || cfg.cn[0].href ;
3853         ctag.html = this.html || cfg.cn[0].html ;
3854         return cfg;
3855     },
3856     
3857     initEvents: function()
3858     {
3859         if (this.parent().type == 'treeview') {
3860             this.el.select('a').on('click', this.onClick, this);
3861         }
3862         
3863         if (this.menu) {
3864             this.menu.parentType = this.xtype;
3865             this.menu.triggerEl = this.el;
3866             this.menu = this.addxtype(Roo.apply({}, this.menu));
3867         }
3868         
3869     },
3870     onClick : function(e)
3871     {
3872         Roo.log('item on click ');
3873         
3874         if(this.preventDefault){
3875             e.preventDefault();
3876         }
3877         //this.parent().hideMenuItems();
3878         
3879         this.fireEvent('click', this, e);
3880     },
3881     getEl : function()
3882     {
3883         return this.el;
3884     } 
3885 });
3886
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * menu separator
3893  * 
3894  */
3895
3896
3897 /**
3898  * @class Roo.bootstrap.MenuSeparator
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap MenuSeparator class
3901  * 
3902  * @constructor
3903  * Create a new MenuItem
3904  * @param {Object} config The config object
3905  */
3906
3907
3908 Roo.bootstrap.MenuSeparator = function(config){
3909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915         var cfg = {
3916             cls: 'divider',
3917             tag : 'li'
3918         };
3919         
3920         return cfg;
3921     }
3922    
3923 });
3924
3925  
3926
3927  
3928 /*
3929 * Licence: LGPL
3930 */
3931
3932 /**
3933  * @class Roo.bootstrap.Modal
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Modal class
3936  * @cfg {String} title Title of dialog
3937  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3939  * @cfg {Boolean} specificTitle default false
3940  * @cfg {Array} buttons Array of buttons or standard button set..
3941  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942  * @cfg {Boolean} animate default true
3943  * @cfg {Boolean} allow_close default true
3944  * @cfg {Boolean} fitwindow default false
3945  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947  * @cfg {String} size (sm|lg|xl) default empty
3948  * @cfg {Number} max_width set the max width of modal
3949  * @cfg {Boolean} editableTitle can the title be edited
3950
3951  *
3952  *
3953  * @constructor
3954  * Create a new Modal Dialog
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Modal = function(config){
3959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3960     this.addEvents({
3961         // raw events
3962         /**
3963          * @event btnclick
3964          * The raw btnclick event for the button
3965          * @param {Roo.EventObject} e
3966          */
3967         "btnclick" : true,
3968         /**
3969          * @event resize
3970          * Fire when dialog resize
3971          * @param {Roo.bootstrap.Modal} this
3972          * @param {Roo.EventObject} e
3973          */
3974         "resize" : true,
3975         /**
3976          * @event titlechanged
3977          * Fire when the editable title has been changed
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} value
3980          */
3981         "titlechanged" : true 
3982         
3983     });
3984     this.buttons = this.buttons || [];
3985
3986     if (this.tmpl) {
3987         this.tmpl = Roo.factory(this.tmpl);
3988     }
3989
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3993
3994     title : 'test dialog',
3995
3996     buttons : false,
3997
3998     // set on load...
3999
4000     html: false,
4001
4002     tmp: false,
4003
4004     specificTitle: false,
4005
4006     buttonPosition: 'right',
4007
4008     allow_close : true,
4009
4010     animate : true,
4011
4012     fitwindow: false,
4013     
4014      // private
4015     dialogEl: false,
4016     bodyEl:  false,
4017     footerEl:  false,
4018     titleEl:  false,
4019     closeEl:  false,
4020
4021     size: '',
4022     
4023     max_width: 0,
4024     
4025     max_height: 0,
4026     
4027     fit_content: false,
4028     editableTitle  : false,
4029
4030     onRender : function(ct, position)
4031     {
4032         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4033
4034         if(!this.el){
4035             var cfg = Roo.apply({},  this.getAutoCreate());
4036             cfg.id = Roo.id();
4037             //if(!cfg.name){
4038             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039             //}
4040             //if (!cfg.name.length) {
4041             //    delete cfg.name;
4042            // }
4043             if (this.cls) {
4044                 cfg.cls += ' ' + this.cls;
4045             }
4046             if (this.style) {
4047                 cfg.style = this.style;
4048             }
4049             this.el = Roo.get(document.body).createChild(cfg, position);
4050         }
4051         //var type = this.el.dom.type;
4052
4053
4054         if(this.tabIndex !== undefined){
4055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4056         }
4057
4058         this.dialogEl = this.el.select('.modal-dialog',true).first();
4059         this.bodyEl = this.el.select('.modal-body',true).first();
4060         this.closeEl = this.el.select('.modal-header .close', true).first();
4061         this.headerEl = this.el.select('.modal-header',true).first();
4062         this.titleEl = this.el.select('.modal-title',true).first();
4063         this.footerEl = this.el.select('.modal-footer',true).first();
4064
4065         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066         
4067         //this.el.addClass("x-dlg-modal");
4068
4069         if (this.buttons.length) {
4070             Roo.each(this.buttons, function(bb) {
4071                 var b = Roo.apply({}, bb);
4072                 b.xns = b.xns || Roo.bootstrap;
4073                 b.xtype = b.xtype || 'Button';
4074                 if (typeof(b.listeners) == 'undefined') {
4075                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4076                 }
4077
4078                 var btn = Roo.factory(b);
4079
4080                 btn.render(this.getButtonContainer());
4081
4082             },this);
4083         }
4084         // render the children.
4085         var nitems = [];
4086
4087         if(typeof(this.items) != 'undefined'){
4088             var items = this.items;
4089             delete this.items;
4090
4091             for(var i =0;i < items.length;i++) {
4092                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4093             }
4094         }
4095
4096         this.items = nitems;
4097
4098         // where are these used - they used to be body/close/footer
4099
4100
4101         this.initEvents();
4102         //this.el.addClass([this.fieldClass, this.cls]);
4103
4104     },
4105
4106     getAutoCreate : function()
4107     {
4108         // we will default to modal-body-overflow - might need to remove or make optional later.
4109         var bdy = {
4110                 cls : 'modal-body enable-modal-body-overflow ', 
4111                 html : this.html || ''
4112         };
4113
4114         var title = {
4115             tag: 'h4',
4116             cls : 'modal-title',
4117             html : this.title
4118         };
4119
4120         if(this.specificTitle){ // WTF is this?
4121             title = this.title;
4122         }
4123
4124         var header = [];
4125         if (this.allow_close && Roo.bootstrap.version == 3) {
4126             header.push({
4127                 tag: 'button',
4128                 cls : 'close',
4129                 html : '&times'
4130             });
4131         }
4132
4133         header.push(title);
4134
4135         if (this.editableTitle) {
4136             header.push({
4137                 cls: 'form-control roo-editable-title d-none',
4138                 tag: 'input',
4139                 type: 'text'
4140             });
4141         }
4142         
4143         if (this.allow_close && Roo.bootstrap.version == 4) {
4144             header.push({
4145                 tag: 'button',
4146                 cls : 'close',
4147                 html : '&times'
4148             });
4149         }
4150         
4151         var size = '';
4152
4153         if(this.size.length){
4154             size = 'modal-' + this.size;
4155         }
4156         
4157         var footer = Roo.bootstrap.version == 3 ?
4158             {
4159                 cls : 'modal-footer',
4160                 cn : [
4161                     {
4162                         tag: 'div',
4163                         cls: 'btn-' + this.buttonPosition
4164                     }
4165                 ]
4166
4167             } :
4168             {  // BS4 uses mr-auto on left buttons....
4169                 cls : 'modal-footer'
4170             };
4171
4172             
4173
4174         
4175         
4176         var modal = {
4177             cls: "modal",
4178              cn : [
4179                 {
4180                     cls: "modal-dialog " + size,
4181                     cn : [
4182                         {
4183                             cls : "modal-content",
4184                             cn : [
4185                                 {
4186                                     cls : 'modal-header',
4187                                     cn : header
4188                                 },
4189                                 bdy,
4190                                 footer
4191                             ]
4192
4193                         }
4194                     ]
4195
4196                 }
4197             ]
4198         };
4199
4200         if(this.animate){
4201             modal.cls += ' fade';
4202         }
4203
4204         return modal;
4205
4206     },
4207     getChildContainer : function() {
4208
4209          return this.bodyEl;
4210
4211     },
4212     getButtonContainer : function() {
4213         
4214          return Roo.bootstrap.version == 4 ?
4215             this.el.select('.modal-footer',true).first()
4216             : this.el.select('.modal-footer div',true).first();
4217
4218     },
4219     initEvents : function()
4220     {
4221         if (this.allow_close) {
4222             this.closeEl.on('click', this.hide, this);
4223         }
4224         Roo.EventManager.onWindowResize(this.resize, this, true);
4225         if (this.editableTitle) {
4226             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4227             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228             this.headerEditEl.on('keyup', function(e) {
4229                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230                         this.toggleHeaderInput(false)
4231                     }
4232                 }, this);
4233             this.headerEditEl.on('blur', function(e) {
4234                 this.toggleHeaderInput(false)
4235             },this);
4236         }
4237
4238     },
4239   
4240
4241     resize : function()
4242     {
4243         this.maskEl.setSize(
4244             Roo.lib.Dom.getViewWidth(true),
4245             Roo.lib.Dom.getViewHeight(true)
4246         );
4247         
4248         if (this.fitwindow) {
4249             
4250            
4251             this.setSize(
4252                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4254             );
4255             return;
4256         }
4257         
4258         if(this.max_width !== 0) {
4259             
4260             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4261             
4262             if(this.height) {
4263                 this.setSize(w, this.height);
4264                 return;
4265             }
4266             
4267             if(this.max_height) {
4268                 this.setSize(w,Math.min(
4269                     this.max_height,
4270                     Roo.lib.Dom.getViewportHeight(true) - 60
4271                 ));
4272                 
4273                 return;
4274             }
4275             
4276             if(!this.fit_content) {
4277                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4278                 return;
4279             }
4280             
4281             this.setSize(w, Math.min(
4282                 60 +
4283                 this.headerEl.getHeight() + 
4284                 this.footerEl.getHeight() + 
4285                 this.getChildHeight(this.bodyEl.dom.childNodes),
4286                 Roo.lib.Dom.getViewportHeight(true) - 60)
4287             );
4288         }
4289         
4290     },
4291
4292     setSize : function(w,h)
4293     {
4294         if (!w && !h) {
4295             return;
4296         }
4297         
4298         this.resizeTo(w,h);
4299     },
4300
4301     show : function() {
4302
4303         if (!this.rendered) {
4304             this.render();
4305         }
4306         this.toggleHeaderInput(false);
4307         //this.el.setStyle('display', 'block');
4308         this.el.removeClass('hideing');
4309         this.el.dom.style.display='block';
4310         
4311         Roo.get(document.body).addClass('modal-open');
4312  
4313         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4314             
4315             (function(){
4316                 this.el.addClass('show');
4317                 this.el.addClass('in');
4318             }).defer(50, this);
4319         }else{
4320             this.el.addClass('show');
4321             this.el.addClass('in');
4322         }
4323
4324         // not sure how we can show data in here..
4325         //if (this.tmpl) {
4326         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4327         //}
4328
4329         Roo.get(document.body).addClass("x-body-masked");
4330         
4331         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4332         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333         this.maskEl.dom.style.display = 'block';
4334         this.maskEl.addClass('show');
4335         
4336         
4337         this.resize();
4338         
4339         this.fireEvent('show', this);
4340
4341         // set zindex here - otherwise it appears to be ignored...
4342         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4343
4344         (function () {
4345             this.items.forEach( function(e) {
4346                 e.layout ? e.layout() : false;
4347
4348             });
4349         }).defer(100,this);
4350
4351     },
4352     hide : function()
4353     {
4354         if(this.fireEvent("beforehide", this) !== false){
4355             
4356             this.maskEl.removeClass('show');
4357             
4358             this.maskEl.dom.style.display = '';
4359             Roo.get(document.body).removeClass("x-body-masked");
4360             this.el.removeClass('in');
4361             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362
4363             if(this.animate){ // why
4364                 this.el.addClass('hideing');
4365                 this.el.removeClass('show');
4366                 (function(){
4367                     if (!this.el.hasClass('hideing')) {
4368                         return; // it's been shown again...
4369                     }
4370                     
4371                     this.el.dom.style.display='';
4372
4373                     Roo.get(document.body).removeClass('modal-open');
4374                     this.el.removeClass('hideing');
4375                 }).defer(150,this);
4376                 
4377             }else{
4378                 this.el.removeClass('show');
4379                 this.el.dom.style.display='';
4380                 Roo.get(document.body).removeClass('modal-open');
4381
4382             }
4383             this.fireEvent('hide', this);
4384         }
4385     },
4386     isVisible : function()
4387     {
4388         
4389         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4390         
4391     },
4392
4393     addButton : function(str, cb)
4394     {
4395
4396
4397         var b = Roo.apply({}, { html : str } );
4398         b.xns = b.xns || Roo.bootstrap;
4399         b.xtype = b.xtype || 'Button';
4400         if (typeof(b.listeners) == 'undefined') {
4401             b.listeners = { click : cb.createDelegate(this)  };
4402         }
4403
4404         var btn = Roo.factory(b);
4405
4406         btn.render(this.getButtonContainer());
4407
4408         return btn;
4409
4410     },
4411
4412     setDefaultButton : function(btn)
4413     {
4414         //this.el.select('.modal-footer').()
4415     },
4416
4417     resizeTo: function(w,h)
4418     {
4419         this.dialogEl.setWidth(w);
4420         
4421         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4422
4423         this.bodyEl.setHeight(h - diff);
4424         
4425         this.fireEvent('resize', this);
4426     },
4427     
4428     setContentSize  : function(w, h)
4429     {
4430
4431     },
4432     onButtonClick: function(btn,e)
4433     {
4434         //Roo.log([a,b,c]);
4435         this.fireEvent('btnclick', btn.name, e);
4436     },
4437      /**
4438      * Set the title of the Dialog
4439      * @param {String} str new Title
4440      */
4441     setTitle: function(str) {
4442         this.titleEl.dom.innerHTML = str;
4443         this.title = str;
4444     },
4445     /**
4446      * Set the body of the Dialog
4447      * @param {String} str new Title
4448      */
4449     setBody: function(str) {
4450         this.bodyEl.dom.innerHTML = str;
4451     },
4452     /**
4453      * Set the body of the Dialog using the template
4454      * @param {Obj} data - apply this data to the template and replace the body contents.
4455      */
4456     applyBody: function(obj)
4457     {
4458         if (!this.tmpl) {
4459             Roo.log("Error - using apply Body without a template");
4460             //code
4461         }
4462         this.tmpl.overwrite(this.bodyEl, obj);
4463     },
4464     
4465     getChildHeight : function(child_nodes)
4466     {
4467         if(
4468             !child_nodes ||
4469             child_nodes.length == 0
4470         ) {
4471             return 0;
4472         }
4473         
4474         var child_height = 0;
4475         
4476         for(var i = 0; i < child_nodes.length; i++) {
4477             
4478             /*
4479             * for modal with tabs...
4480             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481                 
4482                 var layout_childs = child_nodes[i].childNodes;
4483                 
4484                 for(var j = 0; j < layout_childs.length; j++) {
4485                     
4486                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487                         
4488                         var layout_body_childs = layout_childs[j].childNodes;
4489                         
4490                         for(var k = 0; k < layout_body_childs.length; k++) {
4491                             
4492                             if(layout_body_childs[k].classList.contains('navbar')) {
4493                                 child_height += layout_body_childs[k].offsetHeight;
4494                                 continue;
4495                             }
4496                             
4497                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498                                 
4499                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500                                 
4501                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502                                     
4503                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4505                                         continue;
4506                                     }
4507                                     
4508                                 }
4509                                 
4510                             }
4511                             
4512                         }
4513                     }
4514                 }
4515                 continue;
4516             }
4517             */
4518             
4519             child_height += child_nodes[i].offsetHeight;
4520             // Roo.log(child_nodes[i].offsetHeight);
4521         }
4522         
4523         return child_height;
4524     },
4525     toggleHeaderInput : function(is_edit)
4526     {
4527         if (!this.editableTitle) {
4528             return; // not editable.
4529         }
4530         if (is_edit && this.is_header_editing) {
4531             return; // already editing..
4532         }
4533         if (is_edit) {
4534     
4535             this.headerEditEl.dom.value = this.title;
4536             this.headerEditEl.removeClass('d-none');
4537             this.headerEditEl.dom.focus();
4538             this.titleEl.addClass('d-none');
4539             
4540             this.is_header_editing = true;
4541             return
4542         }
4543         // flip back to not editing.
4544         this.title = this.headerEditEl.dom.value;
4545         this.headerEditEl.addClass('d-none');
4546         this.titleEl.removeClass('d-none');
4547         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548         this.is_header_editing = false;
4549         this.fireEvent('titlechanged', this, this.title);
4550     
4551             
4552         
4553     }
4554
4555 });
4556
4557
4558 Roo.apply(Roo.bootstrap.Modal,  {
4559     /**
4560          * Button config that displays a single OK button
4561          * @type Object
4562          */
4563         OK :  [{
4564             name : 'ok',
4565             weight : 'primary',
4566             html : 'OK'
4567         }],
4568         /**
4569          * Button config that displays Yes and No buttons
4570          * @type Object
4571          */
4572         YESNO : [
4573             {
4574                 name  : 'no',
4575                 html : 'No'
4576             },
4577             {
4578                 name  :'yes',
4579                 weight : 'primary',
4580                 html : 'Yes'
4581             }
4582         ],
4583
4584         /**
4585          * Button config that displays OK and Cancel buttons
4586          * @type Object
4587          */
4588         OKCANCEL : [
4589             {
4590                name : 'cancel',
4591                 html : 'Cancel'
4592             },
4593             {
4594                 name : 'ok',
4595                 weight : 'primary',
4596                 html : 'OK'
4597             }
4598         ],
4599         /**
4600          * Button config that displays Yes, No and Cancel buttons
4601          * @type Object
4602          */
4603         YESNOCANCEL : [
4604             {
4605                 name : 'yes',
4606                 weight : 'primary',
4607                 html : 'Yes'
4608             },
4609             {
4610                 name : 'no',
4611                 html : 'No'
4612             },
4613             {
4614                 name : 'cancel',
4615                 html : 'Cancel'
4616             }
4617         ],
4618         
4619         zIndex : 10001
4620 });
4621
4622 /*
4623  * - LGPL
4624  *
4625  * messagebox - can be used as a replace
4626  * 
4627  */
4628 /**
4629  * @class Roo.MessageBox
4630  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4631  * Example usage:
4632  *<pre><code>
4633 // Basic alert:
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638     if (btn == 'ok'){
4639         // process text value...
4640     }
4641 });
4642
4643 // Show a dialog using config options:
4644 Roo.Msg.show({
4645    title:'Save Changes?',
4646    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647    buttons: Roo.Msg.YESNOCANCEL,
4648    fn: processResult,
4649    animEl: 'elId'
4650 });
4651 </code></pre>
4652  * @singleton
4653  */
4654 Roo.bootstrap.MessageBox = function(){
4655     var dlg, opt, mask, waitTimer;
4656     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657     var buttons, activeTextEl, bwidth;
4658
4659     
4660     // private
4661     var handleButton = function(button){
4662         dlg.hide();
4663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664     };
4665
4666     // private
4667     var handleHide = function(){
4668         if(opt && opt.cls){
4669             dlg.el.removeClass(opt.cls);
4670         }
4671         //if(waitTimer){
4672         //    Roo.TaskMgr.stop(waitTimer);
4673         //    waitTimer = null;
4674         //}
4675     };
4676
4677     // private
4678     var updateButtons = function(b){
4679         var width = 0;
4680         if(!b){
4681             buttons["ok"].hide();
4682             buttons["cancel"].hide();
4683             buttons["yes"].hide();
4684             buttons["no"].hide();
4685             dlg.footerEl.hide();
4686             
4687             return width;
4688         }
4689         dlg.footerEl.show();
4690         for(var k in buttons){
4691             if(typeof buttons[k] != "function"){
4692                 if(b[k]){
4693                     buttons[k].show();
4694                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695                     width += buttons[k].el.getWidth()+15;
4696                 }else{
4697                     buttons[k].hide();
4698                 }
4699             }
4700         }
4701         return width;
4702     };
4703
4704     // private
4705     var handleEsc = function(d, k, e){
4706         if(opt && opt.closable !== false){
4707             dlg.hide();
4708         }
4709         if(e){
4710             e.stopEvent();
4711         }
4712     };
4713
4714     return {
4715         /**
4716          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717          * @return {Roo.BasicDialog} The BasicDialog element
4718          */
4719         getDialog : function(){
4720            if(!dlg){
4721                 dlg = new Roo.bootstrap.Modal( {
4722                     //draggable: true,
4723                     //resizable:false,
4724                     //constraintoviewport:false,
4725                     //fixedcenter:true,
4726                     //collapsible : false,
4727                     //shim:true,
4728                     //modal: true,
4729                 //    width: 'auto',
4730                   //  height:100,
4731                     //buttonAlign:"center",
4732                     closeClick : function(){
4733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4734                             handleButton("no");
4735                         }else{
4736                             handleButton("cancel");
4737                         }
4738                     }
4739                 });
4740                 dlg.render();
4741                 dlg.on("hide", handleHide);
4742                 mask = dlg.mask;
4743                 //dlg.addKeyListener(27, handleEsc);
4744                 buttons = {};
4745                 this.buttons = buttons;
4746                 var bt = this.buttonText;
4747                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751                 //Roo.log(buttons);
4752                 bodyEl = dlg.bodyEl.createChild({
4753
4754                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755                         '<textarea class="roo-mb-textarea"></textarea>' +
4756                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4757                 });
4758                 msgEl = bodyEl.dom.firstChild;
4759                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760                 textboxEl.enableDisplayMode();
4761                 textboxEl.addKeyListener([10,13], function(){
4762                     if(dlg.isVisible() && opt && opt.buttons){
4763                         if(opt.buttons.ok){
4764                             handleButton("ok");
4765                         }else if(opt.buttons.yes){
4766                             handleButton("yes");
4767                         }
4768                     }
4769                 });
4770                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771                 textareaEl.enableDisplayMode();
4772                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773                 progressEl.enableDisplayMode();
4774                 
4775                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776                 var pf = progressEl.dom.firstChild;
4777                 if (pf) {
4778                     pp = Roo.get(pf.firstChild);
4779                     pp.setHeight(pf.offsetHeight);
4780                 }
4781                 
4782             }
4783             return dlg;
4784         },
4785
4786         /**
4787          * Updates the message box body text
4788          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789          * the XHTML-compliant non-breaking space character '&amp;#160;')
4790          * @return {Roo.MessageBox} This message box
4791          */
4792         updateText : function(text)
4793         {
4794             if(!dlg.isVisible() && !opt.width){
4795                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797             }
4798             msgEl.innerHTML = text || '&#160;';
4799       
4800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802             var w = Math.max(
4803                     Math.min(opt.width || cw , this.maxWidth), 
4804                     Math.max(opt.minWidth || this.minWidth, bwidth)
4805             );
4806             if(opt.prompt){
4807                 activeTextEl.setWidth(w);
4808             }
4809             if(dlg.isVisible()){
4810                 dlg.fixedcenter = false;
4811             }
4812             // to big, make it scroll. = But as usual stupid IE does not support
4813             // !important..
4814             
4815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818             } else {
4819                 bodyEl.dom.style.height = '';
4820                 bodyEl.dom.style.overflowY = '';
4821             }
4822             if (cw > w) {
4823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.overflowX = '';
4826             }
4827             
4828             dlg.setContentSize(w, bodyEl.getHeight());
4829             if(dlg.isVisible()){
4830                 dlg.fixedcenter = true;
4831             }
4832             return this;
4833         },
4834
4835         /**
4836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840          * @return {Roo.MessageBox} This message box
4841          */
4842         updateProgress : function(value, text){
4843             if(text){
4844                 this.updateText(text);
4845             }
4846             
4847             if (pp) { // weird bug on my firefox - for some reason this is not defined
4848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4850             }
4851             return this;
4852         },        
4853
4854         /**
4855          * Returns true if the message box is currently displayed
4856          * @return {Boolean} True if the message box is visible, else false
4857          */
4858         isVisible : function(){
4859             return dlg && dlg.isVisible();  
4860         },
4861
4862         /**
4863          * Hides the message box if it is displayed
4864          */
4865         hide : function(){
4866             if(this.isVisible()){
4867                 dlg.hide();
4868             }  
4869         },
4870
4871         /**
4872          * Displays a new message box, or reinitializes an existing message box, based on the config options
4873          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874          * The following config object properties are supported:
4875          * <pre>
4876 Property    Type             Description
4877 ----------  ---------------  ------------------------------------------------------------------------------------
4878 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4879                                    closes (defaults to undefined)
4880 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4883                                    progress and wait dialogs will ignore this property and always hide the
4884                                    close button as they can only be closed programmatically.
4885 cls               String           A custom CSS class to apply to the message box element
4886 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4887                                    displayed (defaults to 75)
4888 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4889                                    function will be btn (the name of the button that was clicked, if applicable,
4890                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4891                                    Progress and wait dialogs will ignore this option since they do not respond to
4892                                    user actions and can only be closed programmatically, so any required function
4893                                    should be called by the same code after it closes the dialog.
4894 icon              String           A CSS class that provides a background image to be used as an icon for
4895                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4897 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4898 modal             Boolean          False to allow user interaction with the page while the message box is
4899                                    displayed (defaults to true)
4900 msg               String           A string that will replace the existing message box body text (defaults
4901                                    to the XHTML-compliant non-breaking space character '&#160;')
4902 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4903 progress          Boolean          True to display a progress bar (defaults to false)
4904 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4907 title             String           The title text
4908 value             String           The string value to set into the active textbox element if displayed
4909 wait              Boolean          True to display a progress bar (defaults to false)
4910 width             Number           The width of the dialog in pixels
4911 </pre>
4912          *
4913          * Example usage:
4914          * <pre><code>
4915 Roo.Msg.show({
4916    title: 'Address',
4917    msg: 'Please enter your address:',
4918    width: 300,
4919    buttons: Roo.MessageBox.OKCANCEL,
4920    multiline: true,
4921    fn: saveAddress,
4922    animEl: 'addAddressBtn'
4923 });
4924 </code></pre>
4925          * @param {Object} config Configuration options
4926          * @return {Roo.MessageBox} This message box
4927          */
4928         show : function(options)
4929         {
4930             
4931             // this causes nightmares if you show one dialog after another
4932             // especially on callbacks..
4933              
4934             if(this.isVisible()){
4935                 
4936                 this.hide();
4937                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4939                 Roo.log("New Dialog Message:" +  options.msg )
4940                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4942                 
4943             }
4944             var d = this.getDialog();
4945             opt = options;
4946             d.setTitle(opt.title || "&#160;");
4947             d.closeEl.setDisplayed(opt.closable !== false);
4948             activeTextEl = textboxEl;
4949             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4950             if(opt.prompt){
4951                 if(opt.multiline){
4952                     textboxEl.hide();
4953                     textareaEl.show();
4954                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4955                         opt.multiline : this.defaultTextHeight);
4956                     activeTextEl = textareaEl;
4957                 }else{
4958                     textboxEl.show();
4959                     textareaEl.hide();
4960                 }
4961             }else{
4962                 textboxEl.hide();
4963                 textareaEl.hide();
4964             }
4965             progressEl.setDisplayed(opt.progress === true);
4966             if (opt.progress) {
4967                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968             }
4969             this.updateProgress(0);
4970             activeTextEl.dom.value = opt.value || "";
4971             if(opt.prompt){
4972                 dlg.setDefaultButton(activeTextEl);
4973             }else{
4974                 var bs = opt.buttons;
4975                 var db = null;
4976                 if(bs && bs.ok){
4977                     db = buttons["ok"];
4978                 }else if(bs && bs.yes){
4979                     db = buttons["yes"];
4980                 }
4981                 dlg.setDefaultButton(db);
4982             }
4983             bwidth = updateButtons(opt.buttons);
4984             this.updateText(opt.msg);
4985             if(opt.cls){
4986                 d.el.addClass(opt.cls);
4987             }
4988             d.proxyDrag = opt.proxyDrag === true;
4989             d.modal = opt.modal !== false;
4990             d.mask = opt.modal !== false ? mask : false;
4991             if(!d.isVisible()){
4992                 // force it to the end of the z-index stack so it gets a cursor in FF
4993                 document.body.appendChild(dlg.el.dom);
4994                 d.animateTarget = null;
4995                 d.show(options.animEl);
4996             }
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5002          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003          * and closing the message box when the process is complete.
5004          * @param {String} title The title bar text
5005          * @param {String} msg The message box body text
5006          * @return {Roo.MessageBox} This message box
5007          */
5008         progress : function(title, msg){
5009             this.show({
5010                 title : title,
5011                 msg : msg,
5012                 buttons: false,
5013                 progress:true,
5014                 closable:false,
5015                 minWidth: this.minProgressWidth,
5016                 modal : true
5017             });
5018             return this;
5019         },
5020
5021         /**
5022          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023          * If a callback function is passed it will be called after the user clicks the button, and the
5024          * id of the button that was clicked will be passed as the only parameter to the callback
5025          * (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         alert : function(title, msg, fn, scope)
5033         {
5034             this.show({
5035                 title : title,
5036                 msg : msg,
5037                 buttons: this.OK,
5038                 fn: fn,
5039                 closable : false,
5040                 scope : scope,
5041                 modal : true
5042             });
5043             return this;
5044         },
5045
5046         /**
5047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049          * You are responsible for closing the message box when the process is complete.
5050          * @param {String} msg The message box body text
5051          * @param {String} title (optional) The title bar text
5052          * @return {Roo.MessageBox} This message box
5053          */
5054         wait : function(msg, title){
5055             this.show({
5056                 title : title,
5057                 msg : msg,
5058                 buttons: false,
5059                 closable:false,
5060                 progress:true,
5061                 modal:true,
5062                 width:300,
5063                 wait:true
5064             });
5065             waitTimer = Roo.TaskMgr.start({
5066                 run: function(i){
5067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5068                 },
5069                 interval: 1000
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078          * @param {String} title The title bar text
5079          * @param {String} msg The message box body text
5080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081          * @param {Object} scope (optional) The scope of the callback function
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         confirm : function(title, msg, fn, scope){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: this.YESNO,
5089                 fn: fn,
5090                 scope : scope,
5091                 modal : true
5092             });
5093             return this;
5094         },
5095
5096         /**
5097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100          * (could also be the top-right close button) and the text that was entered will be passed as the two
5101          * parameters to the callback.
5102          * @param {String} title The title bar text
5103          * @param {String} msg The message box body text
5104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105          * @param {Object} scope (optional) The scope of the callback function
5106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108          * @return {Roo.MessageBox} This message box
5109          */
5110         prompt : function(title, msg, fn, scope, multiline){
5111             this.show({
5112                 title : title,
5113                 msg : msg,
5114                 buttons: this.OKCANCEL,
5115                 fn: fn,
5116                 minWidth:250,
5117                 scope : scope,
5118                 prompt:true,
5119                 multiline: multiline,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Button config that displays a single OK button
5127          * @type Object
5128          */
5129         OK : {ok:true},
5130         /**
5131          * Button config that displays Yes and No buttons
5132          * @type Object
5133          */
5134         YESNO : {yes:true, no:true},
5135         /**
5136          * Button config that displays OK and Cancel buttons
5137          * @type Object
5138          */
5139         OKCANCEL : {ok:true, cancel:true},
5140         /**
5141          * Button config that displays Yes, No and Cancel buttons
5142          * @type Object
5143          */
5144         YESNOCANCEL : {yes:true, no:true, cancel:true},
5145
5146         /**
5147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5148          * @type Number
5149          */
5150         defaultTextHeight : 75,
5151         /**
5152          * The maximum width in pixels of the message box (defaults to 600)
5153          * @type Number
5154          */
5155         maxWidth : 600,
5156         /**
5157          * The minimum width in pixels of the message box (defaults to 100)
5158          * @type Number
5159          */
5160         minWidth : 100,
5161         /**
5162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5164          * @type Number
5165          */
5166         minProgressWidth : 250,
5167         /**
5168          * An object containing the default button text strings that can be overriden for localized language support.
5169          * Supported properties are: ok, cancel, yes and no.
5170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5171          * @type Object
5172          */
5173         buttonText : {
5174             ok : "OK",
5175             cancel : "Cancel",
5176             yes : "Yes",
5177             no : "No"
5178         }
5179     };
5180 }();
5181
5182 /**
5183  * Shorthand for {@link Roo.MessageBox}
5184  */
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5187 /*
5188  * - LGPL
5189  *
5190  * navbar
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Navbar
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Navbar class
5198
5199  * @constructor
5200  * Create a new Navbar
5201  * @param {Object} config The config object
5202  */
5203
5204
5205 Roo.bootstrap.Navbar = function(config){
5206     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207     this.addEvents({
5208         // raw events
5209         /**
5210          * @event beforetoggle
5211          * Fire before toggle the menu
5212          * @param {Roo.EventObject} e
5213          */
5214         "beforetoggle" : true
5215     });
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5219     
5220     
5221    
5222     // private
5223     navItems : false,
5224     loadMask : false,
5225     
5226     
5227     getAutoCreate : function(){
5228         
5229         
5230         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231         
5232     },
5233     
5234     initEvents :function ()
5235     {
5236         //Roo.log(this.el.select('.navbar-toggle',true));
5237         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5238         
5239         var mark = {
5240             tag: "div",
5241             cls:"x-dlg-mask"
5242         };
5243         
5244         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245         
5246         var size = this.el.getSize();
5247         this.maskEl.setSize(size.width, size.height);
5248         this.maskEl.enableDisplayMode("block");
5249         this.maskEl.hide();
5250         
5251         if(this.loadMask){
5252             this.maskEl.show();
5253         }
5254     },
5255     
5256     
5257     getChildContainer : function()
5258     {
5259         if (this.el && this.el.select('.collapse').getCount()) {
5260             return this.el.select('.collapse',true).first();
5261         }
5262         
5263         return this.el;
5264     },
5265     
5266     mask : function()
5267     {
5268         this.maskEl.show();
5269     },
5270     
5271     unmask : function()
5272     {
5273         this.maskEl.hide();
5274     },
5275     onToggle : function()
5276     {
5277         
5278         if(this.fireEvent('beforetoggle', this) === false){
5279             return;
5280         }
5281         var ce = this.el.select('.navbar-collapse',true).first();
5282       
5283         if (!ce.hasClass('show')) {
5284            this.expand();
5285         } else {
5286             this.collapse();
5287         }
5288         
5289         
5290     
5291     },
5292     /**
5293      * Expand the navbar pulldown 
5294      */
5295     expand : function ()
5296     {
5297        
5298         var ce = this.el.select('.navbar-collapse',true).first();
5299         if (ce.hasClass('collapsing')) {
5300             return;
5301         }
5302         ce.dom.style.height = '';
5303                // show it...
5304         ce.addClass('in'); // old...
5305         ce.removeClass('collapse');
5306         ce.addClass('show');
5307         var h = ce.getHeight();
5308         Roo.log(h);
5309         ce.removeClass('show');
5310         // at this point we should be able to see it..
5311         ce.addClass('collapsing');
5312         
5313         ce.setHeight(0); // resize it ...
5314         ce.on('transitionend', function() {
5315             //Roo.log('done transition');
5316             ce.removeClass('collapsing');
5317             ce.addClass('show');
5318             ce.removeClass('collapse');
5319
5320             ce.dom.style.height = '';
5321         }, this, { single: true} );
5322         ce.setHeight(h);
5323         ce.dom.scrollTop = 0;
5324     },
5325     /**
5326      * Collapse the navbar pulldown 
5327      */
5328     collapse : function()
5329     {
5330          var ce = this.el.select('.navbar-collapse',true).first();
5331        
5332         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333             // it's collapsed or collapsing..
5334             return;
5335         }
5336         ce.removeClass('in'); // old...
5337         ce.setHeight(ce.getHeight());
5338         ce.removeClass('show');
5339         ce.addClass('collapsing');
5340         
5341         ce.on('transitionend', function() {
5342             ce.dom.style.height = '';
5343             ce.removeClass('collapsing');
5344             ce.addClass('collapse');
5345         }, this, { single: true} );
5346         ce.setHeight(0);
5347     }
5348     
5349     
5350     
5351 });
5352
5353
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * navbar
5361  * 
5362  */
5363
5364 /**
5365  * @class Roo.bootstrap.NavSimplebar
5366  * @extends Roo.bootstrap.Navbar
5367  * Bootstrap Sidebar class
5368  *
5369  * @cfg {Boolean} inverse is inverted color
5370  * 
5371  * @cfg {String} type (nav | pills | tabs)
5372  * @cfg {Boolean} arrangement stacked | justified
5373  * @cfg {String} align (left | right) alignment
5374  * 
5375  * @cfg {Boolean} main (true|false) main nav bar? default false
5376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377  * 
5378  * @cfg {String} tag (header|footer|nav|div) default is nav 
5379
5380  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381  * 
5382  * 
5383  * @constructor
5384  * Create a new Sidebar
5385  * @param {Object} config The config object
5386  */
5387
5388
5389 Roo.bootstrap.NavSimplebar = function(config){
5390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5391 };
5392
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5394     
5395     inverse: false,
5396     
5397     type: false,
5398     arrangement: '',
5399     align : false,
5400     
5401     weight : 'light',
5402     
5403     main : false,
5404     
5405     
5406     tag : false,
5407     
5408     
5409     getAutoCreate : function(){
5410         
5411         
5412         var cfg = {
5413             tag : this.tag || 'div',
5414             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415         };
5416         if (['light','white'].indexOf(this.weight) > -1) {
5417             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418         }
5419         cfg.cls += ' bg-' + this.weight;
5420         
5421         if (this.inverse) {
5422             cfg.cls += ' navbar-inverse';
5423             
5424         }
5425         
5426         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427         
5428         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5429             return cfg;
5430         }
5431         
5432         
5433     
5434         
5435         cfg.cn = [
5436             {
5437                 cls: 'nav nav-' + this.xtype,
5438                 tag : 'ul'
5439             }
5440         ];
5441         
5442          
5443         this.type = this.type || 'nav';
5444         if (['tabs','pills'].indexOf(this.type) != -1) {
5445             cfg.cn[0].cls += ' nav-' + this.type
5446         
5447         
5448         } else {
5449             if (this.type!=='nav') {
5450                 Roo.log('nav type must be nav/tabs/pills')
5451             }
5452             cfg.cn[0].cls += ' navbar-nav'
5453         }
5454         
5455         
5456         
5457         
5458         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459             cfg.cn[0].cls += ' nav-' + this.arrangement;
5460         }
5461         
5462         
5463         if (this.align === 'right') {
5464             cfg.cn[0].cls += ' navbar-right';
5465         }
5466         
5467         
5468         
5469         
5470         return cfg;
5471     
5472         
5473     }
5474     
5475     
5476     
5477 });
5478
5479
5480
5481  
5482
5483  
5484        /*
5485  * - LGPL
5486  *
5487  * navbar
5488  * navbar-fixed-top
5489  * navbar-expand-md  fixed-top 
5490  */
5491
5492 /**
5493  * @class Roo.bootstrap.NavHeaderbar
5494  * @extends Roo.bootstrap.NavSimplebar
5495  * Bootstrap Sidebar class
5496  *
5497  * @cfg {String} brand what is brand
5498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499  * @cfg {String} brand_href href of the brand
5500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5504  * 
5505  * @constructor
5506  * Create a new Sidebar
5507  * @param {Object} config The config object
5508  */
5509
5510
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513       
5514 };
5515
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5517     
5518     position: '',
5519     brand: '',
5520     brand_href: false,
5521     srButton : true,
5522     autohide : false,
5523     desktopCenter : false,
5524    
5525     
5526     getAutoCreate : function(){
5527         
5528         var   cfg = {
5529             tag: this.nav || 'nav',
5530             cls: 'navbar navbar-expand-md',
5531             role: 'navigation',
5532             cn: []
5533         };
5534         
5535         var cn = cfg.cn;
5536         if (this.desktopCenter) {
5537             cn.push({cls : 'container', cn : []});
5538             cn = cn[0].cn;
5539         }
5540         
5541         if(this.srButton){
5542             var btn = {
5543                 tag: 'button',
5544                 type: 'button',
5545                 cls: 'navbar-toggle navbar-toggler',
5546                 'data-toggle': 'collapse',
5547                 cn: [
5548                     {
5549                         tag: 'span',
5550                         cls: 'sr-only',
5551                         html: 'Toggle navigation'
5552                     },
5553                     {
5554                         tag: 'span',
5555                         cls: 'icon-bar navbar-toggler-icon'
5556                     },
5557                     {
5558                         tag: 'span',
5559                         cls: 'icon-bar'
5560                     },
5561                     {
5562                         tag: 'span',
5563                         cls: 'icon-bar'
5564                     }
5565                 ]
5566             };
5567             
5568             cn.push( Roo.bootstrap.version == 4 ? btn : {
5569                 tag: 'div',
5570                 cls: 'navbar-header',
5571                 cn: [
5572                     btn
5573                 ]
5574             });
5575         }
5576         
5577         cn.push({
5578             tag: 'div',
5579             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580             cn : []
5581         });
5582         
5583         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584         
5585         if (['light','white'].indexOf(this.weight) > -1) {
5586             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587         }
5588         cfg.cls += ' bg-' + this.weight;
5589         
5590         
5591         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593             
5594             // tag can override this..
5595             
5596             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5597         }
5598         
5599         if (this.brand !== '') {
5600             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602                 tag: 'a',
5603                 href: this.brand_href ? this.brand_href : '#',
5604                 cls: 'navbar-brand',
5605                 cn: [
5606                 this.brand
5607                 ]
5608             });
5609         }
5610         
5611         if(this.main){
5612             cfg.cls += ' main-nav';
5613         }
5614         
5615         
5616         return cfg;
5617
5618         
5619     },
5620     getHeaderChildContainer : function()
5621     {
5622         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623             return this.el.select('.navbar-header',true).first();
5624         }
5625         
5626         return this.getChildContainer();
5627     },
5628     
5629     getChildContainer : function()
5630     {
5631          
5632         return this.el.select('.roo-navbar-collapse',true).first();
5633          
5634         
5635     },
5636     
5637     initEvents : function()
5638     {
5639         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640         
5641         if (this.autohide) {
5642             
5643             var prevScroll = 0;
5644             var ft = this.el;
5645             
5646             Roo.get(document).on('scroll',function(e) {
5647                 var ns = Roo.get(document).getScroll().top;
5648                 var os = prevScroll;
5649                 prevScroll = ns;
5650                 
5651                 if(ns > os){
5652                     ft.removeClass('slideDown');
5653                     ft.addClass('slideUp');
5654                     return;
5655                 }
5656                 ft.removeClass('slideUp');
5657                 ft.addClass('slideDown');
5658                  
5659               
5660           },this);
5661         }
5662     }    
5663     
5664 });
5665
5666
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * navbar
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.NavSidebar
5679  * @extends Roo.bootstrap.Navbar
5680  * Bootstrap Sidebar class
5681  * 
5682  * @constructor
5683  * Create a new Sidebar
5684  * @param {Object} config The config object
5685  */
5686
5687
5688 Roo.bootstrap.NavSidebar = function(config){
5689     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5693     
5694     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695     
5696     getAutoCreate : function(){
5697         
5698         
5699         return  {
5700             tag: 'div',
5701             cls: 'sidebar sidebar-nav'
5702         };
5703     
5704         
5705     }
5706     
5707     
5708     
5709 });
5710
5711
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * nav group
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.NavGroup
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap NavGroup class
5726  * @cfg {String} align (left|right)
5727  * @cfg {Boolean} inverse
5728  * @cfg {String} type (nav|pills|tab) default nav
5729  * @cfg {String} navId - reference Id for navbar.
5730
5731  * 
5732  * @constructor
5733  * Create a new nav group
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.NavGroup = function(config){
5738     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5739     this.navItems = [];
5740    
5741     Roo.bootstrap.NavGroup.register(this);
5742      this.addEvents({
5743         /**
5744              * @event changed
5745              * Fires when the active item changes
5746              * @param {Roo.bootstrap.NavGroup} this
5747              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5749          */
5750         'changed': true
5751      });
5752     
5753 };
5754
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5756     
5757     align: '',
5758     inverse: false,
5759     form: false,
5760     type: 'nav',
5761     navId : '',
5762     // private
5763     
5764     navItems : false, 
5765     
5766     getAutoCreate : function()
5767     {
5768         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag : 'ul',
5772             cls: 'nav' 
5773         };
5774         if (Roo.bootstrap.version == 4) {
5775             if (['tabs','pills'].indexOf(this.type) != -1) {
5776                 cfg.cls += ' nav-' + this.type; 
5777             } else {
5778                 // trying to remove so header bar can right align top?
5779                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780                     // do not use on header bar... 
5781                     cfg.cls += ' navbar-nav';
5782                 }
5783             }
5784             
5785         } else {
5786             if (['tabs','pills'].indexOf(this.type) != -1) {
5787                 cfg.cls += ' nav-' + this.type
5788             } else {
5789                 if (this.type !== 'nav') {
5790                     Roo.log('nav type must be nav/tabs/pills')
5791                 }
5792                 cfg.cls += ' navbar-nav'
5793             }
5794         }
5795         
5796         if (this.parent() && this.parent().sidebar) {
5797             cfg = {
5798                 tag: 'ul',
5799                 cls: 'dashboard-menu sidebar-menu'
5800             };
5801             
5802             return cfg;
5803         }
5804         
5805         if (this.form === true) {
5806             cfg = {
5807                 tag: 'form',
5808                 cls: 'navbar-form form-inline'
5809             };
5810             //nav navbar-right ml-md-auto
5811             if (this.align === 'right') {
5812                 cfg.cls += ' navbar-right ml-md-auto';
5813             } else {
5814                 cfg.cls += ' navbar-left';
5815             }
5816         }
5817         
5818         if (this.align === 'right') {
5819             cfg.cls += ' navbar-right ml-md-auto';
5820         } else {
5821             cfg.cls += ' mr-auto';
5822         }
5823         
5824         if (this.inverse) {
5825             cfg.cls += ' navbar-inverse';
5826             
5827         }
5828         
5829         
5830         return cfg;
5831     },
5832     /**
5833     * sets the active Navigation item
5834     * @param {Roo.bootstrap.NavItem} the new current navitem
5835     */
5836     setActiveItem : function(item)
5837     {
5838         var prev = false;
5839         Roo.each(this.navItems, function(v){
5840             if (v == item) {
5841                 return ;
5842             }
5843             if (v.isActive()) {
5844                 v.setActive(false, true);
5845                 prev = v;
5846                 
5847             }
5848             
5849         });
5850
5851         item.setActive(true, true);
5852         this.fireEvent('changed', this, item, prev);
5853         
5854         
5855     },
5856     /**
5857     * gets the active Navigation item
5858     * @return {Roo.bootstrap.NavItem} the current navitem
5859     */
5860     getActive : function()
5861     {
5862         
5863         var prev = false;
5864         Roo.each(this.navItems, function(v){
5865             
5866             if (v.isActive()) {
5867                 prev = v;
5868                 
5869             }
5870             
5871         });
5872         return prev;
5873     },
5874     
5875     indexOfNav : function()
5876     {
5877         
5878         var prev = false;
5879         Roo.each(this.navItems, function(v,i){
5880             
5881             if (v.isActive()) {
5882                 prev = i;
5883                 
5884             }
5885             
5886         });
5887         return prev;
5888     },
5889     /**
5890     * adds a Navigation item
5891     * @param {Roo.bootstrap.NavItem} the navitem to add
5892     */
5893     addItem : function(cfg)
5894     {
5895         if (this.form && Roo.bootstrap.version == 4) {
5896             cfg.tag = 'div';
5897         }
5898         var cn = new Roo.bootstrap.NavItem(cfg);
5899         this.register(cn);
5900         cn.parentId = this.id;
5901         cn.onRender(this.el, null);
5902         return cn;
5903     },
5904     /**
5905     * register a Navigation item
5906     * @param {Roo.bootstrap.NavItem} the navitem to add
5907     */
5908     register : function(item)
5909     {
5910         this.navItems.push( item);
5911         item.navId = this.navId;
5912     
5913     },
5914     
5915     /**
5916     * clear all the Navigation item
5917     */
5918    
5919     clearAll : function()
5920     {
5921         this.navItems = [];
5922         this.el.dom.innerHTML = '';
5923     },
5924     
5925     getNavItem: function(tabId)
5926     {
5927         var ret = false;
5928         Roo.each(this.navItems, function(e) {
5929             if (e.tabId == tabId) {
5930                ret =  e;
5931                return false;
5932             }
5933             return true;
5934             
5935         });
5936         return ret;
5937     },
5938     
5939     setActiveNext : function()
5940     {
5941         var i = this.indexOfNav(this.getActive());
5942         if (i > this.navItems.length) {
5943             return;
5944         }
5945         this.setActiveItem(this.navItems[i+1]);
5946     },
5947     setActivePrev : function()
5948     {
5949         var i = this.indexOfNav(this.getActive());
5950         if (i  < 1) {
5951             return;
5952         }
5953         this.setActiveItem(this.navItems[i-1]);
5954     },
5955     clearWasActive : function(except) {
5956         Roo.each(this.navItems, function(e) {
5957             if (e.tabId != except.tabId && e.was_active) {
5958                e.was_active = false;
5959                return false;
5960             }
5961             return true;
5962             
5963         });
5964     },
5965     getWasActive : function ()
5966     {
5967         var r = false;
5968         Roo.each(this.navItems, function(e) {
5969             if (e.was_active) {
5970                r = e;
5971                return false;
5972             }
5973             return true;
5974             
5975         });
5976         return r;
5977     }
5978     
5979     
5980 });
5981
5982  
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5984     
5985     groups: {},
5986      /**
5987     * register a Navigation Group
5988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989     */
5990     register : function(navgrp)
5991     {
5992         this.groups[navgrp.navId] = navgrp;
5993         
5994     },
5995     /**
5996     * fetch a Navigation Group based on the navigation ID
5997     * @param {string} the navgroup to add
5998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5999     */
6000     get: function(navId) {
6001         if (typeof(this.groups[navId]) == 'undefined') {
6002             return false;
6003             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004         }
6005         return this.groups[navId] ;
6006     }
6007     
6008     
6009     
6010 });
6011
6012  /*
6013  * - LGPL
6014  *
6015  * row
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.NavItem
6021  * @extends Roo.bootstrap.Component
6022  * Bootstrap Navbar.NavItem class
6023  * @cfg {String} href  link to
6024  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025
6026  * @cfg {String} html content of button
6027  * @cfg {String} badge text inside badge
6028  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029  * @cfg {String} glyphicon DEPRICATED - use fa
6030  * @cfg {String} icon DEPRICATED - use fa
6031  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032  * @cfg {Boolean} active Is item active
6033  * @cfg {Boolean} disabled Is item disabled
6034  
6035  * @cfg {Boolean} preventDefault (true | false) default false
6036  * @cfg {String} tabId the tab that this item activates.
6037  * @cfg {String} tagtype (a|span) render as a href or span?
6038  * @cfg {Boolean} animateRef (true|false) link to element default false  
6039   
6040  * @constructor
6041  * Create a new Navbar Item
6042  * @param {Object} config The config object
6043  */
6044 Roo.bootstrap.NavItem = function(config){
6045     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6046     this.addEvents({
6047         // raw events
6048         /**
6049          * @event click
6050          * The raw click event for the entire grid.
6051          * @param {Roo.EventObject} e
6052          */
6053         "click" : true,
6054          /**
6055             * @event changed
6056             * Fires when the active item active state changes
6057             * @param {Roo.bootstrap.NavItem} this
6058             * @param {boolean} state the new state
6059              
6060          */
6061         'changed': true,
6062         /**
6063             * @event scrollto
6064             * Fires when scroll to element
6065             * @param {Roo.bootstrap.NavItem} this
6066             * @param {Object} options
6067             * @param {Roo.EventObject} e
6068              
6069          */
6070         'scrollto': true
6071     });
6072    
6073 };
6074
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6076     
6077     href: false,
6078     html: '',
6079     badge: '',
6080     icon: false,
6081     fa : false,
6082     glyphicon: false,
6083     active: false,
6084     preventDefault : false,
6085     tabId : false,
6086     tagtype : 'a',
6087     tag: 'li',
6088     disabled : false,
6089     animateRef : false,
6090     was_active : false,
6091     button_weight : '',
6092     button_outline : false,
6093     
6094     navLink: false,
6095     
6096     getAutoCreate : function(){
6097          
6098         var cfg = {
6099             tag: this.tag,
6100             cls: 'nav-item'
6101         };
6102         
6103         if (this.active) {
6104             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105         }
6106         if (this.disabled) {
6107             cfg.cls += ' disabled';
6108         }
6109         
6110         // BS4 only?
6111         if (this.button_weight.length) {
6112             cfg.tag = this.href ? 'a' : 'button';
6113             cfg.html = this.html || '';
6114             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115             if (this.href) {
6116                 cfg.href = this.href;
6117             }
6118             if (this.fa) {
6119                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6120             }
6121             
6122             // menu .. should add dropdown-menu class - so no need for carat..
6123             
6124             if (this.badge !== '') {
6125                  
6126                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6127             }
6128             return cfg;
6129         }
6130         
6131         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132             cfg.cn = [
6133                 {
6134                     tag: this.tagtype,
6135                     href : this.href || "#",
6136                     html: this.html || ''
6137                 }
6138             ];
6139             if (this.tagtype == 'a') {
6140                 cfg.cn[0].cls = 'nav-link';
6141             }
6142             if (this.icon) {
6143                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if (this.fa) {
6146                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147             }
6148             if(this.glyphicon) {
6149                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6150             }
6151             
6152             if (this.menu) {
6153                 
6154                 cfg.cn[0].html += " <span class='caret'></span>";
6155              
6156             }
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162         }
6163         
6164         
6165         
6166         return cfg;
6167     },
6168     onRender : function(ct, position)
6169     {
6170        // Roo.log("Call onRender: " + this.xtype);
6171         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172             this.tag = 'div';
6173         }
6174         
6175         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176         this.navLink = this.el.select('.nav-link',true).first();
6177         return ret;
6178     },
6179       
6180     
6181     initEvents: function() 
6182     {
6183         if (typeof (this.menu) != 'undefined') {
6184             this.menu.parentType = this.xtype;
6185             this.menu.triggerEl = this.el;
6186             this.menu = this.addxtype(Roo.apply({}, this.menu));
6187         }
6188         
6189         this.el.select('a',true).on('click', this.onClick, this);
6190         
6191         if(this.tagtype == 'span'){
6192             this.el.select('span',true).on('click', this.onClick, this);
6193         }
6194        
6195         // at this point parent should be available..
6196         this.parent().register(this);
6197     },
6198     
6199     onClick : function(e)
6200     {
6201         if (e.getTarget('.dropdown-menu-item')) {
6202             // did you click on a menu itemm.... - then don't trigger onclick..
6203             return;
6204         }
6205         
6206         if(
6207                 this.preventDefault || 
6208                 this.href == '#' 
6209         ){
6210             Roo.log("NavItem - prevent Default?");
6211             e.preventDefault();
6212         }
6213         
6214         if (this.disabled) {
6215             return;
6216         }
6217         
6218         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219         if (tg && tg.transition) {
6220             Roo.log("waiting for the transitionend");
6221             return;
6222         }
6223         
6224         
6225         
6226         //Roo.log("fire event clicked");
6227         if(this.fireEvent('click', this, e) === false){
6228             return;
6229         };
6230         
6231         if(this.tagtype == 'span'){
6232             return;
6233         }
6234         
6235         //Roo.log(this.href);
6236         var ael = this.el.select('a',true).first();
6237         //Roo.log(ael);
6238         
6239         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242                 return; // ignore... - it's a 'hash' to another page.
6243             }
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246             this.scrollToElement(e);
6247         }
6248         
6249         
6250         var p =  this.parent();
6251    
6252         if (['tabs','pills'].indexOf(p.type)!==-1) {
6253             if (typeof(p.setActiveItem) !== 'undefined') {
6254                 p.setActiveItem(this);
6255             }
6256         }
6257         
6258         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260             // remove the collapsed menu expand...
6261             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6262         }
6263     },
6264     
6265     isActive: function () {
6266         return this.active
6267     },
6268     setActive : function(state, fire, is_was_active)
6269     {
6270         if (this.active && !state && this.navId) {
6271             this.was_active = true;
6272             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273             if (nv) {
6274                 nv.clearWasActive(this);
6275             }
6276             
6277         }
6278         this.active = state;
6279         
6280         if (!state ) {
6281             this.el.removeClass('active');
6282             this.navLink ? this.navLink.removeClass('active') : false;
6283         } else if (!this.el.hasClass('active')) {
6284             
6285             this.el.addClass('active');
6286             if (Roo.bootstrap.version == 4 && this.navLink ) {
6287                 this.navLink.addClass('active');
6288             }
6289             
6290         }
6291         if (fire) {
6292             this.fireEvent('changed', this, state);
6293         }
6294         
6295         // show a panel if it's registered and related..
6296         
6297         if (!this.navId || !this.tabId || !state || is_was_active) {
6298             return;
6299         }
6300         
6301         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302         if (!tg) {
6303             return;
6304         }
6305         var pan = tg.getPanelByName(this.tabId);
6306         if (!pan) {
6307             return;
6308         }
6309         // if we can not flip to new panel - go back to old nav highlight..
6310         if (false == tg.showPanel(pan)) {
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 var onav = nv.getWasActive();
6314                 if (onav) {
6315                     onav.setActive(true, false, true);
6316                 }
6317             }
6318             
6319         }
6320         
6321         
6322         
6323     },
6324      // this should not be here...
6325     setDisabled : function(state)
6326     {
6327         this.disabled = state;
6328         if (!state ) {
6329             this.el.removeClass('disabled');
6330         } else if (!this.el.hasClass('disabled')) {
6331             this.el.addClass('disabled');
6332         }
6333         
6334     },
6335     
6336     /**
6337      * Fetch the element to display the tooltip on.
6338      * @return {Roo.Element} defaults to this.el
6339      */
6340     tooltipEl : function()
6341     {
6342         return this.el.select('' + this.tagtype + '', true).first();
6343     },
6344     
6345     scrollToElement : function(e)
6346     {
6347         var c = document.body;
6348         
6349         /*
6350          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351          */
6352         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353             c = document.documentElement;
6354         }
6355         
6356         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6357         
6358         if(!target){
6359             return;
6360         }
6361
6362         var o = target.calcOffsetsTo(c);
6363         
6364         var options = {
6365             target : target,
6366             value : o[1]
6367         };
6368         
6369         this.fireEvent('scrollto', this, options, e);
6370         
6371         Roo.get(c).scrollTo('top', options.value, true);
6372         
6373         return;
6374     }
6375 });
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * sidebar item
6382  *
6383  *  li
6384  *    <span> icon </span>
6385  *    <span> text </span>
6386  *    <span>badge </span>
6387  */
6388
6389 /**
6390  * @class Roo.bootstrap.NavSidebarItem
6391  * @extends Roo.bootstrap.NavItem
6392  * Bootstrap Navbar.NavSidebarItem class
6393  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394  * {Boolean} open is the menu open
6395  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397  * {String} buttonSize (sm|md|lg)the extra classes for the button
6398  * {Boolean} showArrow show arrow next to the text (default true)
6399  * @constructor
6400  * Create a new Navbar Button
6401  * @param {Object} config The config object
6402  */
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6405     this.addEvents({
6406         // raw events
6407         /**
6408          * @event click
6409          * The raw click event for the entire grid.
6410          * @param {Roo.EventObject} e
6411          */
6412         "click" : true,
6413          /**
6414             * @event changed
6415             * Fires when the active item active state changes
6416             * @param {Roo.bootstrap.NavSidebarItem} this
6417             * @param {boolean} state the new state
6418              
6419          */
6420         'changed': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6426     
6427     badgeWeight : 'default',
6428     
6429     open: false,
6430     
6431     buttonView : false,
6432     
6433     buttonWeight : 'default',
6434     
6435     buttonSize : 'md',
6436     
6437     showArrow : true,
6438     
6439     getAutoCreate : function(){
6440         
6441         
6442         var a = {
6443                 tag: 'a',
6444                 href : this.href || '#',
6445                 cls: '',
6446                 html : '',
6447                 cn : []
6448         };
6449         
6450         if(this.buttonView){
6451             a = {
6452                 tag: 'button',
6453                 href : this.href || '#',
6454                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6455                 html : this.html,
6456                 cn : []
6457             };
6458         }
6459         
6460         var cfg = {
6461             tag: 'li',
6462             cls: '',
6463             cn: [ a ]
6464         };
6465         
6466         if (this.active) {
6467             cfg.cls += ' active';
6468         }
6469         
6470         if (this.disabled) {
6471             cfg.cls += ' disabled';
6472         }
6473         if (this.open) {
6474             cfg.cls += ' open x-open';
6475         }
6476         // left icon..
6477         if (this.glyphicon || this.icon) {
6478             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6479             a.cn.push({ tag : 'i', cls : c }) ;
6480         }
6481         
6482         if(!this.buttonView){
6483             var span = {
6484                 tag: 'span',
6485                 html : this.html || ''
6486             };
6487
6488             a.cn.push(span);
6489             
6490         }
6491         
6492         if (this.badge !== '') {
6493             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6494         }
6495         
6496         if (this.menu) {
6497             
6498             if(this.showArrow){
6499                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6500             }
6501             
6502             a.cls += ' dropdown-toggle treeview' ;
6503         }
6504         
6505         return cfg;
6506     },
6507     
6508     initEvents : function()
6509     { 
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         if(this.badge !== ''){
6519             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6520         }
6521         
6522     },
6523     
6524     onClick : function(e)
6525     {
6526         if(this.disabled){
6527             e.preventDefault();
6528             return;
6529         }
6530         
6531         if(this.preventDefault){
6532             e.preventDefault();
6533         }
6534         
6535         this.fireEvent('click', this, e);
6536     },
6537     
6538     disable : function()
6539     {
6540         this.setDisabled(true);
6541     },
6542     
6543     enable : function()
6544     {
6545         this.setDisabled(false);
6546     },
6547     
6548     setDisabled : function(state)
6549     {
6550         if(this.disabled == state){
6551             return;
6552         }
6553         
6554         this.disabled = state;
6555         
6556         if (state) {
6557             this.el.addClass('disabled');
6558             return;
6559         }
6560         
6561         this.el.removeClass('disabled');
6562         
6563         return;
6564     },
6565     
6566     setActive : function(state)
6567     {
6568         if(this.active == state){
6569             return;
6570         }
6571         
6572         this.active = state;
6573         
6574         if (state) {
6575             this.el.addClass('active');
6576             return;
6577         }
6578         
6579         this.el.removeClass('active');
6580         
6581         return;
6582     },
6583     
6584     isActive: function () 
6585     {
6586         return this.active;
6587     },
6588     
6589     setBadge : function(str)
6590     {
6591         if(!this.badgeEl){
6592             return;
6593         }
6594         
6595         this.badgeEl.dom.innerHTML = str;
6596     }
6597     
6598    
6599      
6600  
6601 });
6602  
6603
6604  /*
6605  * - LGPL
6606  *
6607  * row
6608  * 
6609  */
6610
6611 /**
6612  * @class Roo.bootstrap.Row
6613  * @extends Roo.bootstrap.Component
6614  * Bootstrap Row class (contains columns...)
6615  * 
6616  * @constructor
6617  * Create a new Row
6618  * @param {Object} config The config object
6619  */
6620
6621 Roo.bootstrap.Row = function(config){
6622     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6623 };
6624
6625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6626     
6627     getAutoCreate : function(){
6628        return {
6629             cls: 'row clearfix'
6630        };
6631     }
6632     
6633     
6634 });
6635
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  * pagination
6642  * 
6643  */
6644
6645 /**
6646  * @class Roo.bootstrap.Pagination
6647  * @extends Roo.bootstrap.Component
6648  * Bootstrap Pagination class
6649  * @cfg {String} size xs | sm | md | lg
6650  * @cfg {Boolean} inverse false | true
6651  * 
6652  * @constructor
6653  * Create a new Pagination
6654  * @param {Object} config The config object
6655  */
6656
6657 Roo.bootstrap.Pagination = function(config){
6658     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6659 };
6660
6661 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6662     
6663     cls: false,
6664     size: false,
6665     inverse: false,
6666     
6667     getAutoCreate : function(){
6668         var cfg = {
6669             tag: 'ul',
6670                 cls: 'pagination'
6671         };
6672         if (this.inverse) {
6673             cfg.cls += ' inverse';
6674         }
6675         if (this.html) {
6676             cfg.html=this.html;
6677         }
6678         if (this.cls) {
6679             cfg.cls += " " + this.cls;
6680         }
6681         return cfg;
6682     }
6683    
6684 });
6685
6686  
6687
6688  /*
6689  * - LGPL
6690  *
6691  * Pagination item
6692  * 
6693  */
6694
6695
6696 /**
6697  * @class Roo.bootstrap.PaginationItem
6698  * @extends Roo.bootstrap.Component
6699  * Bootstrap PaginationItem class
6700  * @cfg {String} html text
6701  * @cfg {String} href the link
6702  * @cfg {Boolean} preventDefault (true | false) default true
6703  * @cfg {Boolean} active (true | false) default false
6704  * @cfg {Boolean} disabled default false
6705  * 
6706  * 
6707  * @constructor
6708  * Create a new PaginationItem
6709  * @param {Object} config The config object
6710  */
6711
6712
6713 Roo.bootstrap.PaginationItem = function(config){
6714     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6715     this.addEvents({
6716         // raw events
6717         /**
6718          * @event click
6719          * The raw click event for the entire grid.
6720          * @param {Roo.EventObject} e
6721          */
6722         "click" : true
6723     });
6724 };
6725
6726 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6727     
6728     href : false,
6729     html : false,
6730     preventDefault: true,
6731     active : false,
6732     cls : false,
6733     disabled: false,
6734     
6735     getAutoCreate : function(){
6736         var cfg= {
6737             tag: 'li',
6738             cn: [
6739                 {
6740                     tag : 'a',
6741                     href : this.href ? this.href : '#',
6742                     html : this.html ? this.html : ''
6743                 }
6744             ]
6745         };
6746         
6747         if(this.cls){
6748             cfg.cls = this.cls;
6749         }
6750         
6751         if(this.disabled){
6752             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753         }
6754         
6755         if(this.active){
6756             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6757         }
6758         
6759         return cfg;
6760     },
6761     
6762     initEvents: function() {
6763         
6764         this.el.on('click', this.onClick, this);
6765         
6766     },
6767     onClick : function(e)
6768     {
6769         Roo.log('PaginationItem on click ');
6770         if(this.preventDefault){
6771             e.preventDefault();
6772         }
6773         
6774         if(this.disabled){
6775             return;
6776         }
6777         
6778         this.fireEvent('click', this, e);
6779     }
6780    
6781 });
6782
6783  
6784
6785  /*
6786  * - LGPL
6787  *
6788  * slider
6789  * 
6790  */
6791
6792
6793 /**
6794  * @class Roo.bootstrap.Slider
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap Slider class
6797  *    
6798  * @constructor
6799  * Create a new Slider
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Slider = function(config){
6804     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6808     
6809     getAutoCreate : function(){
6810         
6811         var cfg = {
6812             tag: 'div',
6813             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814             cn: [
6815                 {
6816                     tag: 'a',
6817                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6818                 }
6819             ]
6820         };
6821         
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  /*
6828  * Based on:
6829  * Ext JS Library 1.1.1
6830  * Copyright(c) 2006-2007, Ext JS, LLC.
6831  *
6832  * Originally Released Under LGPL - original licence link has changed is not relivant.
6833  *
6834  * Fork - LGPL
6835  * <script type="text/javascript">
6836  */
6837  
6838
6839 /**
6840  * @class Roo.grid.ColumnModel
6841  * @extends Roo.util.Observable
6842  * This is the default implementation of a ColumnModel used by the Grid. It defines
6843  * the columns in the grid.
6844  * <br>Usage:<br>
6845  <pre><code>
6846  var colModel = new Roo.grid.ColumnModel([
6847         {header: "Ticker", width: 60, sortable: true, locked: true},
6848         {header: "Company Name", width: 150, sortable: true},
6849         {header: "Market Cap.", width: 100, sortable: true},
6850         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6851         {header: "Employees", width: 100, sortable: true, resizable: false}
6852  ]);
6853  </code></pre>
6854  * <p>
6855  
6856  * The config options listed for this class are options which may appear in each
6857  * individual column definition.
6858  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6859  * @constructor
6860  * @param {Object} config An Array of column config objects. See this class's
6861  * config objects for details.
6862 */
6863 Roo.grid.ColumnModel = function(config){
6864         /**
6865      * The config passed into the constructor
6866      */
6867     this.config = config;
6868     this.lookup = {};
6869
6870     // if no id, create one
6871     // if the column does not have a dataIndex mapping,
6872     // map it to the order it is in the config
6873     for(var i = 0, len = config.length; i < len; i++){
6874         var c = config[i];
6875         if(typeof c.dataIndex == "undefined"){
6876             c.dataIndex = i;
6877         }
6878         if(typeof c.renderer == "string"){
6879             c.renderer = Roo.util.Format[c.renderer];
6880         }
6881         if(typeof c.id == "undefined"){
6882             c.id = Roo.id();
6883         }
6884         if(c.editor && c.editor.xtype){
6885             c.editor  = Roo.factory(c.editor, Roo.grid);
6886         }
6887         if(c.editor && c.editor.isFormField){
6888             c.editor = new Roo.grid.GridEditor(c.editor);
6889         }
6890         this.lookup[c.id] = c;
6891     }
6892
6893     /**
6894      * The width of columns which have no width specified (defaults to 100)
6895      * @type Number
6896      */
6897     this.defaultWidth = 100;
6898
6899     /**
6900      * Default sortable of columns which have no sortable specified (defaults to false)
6901      * @type Boolean
6902      */
6903     this.defaultSortable = false;
6904
6905     this.addEvents({
6906         /**
6907              * @event widthchange
6908              * Fires when the width of a column changes.
6909              * @param {ColumnModel} this
6910              * @param {Number} columnIndex The column index
6911              * @param {Number} newWidth The new width
6912              */
6913             "widthchange": true,
6914         /**
6915              * @event headerchange
6916              * Fires when the text of a header changes.
6917              * @param {ColumnModel} this
6918              * @param {Number} columnIndex The column index
6919              * @param {Number} newText The new header text
6920              */
6921             "headerchange": true,
6922         /**
6923              * @event hiddenchange
6924              * Fires when a column is hidden or "unhidden".
6925              * @param {ColumnModel} this
6926              * @param {Number} columnIndex The column index
6927              * @param {Boolean} hidden true if hidden, false otherwise
6928              */
6929             "hiddenchange": true,
6930             /**
6931          * @event columnmoved
6932          * Fires when a column is moved.
6933          * @param {ColumnModel} this
6934          * @param {Number} oldIndex
6935          * @param {Number} newIndex
6936          */
6937         "columnmoved" : true,
6938         /**
6939          * @event columlockchange
6940          * Fires when a column's locked state is changed
6941          * @param {ColumnModel} this
6942          * @param {Number} colIndex
6943          * @param {Boolean} locked true if locked
6944          */
6945         "columnlockchange" : true
6946     });
6947     Roo.grid.ColumnModel.superclass.constructor.call(this);
6948 };
6949 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6950     /**
6951      * @cfg {String} header The header text to display in the Grid view.
6952      */
6953     /**
6954      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6955      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6956      * specified, the column's index is used as an index into the Record's data Array.
6957      */
6958     /**
6959      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6960      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6961      */
6962     /**
6963      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6964      * Defaults to the value of the {@link #defaultSortable} property.
6965      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6966      */
6967     /**
6968      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6972      */
6973     /**
6974      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6975      */
6976     /**
6977      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6978      */
6979     /**
6980      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6981      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6982      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6983      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6984      */
6985        /**
6986      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6987      */
6988     /**
6989      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6993      */
6994     /**
6995      * @cfg {String} cursor (Optional)
6996      */
6997     /**
6998      * @cfg {String} tooltip (Optional)
6999      */
7000     /**
7001      * @cfg {Number} xs (Optional)
7002      */
7003     /**
7004      * @cfg {Number} sm (Optional)
7005      */
7006     /**
7007      * @cfg {Number} md (Optional)
7008      */
7009     /**
7010      * @cfg {Number} lg (Optional)
7011      */
7012     /**
7013      * Returns the id of the column at the specified index.
7014      * @param {Number} index The column index
7015      * @return {String} the id
7016      */
7017     getColumnId : function(index){
7018         return this.config[index].id;
7019     },
7020
7021     /**
7022      * Returns the column for a specified id.
7023      * @param {String} id The column id
7024      * @return {Object} the column
7025      */
7026     getColumnById : function(id){
7027         return this.lookup[id];
7028     },
7029
7030     
7031     /**
7032      * Returns the column for a specified dataIndex.
7033      * @param {String} dataIndex The column dataIndex
7034      * @return {Object|Boolean} the column or false if not found
7035      */
7036     getColumnByDataIndex: function(dataIndex){
7037         var index = this.findColumnIndex(dataIndex);
7038         return index > -1 ? this.config[index] : false;
7039     },
7040     
7041     /**
7042      * Returns the index for a specified column id.
7043      * @param {String} id The column id
7044      * @return {Number} the index, or -1 if not found
7045      */
7046     getIndexById : function(id){
7047         for(var i = 0, len = this.config.length; i < len; i++){
7048             if(this.config[i].id == id){
7049                 return i;
7050             }
7051         }
7052         return -1;
7053     },
7054     
7055     /**
7056      * Returns the index for a specified column dataIndex.
7057      * @param {String} dataIndex The column dataIndex
7058      * @return {Number} the index, or -1 if not found
7059      */
7060     
7061     findColumnIndex : function(dataIndex){
7062         for(var i = 0, len = this.config.length; i < len; i++){
7063             if(this.config[i].dataIndex == dataIndex){
7064                 return i;
7065             }
7066         }
7067         return -1;
7068     },
7069     
7070     
7071     moveColumn : function(oldIndex, newIndex){
7072         var c = this.config[oldIndex];
7073         this.config.splice(oldIndex, 1);
7074         this.config.splice(newIndex, 0, c);
7075         this.dataMap = null;
7076         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7077     },
7078
7079     isLocked : function(colIndex){
7080         return this.config[colIndex].locked === true;
7081     },
7082
7083     setLocked : function(colIndex, value, suppressEvent){
7084         if(this.isLocked(colIndex) == value){
7085             return;
7086         }
7087         this.config[colIndex].locked = value;
7088         if(!suppressEvent){
7089             this.fireEvent("columnlockchange", this, colIndex, value);
7090         }
7091     },
7092
7093     getTotalLockedWidth : function(){
7094         var totalWidth = 0;
7095         for(var i = 0; i < this.config.length; i++){
7096             if(this.isLocked(i) && !this.isHidden(i)){
7097                 this.totalWidth += this.getColumnWidth(i);
7098             }
7099         }
7100         return totalWidth;
7101     },
7102
7103     getLockedCount : function(){
7104         for(var i = 0, len = this.config.length; i < len; i++){
7105             if(!this.isLocked(i)){
7106                 return i;
7107             }
7108         }
7109         
7110         return this.config.length;
7111     },
7112
7113     /**
7114      * Returns the number of columns.
7115      * @return {Number}
7116      */
7117     getColumnCount : function(visibleOnly){
7118         if(visibleOnly === true){
7119             var c = 0;
7120             for(var i = 0, len = this.config.length; i < len; i++){
7121                 if(!this.isHidden(i)){
7122                     c++;
7123                 }
7124             }
7125             return c;
7126         }
7127         return this.config.length;
7128     },
7129
7130     /**
7131      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7132      * @param {Function} fn
7133      * @param {Object} scope (optional)
7134      * @return {Array} result
7135      */
7136     getColumnsBy : function(fn, scope){
7137         var r = [];
7138         for(var i = 0, len = this.config.length; i < len; i++){
7139             var c = this.config[i];
7140             if(fn.call(scope||this, c, i) === true){
7141                 r[r.length] = c;
7142             }
7143         }
7144         return r;
7145     },
7146
7147     /**
7148      * Returns true if the specified column is sortable.
7149      * @param {Number} col The column index
7150      * @return {Boolean}
7151      */
7152     isSortable : function(col){
7153         if(typeof this.config[col].sortable == "undefined"){
7154             return this.defaultSortable;
7155         }
7156         return this.config[col].sortable;
7157     },
7158
7159     /**
7160      * Returns the rendering (formatting) function defined for the column.
7161      * @param {Number} col The column index.
7162      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7163      */
7164     getRenderer : function(col){
7165         if(!this.config[col].renderer){
7166             return Roo.grid.ColumnModel.defaultRenderer;
7167         }
7168         return this.config[col].renderer;
7169     },
7170
7171     /**
7172      * Sets the rendering (formatting) function for a column.
7173      * @param {Number} col The column index
7174      * @param {Function} fn The function to use to process the cell's raw data
7175      * to return HTML markup for the grid view. The render function is called with
7176      * the following parameters:<ul>
7177      * <li>Data value.</li>
7178      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7179      * <li>css A CSS style string to apply to the table cell.</li>
7180      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7181      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7182      * <li>Row index</li>
7183      * <li>Column index</li>
7184      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7185      */
7186     setRenderer : function(col, fn){
7187         this.config[col].renderer = fn;
7188     },
7189
7190     /**
7191      * Returns the width for the specified column.
7192      * @param {Number} col The column index
7193      * @return {Number}
7194      */
7195     getColumnWidth : function(col){
7196         return this.config[col].width * 1 || this.defaultWidth;
7197     },
7198
7199     /**
7200      * Sets the width for a column.
7201      * @param {Number} col The column index
7202      * @param {Number} width The new width
7203      */
7204     setColumnWidth : function(col, width, suppressEvent){
7205         this.config[col].width = width;
7206         this.totalWidth = null;
7207         if(!suppressEvent){
7208              this.fireEvent("widthchange", this, col, width);
7209         }
7210     },
7211
7212     /**
7213      * Returns the total width of all columns.
7214      * @param {Boolean} includeHidden True to include hidden column widths
7215      * @return {Number}
7216      */
7217     getTotalWidth : function(includeHidden){
7218         if(!this.totalWidth){
7219             this.totalWidth = 0;
7220             for(var i = 0, len = this.config.length; i < len; i++){
7221                 if(includeHidden || !this.isHidden(i)){
7222                     this.totalWidth += this.getColumnWidth(i);
7223                 }
7224             }
7225         }
7226         return this.totalWidth;
7227     },
7228
7229     /**
7230      * Returns the header for the specified column.
7231      * @param {Number} col The column index
7232      * @return {String}
7233      */
7234     getColumnHeader : function(col){
7235         return this.config[col].header;
7236     },
7237
7238     /**
7239      * Sets the header for a column.
7240      * @param {Number} col The column index
7241      * @param {String} header The new header
7242      */
7243     setColumnHeader : function(col, header){
7244         this.config[col].header = header;
7245         this.fireEvent("headerchange", this, col, header);
7246     },
7247
7248     /**
7249      * Returns the tooltip for the specified column.
7250      * @param {Number} col The column index
7251      * @return {String}
7252      */
7253     getColumnTooltip : function(col){
7254             return this.config[col].tooltip;
7255     },
7256     /**
7257      * Sets the tooltip for a column.
7258      * @param {Number} col The column index
7259      * @param {String} tooltip The new tooltip
7260      */
7261     setColumnTooltip : function(col, tooltip){
7262             this.config[col].tooltip = tooltip;
7263     },
7264
7265     /**
7266      * Returns the dataIndex for the specified column.
7267      * @param {Number} col The column index
7268      * @return {Number}
7269      */
7270     getDataIndex : function(col){
7271         return this.config[col].dataIndex;
7272     },
7273
7274     /**
7275      * Sets the dataIndex for a column.
7276      * @param {Number} col The column index
7277      * @param {Number} dataIndex The new dataIndex
7278      */
7279     setDataIndex : function(col, dataIndex){
7280         this.config[col].dataIndex = dataIndex;
7281     },
7282
7283     
7284     
7285     /**
7286      * Returns true if the cell is editable.
7287      * @param {Number} colIndex The column index
7288      * @param {Number} rowIndex The row index - this is nto actually used..?
7289      * @return {Boolean}
7290      */
7291     isCellEditable : function(colIndex, rowIndex){
7292         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293     },
7294
7295     /**
7296      * Returns the editor defined for the cell/column.
7297      * return false or null to disable editing.
7298      * @param {Number} colIndex The column index
7299      * @param {Number} rowIndex The row index
7300      * @return {Object}
7301      */
7302     getCellEditor : function(colIndex, rowIndex){
7303         return this.config[colIndex].editor;
7304     },
7305
7306     /**
7307      * Sets if a column is editable.
7308      * @param {Number} col The column index
7309      * @param {Boolean} editable True if the column is editable
7310      */
7311     setEditable : function(col, editable){
7312         this.config[col].editable = editable;
7313     },
7314
7315
7316     /**
7317      * Returns true if the column is hidden.
7318      * @param {Number} colIndex The column index
7319      * @return {Boolean}
7320      */
7321     isHidden : function(colIndex){
7322         return this.config[colIndex].hidden;
7323     },
7324
7325
7326     /**
7327      * Returns true if the column width cannot be changed
7328      */
7329     isFixed : function(colIndex){
7330         return this.config[colIndex].fixed;
7331     },
7332
7333     /**
7334      * Returns true if the column can be resized
7335      * @return {Boolean}
7336      */
7337     isResizable : function(colIndex){
7338         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7339     },
7340     /**
7341      * Sets if a column is hidden.
7342      * @param {Number} colIndex The column index
7343      * @param {Boolean} hidden True if the column is hidden
7344      */
7345     setHidden : function(colIndex, hidden){
7346         this.config[colIndex].hidden = hidden;
7347         this.totalWidth = null;
7348         this.fireEvent("hiddenchange", this, colIndex, hidden);
7349     },
7350
7351     /**
7352      * Sets the editor for a column.
7353      * @param {Number} col The column index
7354      * @param {Object} editor The editor object
7355      */
7356     setEditor : function(col, editor){
7357         this.config[col].editor = editor;
7358     }
7359 });
7360
7361 Roo.grid.ColumnModel.defaultRenderer = function(value)
7362 {
7363     if(typeof value == "object") {
7364         return value;
7365     }
7366         if(typeof value == "string" && value.length < 1){
7367             return "&#160;";
7368         }
7369     
7370         return String.format("{0}", value);
7371 };
7372
7373 // Alias for backwards compatibility
7374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7375 /*
7376  * Based on:
7377  * Ext JS Library 1.1.1
7378  * Copyright(c) 2006-2007, Ext JS, LLC.
7379  *
7380  * Originally Released Under LGPL - original licence link has changed is not relivant.
7381  *
7382  * Fork - LGPL
7383  * <script type="text/javascript">
7384  */
7385  
7386 /**
7387  * @class Roo.LoadMask
7388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7391  * element's UpdateManager load indicator and will be destroyed after the initial load.
7392  * @constructor
7393  * Create a new LoadMask
7394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7395  * @param {Object} config The config object
7396  */
7397 Roo.LoadMask = function(el, config){
7398     this.el = Roo.get(el);
7399     Roo.apply(this, config);
7400     if(this.store){
7401         this.store.on('beforeload', this.onBeforeLoad, this);
7402         this.store.on('load', this.onLoad, this);
7403         this.store.on('loadexception', this.onLoadException, this);
7404         this.removeMask = false;
7405     }else{
7406         var um = this.el.getUpdateManager();
7407         um.showLoadIndicator = false; // disable the default indicator
7408         um.on('beforeupdate', this.onBeforeLoad, this);
7409         um.on('update', this.onLoad, this);
7410         um.on('failure', this.onLoad, this);
7411         this.removeMask = true;
7412     }
7413 };
7414
7415 Roo.LoadMask.prototype = {
7416     /**
7417      * @cfg {Boolean} removeMask
7418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7420      */
7421     /**
7422      * @cfg {String} msg
7423      * The text to display in a centered loading message box (defaults to 'Loading...')
7424      */
7425     msg : 'Loading...',
7426     /**
7427      * @cfg {String} msgCls
7428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7429      */
7430     msgCls : 'x-mask-loading',
7431
7432     /**
7433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7434      * @type Boolean
7435      */
7436     disabled: false,
7437
7438     /**
7439      * Disables the mask to prevent it from being displayed
7440      */
7441     disable : function(){
7442        this.disabled = true;
7443     },
7444
7445     /**
7446      * Enables the mask so that it can be displayed
7447      */
7448     enable : function(){
7449         this.disabled = false;
7450     },
7451     
7452     onLoadException : function()
7453     {
7454         Roo.log(arguments);
7455         
7456         if (typeof(arguments[3]) != 'undefined') {
7457             Roo.MessageBox.alert("Error loading",arguments[3]);
7458         } 
7459         /*
7460         try {
7461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7463             }   
7464         } catch(e) {
7465             
7466         }
7467         */
7468     
7469         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7470     },
7471     // private
7472     onLoad : function()
7473     {
7474         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475     },
7476
7477     // private
7478     onBeforeLoad : function(){
7479         if(!this.disabled){
7480             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7481         }
7482     },
7483
7484     // private
7485     destroy : function(){
7486         if(this.store){
7487             this.store.un('beforeload', this.onBeforeLoad, this);
7488             this.store.un('load', this.onLoad, this);
7489             this.store.un('loadexception', this.onLoadException, this);
7490         }else{
7491             var um = this.el.getUpdateManager();
7492             um.un('beforeupdate', this.onBeforeLoad, this);
7493             um.un('update', this.onLoad, this);
7494             um.un('failure', this.onLoad, this);
7495         }
7496     }
7497 };/*
7498  * - LGPL
7499  *
7500  * table
7501  * 
7502  */
7503
7504 /**
7505  * @class Roo.bootstrap.Table
7506  * @extends Roo.bootstrap.Component
7507  * Bootstrap Table class
7508  * @cfg {String} cls table class
7509  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7510  * @cfg {String} bgcolor Specifies the background color for a table
7511  * @cfg {Number} border Specifies whether the table cells should have borders or not
7512  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7513  * @cfg {Number} cellspacing Specifies the space between cells
7514  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7515  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7516  * @cfg {String} sortable Specifies that the table should be sortable
7517  * @cfg {String} summary Specifies a summary of the content of a table
7518  * @cfg {Number} width Specifies the width of a table
7519  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7520  * 
7521  * @cfg {boolean} striped Should the rows be alternative striped
7522  * @cfg {boolean} bordered Add borders to the table
7523  * @cfg {boolean} hover Add hover highlighting
7524  * @cfg {boolean} condensed Format condensed
7525  * @cfg {boolean} responsive Format condensed
7526  * @cfg {Boolean} loadMask (true|false) default false
7527  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7528  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7529  * @cfg {Boolean} rowSelection (true|false) default false
7530  * @cfg {Boolean} cellSelection (true|false) default false
7531  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7533  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7534  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7535  
7536  * 
7537  * @constructor
7538  * Create a new Table
7539  * @param {Object} config The config object
7540  */
7541
7542 Roo.bootstrap.Table = function(config){
7543     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7544     
7545   
7546     
7547     // BC...
7548     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7549     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7550     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7551     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7552     
7553     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7554     if (this.sm) {
7555         this.sm.grid = this;
7556         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7557         this.sm = this.selModel;
7558         this.sm.xmodule = this.xmodule || false;
7559     }
7560     
7561     if (this.cm && typeof(this.cm.config) == 'undefined') {
7562         this.colModel = new Roo.grid.ColumnModel(this.cm);
7563         this.cm = this.colModel;
7564         this.cm.xmodule = this.xmodule || false;
7565     }
7566     if (this.store) {
7567         this.store= Roo.factory(this.store, Roo.data);
7568         this.ds = this.store;
7569         this.ds.xmodule = this.xmodule || false;
7570          
7571     }
7572     if (this.footer && this.store) {
7573         this.footer.dataSource = this.ds;
7574         this.footer = Roo.factory(this.footer);
7575     }
7576     
7577     /** @private */
7578     this.addEvents({
7579         /**
7580          * @event cellclick
7581          * Fires when a cell is clicked
7582          * @param {Roo.bootstrap.Table} this
7583          * @param {Roo.Element} el
7584          * @param {Number} rowIndex
7585          * @param {Number} columnIndex
7586          * @param {Roo.EventObject} e
7587          */
7588         "cellclick" : true,
7589         /**
7590          * @event celldblclick
7591          * Fires when a cell is double clicked
7592          * @param {Roo.bootstrap.Table} this
7593          * @param {Roo.Element} el
7594          * @param {Number} rowIndex
7595          * @param {Number} columnIndex
7596          * @param {Roo.EventObject} e
7597          */
7598         "celldblclick" : true,
7599         /**
7600          * @event rowclick
7601          * Fires when a row is clicked
7602          * @param {Roo.bootstrap.Table} this
7603          * @param {Roo.Element} el
7604          * @param {Number} rowIndex
7605          * @param {Roo.EventObject} e
7606          */
7607         "rowclick" : true,
7608         /**
7609          * @event rowdblclick
7610          * Fires when a row is double clicked
7611          * @param {Roo.bootstrap.Table} this
7612          * @param {Roo.Element} el
7613          * @param {Number} rowIndex
7614          * @param {Roo.EventObject} e
7615          */
7616         "rowdblclick" : true,
7617         /**
7618          * @event mouseover
7619          * Fires when a mouseover occur
7620          * @param {Roo.bootstrap.Table} this
7621          * @param {Roo.Element} el
7622          * @param {Number} rowIndex
7623          * @param {Number} columnIndex
7624          * @param {Roo.EventObject} e
7625          */
7626         "mouseover" : true,
7627         /**
7628          * @event mouseout
7629          * Fires when a mouseout occur
7630          * @param {Roo.bootstrap.Table} this
7631          * @param {Roo.Element} el
7632          * @param {Number} rowIndex
7633          * @param {Number} columnIndex
7634          * @param {Roo.EventObject} e
7635          */
7636         "mouseout" : true,
7637         /**
7638          * @event rowclass
7639          * Fires when a row is rendered, so you can change add a style to it.
7640          * @param {Roo.bootstrap.Table} this
7641          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7642          */
7643         'rowclass' : true,
7644           /**
7645          * @event rowsrendered
7646          * Fires when all the  rows have been rendered
7647          * @param {Roo.bootstrap.Table} this
7648          */
7649         'rowsrendered' : true,
7650         /**
7651          * @event contextmenu
7652          * The raw contextmenu event for the entire grid.
7653          * @param {Roo.EventObject} e
7654          */
7655         "contextmenu" : true,
7656         /**
7657          * @event rowcontextmenu
7658          * Fires when a row is right clicked
7659          * @param {Roo.bootstrap.Table} this
7660          * @param {Number} rowIndex
7661          * @param {Roo.EventObject} e
7662          */
7663         "rowcontextmenu" : true,
7664         /**
7665          * @event cellcontextmenu
7666          * Fires when a cell is right clicked
7667          * @param {Roo.bootstrap.Table} this
7668          * @param {Number} rowIndex
7669          * @param {Number} cellIndex
7670          * @param {Roo.EventObject} e
7671          */
7672          "cellcontextmenu" : true,
7673          /**
7674          * @event headercontextmenu
7675          * Fires when a header is right clicked
7676          * @param {Roo.bootstrap.Table} this
7677          * @param {Number} columnIndex
7678          * @param {Roo.EventObject} e
7679          */
7680         "headercontextmenu" : true
7681     });
7682 };
7683
7684 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7685     
7686     cls: false,
7687     align: false,
7688     bgcolor: false,
7689     border: false,
7690     cellpadding: false,
7691     cellspacing: false,
7692     frame: false,
7693     rules: false,
7694     sortable: false,
7695     summary: false,
7696     width: false,
7697     striped : false,
7698     scrollBody : false,
7699     bordered: false,
7700     hover:  false,
7701     condensed : false,
7702     responsive : false,
7703     sm : false,
7704     cm : false,
7705     store : false,
7706     loadMask : false,
7707     footerShow : true,
7708     headerShow : true,
7709   
7710     rowSelection : false,
7711     cellSelection : false,
7712     layout : false,
7713     
7714     // Roo.Element - the tbody
7715     mainBody: false,
7716     // Roo.Element - thead element
7717     mainHead: false,
7718     
7719     container: false, // used by gridpanel...
7720     
7721     lazyLoad : false,
7722     
7723     CSS : Roo.util.CSS,
7724     
7725     auto_hide_footer : false,
7726     
7727     getAutoCreate : function()
7728     {
7729         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7730         
7731         cfg = {
7732             tag: 'table',
7733             cls : 'table',
7734             cn : []
7735         };
7736         if (this.scrollBody) {
7737             cfg.cls += ' table-body-fixed';
7738         }    
7739         if (this.striped) {
7740             cfg.cls += ' table-striped';
7741         }
7742         
7743         if (this.hover) {
7744             cfg.cls += ' table-hover';
7745         }
7746         if (this.bordered) {
7747             cfg.cls += ' table-bordered';
7748         }
7749         if (this.condensed) {
7750             cfg.cls += ' table-condensed';
7751         }
7752         if (this.responsive) {
7753             cfg.cls += ' table-responsive';
7754         }
7755         
7756         if (this.cls) {
7757             cfg.cls+=  ' ' +this.cls;
7758         }
7759         
7760         // this lot should be simplifed...
7761         var _t = this;
7762         var cp = [
7763             'align',
7764             'bgcolor',
7765             'border',
7766             'cellpadding',
7767             'cellspacing',
7768             'frame',
7769             'rules',
7770             'sortable',
7771             'summary',
7772             'width'
7773         ].forEach(function(k) {
7774             if (_t[k]) {
7775                 cfg[k] = _t[k];
7776             }
7777         });
7778         
7779         
7780         if (this.layout) {
7781             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7782         }
7783         
7784         if(this.store || this.cm){
7785             if(this.headerShow){
7786                 cfg.cn.push(this.renderHeader());
7787             }
7788             
7789             cfg.cn.push(this.renderBody());
7790             
7791             if(this.footerShow){
7792                 cfg.cn.push(this.renderFooter());
7793             }
7794             // where does this come from?
7795             //cfg.cls+=  ' TableGrid';
7796         }
7797         
7798         return { cn : [ cfg ] };
7799     },
7800     
7801     initEvents : function()
7802     {   
7803         if(!this.store || !this.cm){
7804             return;
7805         }
7806         if (this.selModel) {
7807             this.selModel.initEvents();
7808         }
7809         
7810         
7811         //Roo.log('initEvents with ds!!!!');
7812         
7813         this.mainBody = this.el.select('tbody', true).first();
7814         this.mainHead = this.el.select('thead', true).first();
7815         this.mainFoot = this.el.select('tfoot', true).first();
7816         
7817         
7818         
7819         var _this = this;
7820         
7821         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7822             e.on('click', _this.sort, _this);
7823         });
7824         
7825         this.mainBody.on("click", this.onClick, this);
7826         this.mainBody.on("dblclick", this.onDblClick, this);
7827         
7828         // why is this done????? = it breaks dialogs??
7829         //this.parent().el.setStyle('position', 'relative');
7830         
7831         
7832         if (this.footer) {
7833             this.footer.parentId = this.id;
7834             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7835             
7836             if(this.lazyLoad){
7837                 this.el.select('tfoot tr td').first().addClass('hide');
7838             }
7839         } 
7840         
7841         if(this.loadMask) {
7842             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7843         }
7844         
7845         this.store.on('load', this.onLoad, this);
7846         this.store.on('beforeload', this.onBeforeLoad, this);
7847         this.store.on('update', this.onUpdate, this);
7848         this.store.on('add', this.onAdd, this);
7849         this.store.on("clear", this.clear, this);
7850         
7851         this.el.on("contextmenu", this.onContextMenu, this);
7852         
7853         this.mainBody.on('scroll', this.onBodyScroll, this);
7854         
7855         this.cm.on("headerchange", this.onHeaderChange, this);
7856         
7857         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858         
7859     },
7860     
7861     onContextMenu : function(e, t)
7862     {
7863         this.processEvent("contextmenu", e);
7864     },
7865     
7866     processEvent : function(name, e)
7867     {
7868         if (name != 'touchstart' ) {
7869             this.fireEvent(name, e);    
7870         }
7871         
7872         var t = e.getTarget();
7873         
7874         var cell = Roo.get(t);
7875         
7876         if(!cell){
7877             return;
7878         }
7879         
7880         if(cell.findParent('tfoot', false, true)){
7881             return;
7882         }
7883         
7884         if(cell.findParent('thead', false, true)){
7885             
7886             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7887                 cell = Roo.get(t).findParent('th', false, true);
7888                 if (!cell) {
7889                     Roo.log("failed to find th in thead?");
7890                     Roo.log(e.getTarget());
7891                     return;
7892                 }
7893             }
7894             
7895             var cellIndex = cell.dom.cellIndex;
7896             
7897             var ename = name == 'touchstart' ? 'click' : name;
7898             this.fireEvent("header" + ename, this, cellIndex, e);
7899             
7900             return;
7901         }
7902         
7903         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7904             cell = Roo.get(t).findParent('td', false, true);
7905             if (!cell) {
7906                 Roo.log("failed to find th in tbody?");
7907                 Roo.log(e.getTarget());
7908                 return;
7909             }
7910         }
7911         
7912         var row = cell.findParent('tr', false, true);
7913         var cellIndex = cell.dom.cellIndex;
7914         var rowIndex = row.dom.rowIndex - 1;
7915         
7916         if(row !== false){
7917             
7918             this.fireEvent("row" + name, this, rowIndex, e);
7919             
7920             if(cell !== false){
7921             
7922                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7923             }
7924         }
7925         
7926     },
7927     
7928     onMouseover : function(e, el)
7929     {
7930         var cell = Roo.get(el);
7931         
7932         if(!cell){
7933             return;
7934         }
7935         
7936         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7937             cell = cell.findParent('td', false, true);
7938         }
7939         
7940         var row = cell.findParent('tr', false, true);
7941         var cellIndex = cell.dom.cellIndex;
7942         var rowIndex = row.dom.rowIndex - 1; // start from 0
7943         
7944         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945         
7946     },
7947     
7948     onMouseout : function(e, el)
7949     {
7950         var cell = Roo.get(el);
7951         
7952         if(!cell){
7953             return;
7954         }
7955         
7956         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957             cell = cell.findParent('td', false, true);
7958         }
7959         
7960         var row = cell.findParent('tr', false, true);
7961         var cellIndex = cell.dom.cellIndex;
7962         var rowIndex = row.dom.rowIndex - 1; // start from 0
7963         
7964         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965         
7966     },
7967     
7968     onClick : function(e, el)
7969     {
7970         var cell = Roo.get(el);
7971         
7972         if(!cell || (!this.cellSelection && !this.rowSelection)){
7973             return;
7974         }
7975         
7976         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7977             cell = cell.findParent('td', false, true);
7978         }
7979         
7980         if(!cell || typeof(cell) == 'undefined'){
7981             return;
7982         }
7983         
7984         var row = cell.findParent('tr', false, true);
7985         
7986         if(!row || typeof(row) == 'undefined'){
7987             return;
7988         }
7989         
7990         var cellIndex = cell.dom.cellIndex;
7991         var rowIndex = this.getRowIndex(row);
7992         
7993         // why??? - should these not be based on SelectionModel?
7994         if(this.cellSelection){
7995             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7996         }
7997         
7998         if(this.rowSelection){
7999             this.fireEvent('rowclick', this, row, rowIndex, e);
8000         }
8001         
8002         
8003     },
8004         
8005     onDblClick : function(e,el)
8006     {
8007         var cell = Roo.get(el);
8008         
8009         if(!cell || (!this.cellSelection && !this.rowSelection)){
8010             return;
8011         }
8012         
8013         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8014             cell = cell.findParent('td', false, true);
8015         }
8016         
8017         if(!cell || typeof(cell) == 'undefined'){
8018             return;
8019         }
8020         
8021         var row = cell.findParent('tr', false, true);
8022         
8023         if(!row || typeof(row) == 'undefined'){
8024             return;
8025         }
8026         
8027         var cellIndex = cell.dom.cellIndex;
8028         var rowIndex = this.getRowIndex(row);
8029         
8030         if(this.cellSelection){
8031             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8032         }
8033         
8034         if(this.rowSelection){
8035             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036         }
8037     },
8038     
8039     sort : function(e,el)
8040     {
8041         var col = Roo.get(el);
8042         
8043         if(!col.hasClass('sortable')){
8044             return;
8045         }
8046         
8047         var sort = col.attr('sort');
8048         var dir = 'ASC';
8049         
8050         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051             dir = 'DESC';
8052         }
8053         
8054         this.store.sortInfo = {field : sort, direction : dir};
8055         
8056         if (this.footer) {
8057             Roo.log("calling footer first");
8058             this.footer.onClick('first');
8059         } else {
8060         
8061             this.store.load({ params : { start : 0 } });
8062         }
8063     },
8064     
8065     renderHeader : function()
8066     {
8067         var header = {
8068             tag: 'thead',
8069             cn : []
8070         };
8071         
8072         var cm = this.cm;
8073         this.totalWidth = 0;
8074         
8075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076             
8077             var config = cm.config[i];
8078             
8079             var c = {
8080                 tag: 'th',
8081                 cls : 'x-hcol-' + i,
8082                 style : '',
8083                 html: cm.getColumnHeader(i)
8084             };
8085             
8086             var hh = '';
8087             
8088             if(typeof(config.sortable) != 'undefined' && config.sortable){
8089                 c.cls = 'sortable';
8090                 c.html = '<i class="glyphicon"></i>' + c.html;
8091             }
8092             
8093             // could use BS4 hidden-..-down 
8094             
8095             if(typeof(config.lgHeader) != 'undefined'){
8096                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8097             }
8098             
8099             if(typeof(config.mdHeader) != 'undefined'){
8100                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8101             }
8102             
8103             if(typeof(config.smHeader) != 'undefined'){
8104                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8105             }
8106             
8107             if(typeof(config.xsHeader) != 'undefined'){
8108                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8109             }
8110             
8111             if(hh.length){
8112                 c.html = hh;
8113             }
8114             
8115             if(typeof(config.tooltip) != 'undefined'){
8116                 c.tooltip = config.tooltip;
8117             }
8118             
8119             if(typeof(config.colspan) != 'undefined'){
8120                 c.colspan = config.colspan;
8121             }
8122             
8123             if(typeof(config.hidden) != 'undefined' && config.hidden){
8124                 c.style += ' display:none;';
8125             }
8126             
8127             if(typeof(config.dataIndex) != 'undefined'){
8128                 c.sort = config.dataIndex;
8129             }
8130             
8131            
8132             
8133             if(typeof(config.align) != 'undefined' && config.align.length){
8134                 c.style += ' text-align:' + config.align + ';';
8135             }
8136             
8137             if(typeof(config.width) != 'undefined'){
8138                 c.style += ' width:' + config.width + 'px;';
8139                 this.totalWidth += config.width;
8140             } else {
8141                 this.totalWidth += 100; // assume minimum of 100 per column?
8142             }
8143             
8144             if(typeof(config.cls) != 'undefined'){
8145                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8146             }
8147             
8148             ['xs','sm','md','lg'].map(function(size){
8149                 
8150                 if(typeof(config[size]) == 'undefined'){
8151                     return;
8152                 }
8153                  
8154                 if (!config[size]) { // 0 = hidden
8155                     // BS 4 '0' is treated as hide that column and below.
8156                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157                     return;
8158                 }
8159                 
8160                 c.cls += ' col-' + size + '-' + config[size] + (
8161                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8162                 );
8163                 
8164                 
8165             });
8166             
8167             header.cn.push(c)
8168         }
8169         
8170         return header;
8171     },
8172     
8173     renderBody : function()
8174     {
8175         var body = {
8176             tag: 'tbody',
8177             cn : [
8178                 {
8179                     tag: 'tr',
8180                     cn : [
8181                         {
8182                             tag : 'td',
8183                             colspan :  this.cm.getColumnCount()
8184                         }
8185                     ]
8186                 }
8187             ]
8188         };
8189         
8190         return body;
8191     },
8192     
8193     renderFooter : function()
8194     {
8195         var footer = {
8196             tag: 'tfoot',
8197             cn : [
8198                 {
8199                     tag: 'tr',
8200                     cn : [
8201                         {
8202                             tag : 'td',
8203                             colspan :  this.cm.getColumnCount()
8204                         }
8205                     ]
8206                 }
8207             ]
8208         };
8209         
8210         return footer;
8211     },
8212     
8213     
8214     
8215     onLoad : function()
8216     {
8217 //        Roo.log('ds onload');
8218         this.clear();
8219         
8220         var _this = this;
8221         var cm = this.cm;
8222         var ds = this.store;
8223         
8224         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8226             if (_this.store.sortInfo) {
8227                     
8228                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8229                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8230                 }
8231                 
8232                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8233                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8234                 }
8235             }
8236         });
8237         
8238         var tbody =  this.mainBody;
8239               
8240         if(ds.getCount() > 0){
8241             ds.data.each(function(d,rowIndex){
8242                 var row =  this.renderRow(cm, ds, rowIndex);
8243                 
8244                 tbody.createChild(row);
8245                 
8246                 var _this = this;
8247                 
8248                 if(row.cellObjects.length){
8249                     Roo.each(row.cellObjects, function(r){
8250                         _this.renderCellObject(r);
8251                     })
8252                 }
8253                 
8254             }, this);
8255         }
8256         
8257         var tfoot = this.el.select('tfoot', true).first();
8258         
8259         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8260             
8261             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8262             
8263             var total = this.ds.getTotalCount();
8264             
8265             if(this.footer.pageSize < total){
8266                 this.mainFoot.show();
8267             }
8268         }
8269         
8270         Roo.each(this.el.select('tbody td', true).elements, function(e){
8271             e.on('mouseover', _this.onMouseover, _this);
8272         });
8273         
8274         Roo.each(this.el.select('tbody td', true).elements, function(e){
8275             e.on('mouseout', _this.onMouseout, _this);
8276         });
8277         this.fireEvent('rowsrendered', this);
8278         
8279         this.autoSize();
8280     },
8281     
8282     
8283     onUpdate : function(ds,record)
8284     {
8285         this.refreshRow(record);
8286         this.autoSize();
8287     },
8288     
8289     onRemove : function(ds, record, index, isUpdate){
8290         if(isUpdate !== true){
8291             this.fireEvent("beforerowremoved", this, index, record);
8292         }
8293         var bt = this.mainBody.dom;
8294         
8295         var rows = this.el.select('tbody > tr', true).elements;
8296         
8297         if(typeof(rows[index]) != 'undefined'){
8298             bt.removeChild(rows[index].dom);
8299         }
8300         
8301 //        if(bt.rows[index]){
8302 //            bt.removeChild(bt.rows[index]);
8303 //        }
8304         
8305         if(isUpdate !== true){
8306             //this.stripeRows(index);
8307             //this.syncRowHeights(index, index);
8308             //this.layout();
8309             this.fireEvent("rowremoved", this, index, record);
8310         }
8311     },
8312     
8313     onAdd : function(ds, records, rowIndex)
8314     {
8315         //Roo.log('on Add called');
8316         // - note this does not handle multiple adding very well..
8317         var bt = this.mainBody.dom;
8318         for (var i =0 ; i < records.length;i++) {
8319             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8320             //Roo.log(records[i]);
8321             //Roo.log(this.store.getAt(rowIndex+i));
8322             this.insertRow(this.store, rowIndex + i, false);
8323             return;
8324         }
8325         
8326     },
8327     
8328     
8329     refreshRow : function(record){
8330         var ds = this.store, index;
8331         if(typeof record == 'number'){
8332             index = record;
8333             record = ds.getAt(index);
8334         }else{
8335             index = ds.indexOf(record);
8336             if (index < 0) {
8337                 return; // should not happen - but seems to 
8338             }
8339         }
8340         this.insertRow(ds, index, true);
8341         this.autoSize();
8342         this.onRemove(ds, record, index+1, true);
8343         this.autoSize();
8344         //this.syncRowHeights(index, index);
8345         //this.layout();
8346         this.fireEvent("rowupdated", this, index, record);
8347     },
8348     
8349     insertRow : function(dm, rowIndex, isUpdate){
8350         
8351         if(!isUpdate){
8352             this.fireEvent("beforerowsinserted", this, rowIndex);
8353         }
8354             //var s = this.getScrollState();
8355         var row = this.renderRow(this.cm, this.store, rowIndex);
8356         // insert before rowIndex..
8357         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358         
8359         var _this = this;
8360                 
8361         if(row.cellObjects.length){
8362             Roo.each(row.cellObjects, function(r){
8363                 _this.renderCellObject(r);
8364             })
8365         }
8366             
8367         if(!isUpdate){
8368             this.fireEvent("rowsinserted", this, rowIndex);
8369             //this.syncRowHeights(firstRow, lastRow);
8370             //this.stripeRows(firstRow);
8371             //this.layout();
8372         }
8373         
8374     },
8375     
8376     
8377     getRowDom : function(rowIndex)
8378     {
8379         var rows = this.el.select('tbody > tr', true).elements;
8380         
8381         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8382         
8383     },
8384     // returns the object tree for a tr..
8385   
8386     
8387     renderRow : function(cm, ds, rowIndex) 
8388     {
8389         var d = ds.getAt(rowIndex);
8390         
8391         var row = {
8392             tag : 'tr',
8393             cls : 'x-row-' + rowIndex,
8394             cn : []
8395         };
8396             
8397         var cellObjects = [];
8398         
8399         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8400             var config = cm.config[i];
8401             
8402             var renderer = cm.getRenderer(i);
8403             var value = '';
8404             var id = false;
8405             
8406             if(typeof(renderer) !== 'undefined'){
8407                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8408             }
8409             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8410             // and are rendered into the cells after the row is rendered - using the id for the element.
8411             
8412             if(typeof(value) === 'object'){
8413                 id = Roo.id();
8414                 cellObjects.push({
8415                     container : id,
8416                     cfg : value 
8417                 })
8418             }
8419             
8420             var rowcfg = {
8421                 record: d,
8422                 rowIndex : rowIndex,
8423                 colIndex : i,
8424                 rowClass : ''
8425             };
8426
8427             this.fireEvent('rowclass', this, rowcfg);
8428             
8429             var td = {
8430                 tag: 'td',
8431                 cls : rowcfg.rowClass + ' x-col-' + i,
8432                 style: '',
8433                 html: (typeof(value) === 'object') ? '' : value
8434             };
8435             
8436             if (id) {
8437                 td.id = id;
8438             }
8439             
8440             if(typeof(config.colspan) != 'undefined'){
8441                 td.colspan = config.colspan;
8442             }
8443             
8444             if(typeof(config.hidden) != 'undefined' && config.hidden){
8445                 td.style += ' display:none;';
8446             }
8447             
8448             if(typeof(config.align) != 'undefined' && config.align.length){
8449                 td.style += ' text-align:' + config.align + ';';
8450             }
8451             if(typeof(config.valign) != 'undefined' && config.valign.length){
8452                 td.style += ' vertical-align:' + config.valign + ';';
8453             }
8454             
8455             if(typeof(config.width) != 'undefined'){
8456                 td.style += ' width:' +  config.width + 'px;';
8457             }
8458             
8459             if(typeof(config.cursor) != 'undefined'){
8460                 td.style += ' cursor:' +  config.cursor + ';';
8461             }
8462             
8463             if(typeof(config.cls) != 'undefined'){
8464                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8465             }
8466             
8467             ['xs','sm','md','lg'].map(function(size){
8468                 
8469                 if(typeof(config[size]) == 'undefined'){
8470                     return;
8471                 }
8472                 
8473                 
8474                   
8475                 if (!config[size]) { // 0 = hidden
8476                     // BS 4 '0' is treated as hide that column and below.
8477                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478                     return;
8479                 }
8480                 
8481                 td.cls += ' col-' + size + '-' + config[size] + (
8482                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8483                 );
8484                  
8485
8486             });
8487             
8488             row.cn.push(td);
8489            
8490         }
8491         
8492         row.cellObjects = cellObjects;
8493         
8494         return row;
8495           
8496     },
8497     
8498     
8499     
8500     onBeforeLoad : function()
8501     {
8502         
8503     },
8504      /**
8505      * Remove all rows
8506      */
8507     clear : function()
8508     {
8509         this.el.select('tbody', true).first().dom.innerHTML = '';
8510     },
8511     /**
8512      * Show or hide a row.
8513      * @param {Number} rowIndex to show or hide
8514      * @param {Boolean} state hide
8515      */
8516     setRowVisibility : function(rowIndex, state)
8517     {
8518         var bt = this.mainBody.dom;
8519         
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         if(typeof(rows[rowIndex]) == 'undefined'){
8523             return;
8524         }
8525         rows[rowIndex].dom.style.display = state ? '' : 'none';
8526     },
8527     
8528     
8529     getSelectionModel : function(){
8530         if(!this.selModel){
8531             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8532         }
8533         return this.selModel;
8534     },
8535     /*
8536      * Render the Roo.bootstrap object from renderder
8537      */
8538     renderCellObject : function(r)
8539     {
8540         var _this = this;
8541         
8542         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8543         
8544         var t = r.cfg.render(r.container);
8545         
8546         if(r.cfg.cn){
8547             Roo.each(r.cfg.cn, function(c){
8548                 var child = {
8549                     container: t.getChildContainer(),
8550                     cfg: c
8551                 };
8552                 _this.renderCellObject(child);
8553             })
8554         }
8555     },
8556     
8557     getRowIndex : function(row)
8558     {
8559         var rowIndex = -1;
8560         
8561         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8562             if(el != row){
8563                 return;
8564             }
8565             
8566             rowIndex = index;
8567         });
8568         
8569         return rowIndex;
8570     },
8571      /**
8572      * Returns the grid's underlying element = used by panel.Grid
8573      * @return {Element} The element
8574      */
8575     getGridEl : function(){
8576         return this.el;
8577     },
8578      /**
8579      * Forces a resize - used by panel.Grid
8580      * @return {Element} The element
8581      */
8582     autoSize : function()
8583     {
8584         //var ctr = Roo.get(this.container.dom.parentElement);
8585         var ctr = Roo.get(this.el.dom);
8586         
8587         var thd = this.getGridEl().select('thead',true).first();
8588         var tbd = this.getGridEl().select('tbody', true).first();
8589         var tfd = this.getGridEl().select('tfoot', true).first();
8590         
8591         var cw = ctr.getWidth();
8592         
8593         if (tbd) {
8594             
8595             tbd.setWidth(ctr.getWidth());
8596             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8597             // this needs fixing for various usage - currently only hydra job advers I think..
8598             //tdb.setHeight(
8599             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8600             //); 
8601             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8602             cw -= barsize;
8603         }
8604         cw = Math.max(cw, this.totalWidth);
8605         this.getGridEl().select('tr',true).setWidth(cw);
8606         // resize 'expandable coloumn?
8607         
8608         return; // we doe not have a view in this design..
8609         
8610     },
8611     onBodyScroll: function()
8612     {
8613         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8614         if(this.mainHead){
8615             this.mainHead.setStyle({
8616                 'position' : 'relative',
8617                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8618             });
8619         }
8620         
8621         if(this.lazyLoad){
8622             
8623             var scrollHeight = this.mainBody.dom.scrollHeight;
8624             
8625             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8626             
8627             var height = this.mainBody.getHeight();
8628             
8629             if(scrollHeight - height == scrollTop) {
8630                 
8631                 var total = this.ds.getTotalCount();
8632                 
8633                 if(this.footer.cursor + this.footer.pageSize < total){
8634                     
8635                     this.footer.ds.load({
8636                         params : {
8637                             start : this.footer.cursor + this.footer.pageSize,
8638                             limit : this.footer.pageSize
8639                         },
8640                         add : true
8641                     });
8642                 }
8643             }
8644             
8645         }
8646     },
8647     
8648     onHeaderChange : function()
8649     {
8650         var header = this.renderHeader();
8651         var table = this.el.select('table', true).first();
8652         
8653         this.mainHead.remove();
8654         this.mainHead = table.createChild(header, this.mainBody, false);
8655     },
8656     
8657     onHiddenChange : function(colModel, colIndex, hidden)
8658     {
8659         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8660         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8661         
8662         this.CSS.updateRule(thSelector, "display", "");
8663         this.CSS.updateRule(tdSelector, "display", "");
8664         
8665         if(hidden){
8666             this.CSS.updateRule(thSelector, "display", "none");
8667             this.CSS.updateRule(tdSelector, "display", "none");
8668         }
8669         
8670         this.onHeaderChange();
8671         this.onLoad();
8672     },
8673     
8674     setColumnWidth: function(col_index, width)
8675     {
8676         // width = "md-2 xs-2..."
8677         if(!this.colModel.config[col_index]) {
8678             return;
8679         }
8680         
8681         var w = width.split(" ");
8682         
8683         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8684         
8685         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8686         
8687         
8688         for(var j = 0; j < w.length; j++) {
8689             
8690             if(!w[j]) {
8691                 continue;
8692             }
8693             
8694             var size_cls = w[j].split("-");
8695             
8696             if(!Number.isInteger(size_cls[1] * 1)) {
8697                 continue;
8698             }
8699             
8700             if(!this.colModel.config[col_index][size_cls[0]]) {
8701                 continue;
8702             }
8703             
8704             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8705                 continue;
8706             }
8707             
8708             h_row[0].classList.replace(
8709                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8710                 "col-"+size_cls[0]+"-"+size_cls[1]
8711             );
8712             
8713             for(var i = 0; i < rows.length; i++) {
8714                 
8715                 var size_cls = w[j].split("-");
8716                 
8717                 if(!Number.isInteger(size_cls[1] * 1)) {
8718                     continue;
8719                 }
8720                 
8721                 if(!this.colModel.config[col_index][size_cls[0]]) {
8722                     continue;
8723                 }
8724                 
8725                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8726                     continue;
8727                 }
8728                 
8729                 rows[i].classList.replace(
8730                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8731                     "col-"+size_cls[0]+"-"+size_cls[1]
8732                 );
8733             }
8734             
8735             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8736         }
8737     }
8738 });
8739
8740  
8741
8742  /*
8743  * - LGPL
8744  *
8745  * table cell
8746  * 
8747  */
8748
8749 /**
8750  * @class Roo.bootstrap.TableCell
8751  * @extends Roo.bootstrap.Component
8752  * Bootstrap TableCell class
8753  * @cfg {String} html cell contain text
8754  * @cfg {String} cls cell class
8755  * @cfg {String} tag cell tag (td|th) default td
8756  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8757  * @cfg {String} align Aligns the content in a cell
8758  * @cfg {String} axis Categorizes cells
8759  * @cfg {String} bgcolor Specifies the background color of a cell
8760  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8761  * @cfg {Number} colspan Specifies the number of columns a cell should span
8762  * @cfg {String} headers Specifies one or more header cells a cell is related to
8763  * @cfg {Number} height Sets the height of a cell
8764  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8765  * @cfg {Number} rowspan Sets the number of rows a cell should span
8766  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8767  * @cfg {String} valign Vertical aligns the content in a cell
8768  * @cfg {Number} width Specifies the width of a cell
8769  * 
8770  * @constructor
8771  * Create a new TableCell
8772  * @param {Object} config The config object
8773  */
8774
8775 Roo.bootstrap.TableCell = function(config){
8776     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8777 };
8778
8779 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8780     
8781     html: false,
8782     cls: false,
8783     tag: false,
8784     abbr: false,
8785     align: false,
8786     axis: false,
8787     bgcolor: false,
8788     charoff: false,
8789     colspan: false,
8790     headers: false,
8791     height: false,
8792     nowrap: false,
8793     rowspan: false,
8794     scope: false,
8795     valign: false,
8796     width: false,
8797     
8798     
8799     getAutoCreate : function(){
8800         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8801         
8802         cfg = {
8803             tag: 'td'
8804         };
8805         
8806         if(this.tag){
8807             cfg.tag = this.tag;
8808         }
8809         
8810         if (this.html) {
8811             cfg.html=this.html
8812         }
8813         if (this.cls) {
8814             cfg.cls=this.cls
8815         }
8816         if (this.abbr) {
8817             cfg.abbr=this.abbr
8818         }
8819         if (this.align) {
8820             cfg.align=this.align
8821         }
8822         if (this.axis) {
8823             cfg.axis=this.axis
8824         }
8825         if (this.bgcolor) {
8826             cfg.bgcolor=this.bgcolor
8827         }
8828         if (this.charoff) {
8829             cfg.charoff=this.charoff
8830         }
8831         if (this.colspan) {
8832             cfg.colspan=this.colspan
8833         }
8834         if (this.headers) {
8835             cfg.headers=this.headers
8836         }
8837         if (this.height) {
8838             cfg.height=this.height
8839         }
8840         if (this.nowrap) {
8841             cfg.nowrap=this.nowrap
8842         }
8843         if (this.rowspan) {
8844             cfg.rowspan=this.rowspan
8845         }
8846         if (this.scope) {
8847             cfg.scope=this.scope
8848         }
8849         if (this.valign) {
8850             cfg.valign=this.valign
8851         }
8852         if (this.width) {
8853             cfg.width=this.width
8854         }
8855         
8856         
8857         return cfg;
8858     }
8859    
8860 });
8861
8862  
8863
8864  /*
8865  * - LGPL
8866  *
8867  * table row
8868  * 
8869  */
8870
8871 /**
8872  * @class Roo.bootstrap.TableRow
8873  * @extends Roo.bootstrap.Component
8874  * Bootstrap TableRow class
8875  * @cfg {String} cls row class
8876  * @cfg {String} align Aligns the content in a table row
8877  * @cfg {String} bgcolor Specifies a background color for a table row
8878  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8879  * @cfg {String} valign Vertical aligns the content in a table row
8880  * 
8881  * @constructor
8882  * Create a new TableRow
8883  * @param {Object} config The config object
8884  */
8885
8886 Roo.bootstrap.TableRow = function(config){
8887     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8888 };
8889
8890 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8891     
8892     cls: false,
8893     align: false,
8894     bgcolor: false,
8895     charoff: false,
8896     valign: false,
8897     
8898     getAutoCreate : function(){
8899         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8900         
8901         cfg = {
8902             tag: 'tr'
8903         };
8904             
8905         if(this.cls){
8906             cfg.cls = this.cls;
8907         }
8908         if(this.align){
8909             cfg.align = this.align;
8910         }
8911         if(this.bgcolor){
8912             cfg.bgcolor = this.bgcolor;
8913         }
8914         if(this.charoff){
8915             cfg.charoff = this.charoff;
8916         }
8917         if(this.valign){
8918             cfg.valign = this.valign;
8919         }
8920         
8921         return cfg;
8922     }
8923    
8924 });
8925
8926  
8927
8928  /*
8929  * - LGPL
8930  *
8931  * table body
8932  * 
8933  */
8934
8935 /**
8936  * @class Roo.bootstrap.TableBody
8937  * @extends Roo.bootstrap.Component
8938  * Bootstrap TableBody class
8939  * @cfg {String} cls element class
8940  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8941  * @cfg {String} align Aligns the content inside the element
8942  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8943  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8944  * 
8945  * @constructor
8946  * Create a new TableBody
8947  * @param {Object} config The config object
8948  */
8949
8950 Roo.bootstrap.TableBody = function(config){
8951     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8952 };
8953
8954 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8955     
8956     cls: false,
8957     tag: false,
8958     align: false,
8959     charoff: false,
8960     valign: false,
8961     
8962     getAutoCreate : function(){
8963         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8964         
8965         cfg = {
8966             tag: 'tbody'
8967         };
8968             
8969         if (this.cls) {
8970             cfg.cls=this.cls
8971         }
8972         if(this.tag){
8973             cfg.tag = this.tag;
8974         }
8975         
8976         if(this.align){
8977             cfg.align = this.align;
8978         }
8979         if(this.charoff){
8980             cfg.charoff = this.charoff;
8981         }
8982         if(this.valign){
8983             cfg.valign = this.valign;
8984         }
8985         
8986         return cfg;
8987     }
8988     
8989     
8990 //    initEvents : function()
8991 //    {
8992 //        
8993 //        if(!this.store){
8994 //            return;
8995 //        }
8996 //        
8997 //        this.store = Roo.factory(this.store, Roo.data);
8998 //        this.store.on('load', this.onLoad, this);
8999 //        
9000 //        this.store.load();
9001 //        
9002 //    },
9003 //    
9004 //    onLoad: function () 
9005 //    {   
9006 //        this.fireEvent('load', this);
9007 //    }
9008 //    
9009 //   
9010 });
9011
9012  
9013
9014  /*
9015  * Based on:
9016  * Ext JS Library 1.1.1
9017  * Copyright(c) 2006-2007, Ext JS, LLC.
9018  *
9019  * Originally Released Under LGPL - original licence link has changed is not relivant.
9020  *
9021  * Fork - LGPL
9022  * <script type="text/javascript">
9023  */
9024
9025 // as we use this in bootstrap.
9026 Roo.namespace('Roo.form');
9027  /**
9028  * @class Roo.form.Action
9029  * Internal Class used to handle form actions
9030  * @constructor
9031  * @param {Roo.form.BasicForm} el The form element or its id
9032  * @param {Object} config Configuration options
9033  */
9034
9035  
9036  
9037 // define the action interface
9038 Roo.form.Action = function(form, options){
9039     this.form = form;
9040     this.options = options || {};
9041 };
9042 /**
9043  * Client Validation Failed
9044  * @const 
9045  */
9046 Roo.form.Action.CLIENT_INVALID = 'client';
9047 /**
9048  * Server Validation Failed
9049  * @const 
9050  */
9051 Roo.form.Action.SERVER_INVALID = 'server';
9052  /**
9053  * Connect to Server Failed
9054  * @const 
9055  */
9056 Roo.form.Action.CONNECT_FAILURE = 'connect';
9057 /**
9058  * Reading Data from Server Failed
9059  * @const 
9060  */
9061 Roo.form.Action.LOAD_FAILURE = 'load';
9062
9063 Roo.form.Action.prototype = {
9064     type : 'default',
9065     failureType : undefined,
9066     response : undefined,
9067     result : undefined,
9068
9069     // interface method
9070     run : function(options){
9071
9072     },
9073
9074     // interface method
9075     success : function(response){
9076
9077     },
9078
9079     // interface method
9080     handleResponse : function(response){
9081
9082     },
9083
9084     // default connection failure
9085     failure : function(response){
9086         
9087         this.response = response;
9088         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9089         this.form.afterAction(this, false);
9090     },
9091
9092     processResponse : function(response){
9093         this.response = response;
9094         if(!response.responseText){
9095             return true;
9096         }
9097         this.result = this.handleResponse(response);
9098         return this.result;
9099     },
9100
9101     // utility functions used internally
9102     getUrl : function(appendParams){
9103         var url = this.options.url || this.form.url || this.form.el.dom.action;
9104         if(appendParams){
9105             var p = this.getParams();
9106             if(p){
9107                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9108             }
9109         }
9110         return url;
9111     },
9112
9113     getMethod : function(){
9114         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9115     },
9116
9117     getParams : function(){
9118         var bp = this.form.baseParams;
9119         var p = this.options.params;
9120         if(p){
9121             if(typeof p == "object"){
9122                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9123             }else if(typeof p == 'string' && bp){
9124                 p += '&' + Roo.urlEncode(bp);
9125             }
9126         }else if(bp){
9127             p = Roo.urlEncode(bp);
9128         }
9129         return p;
9130     },
9131
9132     createCallback : function(){
9133         return {
9134             success: this.success,
9135             failure: this.failure,
9136             scope: this,
9137             timeout: (this.form.timeout*1000),
9138             upload: this.form.fileUpload ? this.success : undefined
9139         };
9140     }
9141 };
9142
9143 Roo.form.Action.Submit = function(form, options){
9144     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9145 };
9146
9147 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9148     type : 'submit',
9149
9150     haveProgress : false,
9151     uploadComplete : false,
9152     
9153     // uploadProgress indicator.
9154     uploadProgress : function()
9155     {
9156         if (!this.form.progressUrl) {
9157             return;
9158         }
9159         
9160         if (!this.haveProgress) {
9161             Roo.MessageBox.progress("Uploading", "Uploading");
9162         }
9163         if (this.uploadComplete) {
9164            Roo.MessageBox.hide();
9165            return;
9166         }
9167         
9168         this.haveProgress = true;
9169    
9170         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9171         
9172         var c = new Roo.data.Connection();
9173         c.request({
9174             url : this.form.progressUrl,
9175             params: {
9176                 id : uid
9177             },
9178             method: 'GET',
9179             success : function(req){
9180                //console.log(data);
9181                 var rdata = false;
9182                 var edata;
9183                 try  {
9184                    rdata = Roo.decode(req.responseText)
9185                 } catch (e) {
9186                     Roo.log("Invalid data from server..");
9187                     Roo.log(edata);
9188                     return;
9189                 }
9190                 if (!rdata || !rdata.success) {
9191                     Roo.log(rdata);
9192                     Roo.MessageBox.alert(Roo.encode(rdata));
9193                     return;
9194                 }
9195                 var data = rdata.data;
9196                 
9197                 if (this.uploadComplete) {
9198                    Roo.MessageBox.hide();
9199                    return;
9200                 }
9201                    
9202                 if (data){
9203                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9204                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9205                     );
9206                 }
9207                 this.uploadProgress.defer(2000,this);
9208             },
9209        
9210             failure: function(data) {
9211                 Roo.log('progress url failed ');
9212                 Roo.log(data);
9213             },
9214             scope : this
9215         });
9216            
9217     },
9218     
9219     
9220     run : function()
9221     {
9222         // run get Values on the form, so it syncs any secondary forms.
9223         this.form.getValues();
9224         
9225         var o = this.options;
9226         var method = this.getMethod();
9227         var isPost = method == 'POST';
9228         if(o.clientValidation === false || this.form.isValid()){
9229             
9230             if (this.form.progressUrl) {
9231                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9232                     (new Date() * 1) + '' + Math.random());
9233                     
9234             } 
9235             
9236             
9237             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9238                 form:this.form.el.dom,
9239                 url:this.getUrl(!isPost),
9240                 method: method,
9241                 params:isPost ? this.getParams() : null,
9242                 isUpload: this.form.fileUpload,
9243                 formData : this.form.formData
9244             }));
9245             
9246             this.uploadProgress();
9247
9248         }else if (o.clientValidation !== false){ // client validation failed
9249             this.failureType = Roo.form.Action.CLIENT_INVALID;
9250             this.form.afterAction(this, false);
9251         }
9252     },
9253
9254     success : function(response)
9255     {
9256         this.uploadComplete= true;
9257         if (this.haveProgress) {
9258             Roo.MessageBox.hide();
9259         }
9260         
9261         
9262         var result = this.processResponse(response);
9263         if(result === true || result.success){
9264             this.form.afterAction(this, true);
9265             return;
9266         }
9267         if(result.errors){
9268             this.form.markInvalid(result.errors);
9269             this.failureType = Roo.form.Action.SERVER_INVALID;
9270         }
9271         this.form.afterAction(this, false);
9272     },
9273     failure : function(response)
9274     {
9275         this.uploadComplete= true;
9276         if (this.haveProgress) {
9277             Roo.MessageBox.hide();
9278         }
9279         
9280         this.response = response;
9281         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9282         this.form.afterAction(this, false);
9283     },
9284     
9285     handleResponse : function(response){
9286         if(this.form.errorReader){
9287             var rs = this.form.errorReader.read(response);
9288             var errors = [];
9289             if(rs.records){
9290                 for(var i = 0, len = rs.records.length; i < len; i++) {
9291                     var r = rs.records[i];
9292                     errors[i] = r.data;
9293                 }
9294             }
9295             if(errors.length < 1){
9296                 errors = null;
9297             }
9298             return {
9299                 success : rs.success,
9300                 errors : errors
9301             };
9302         }
9303         var ret = false;
9304         try {
9305             ret = Roo.decode(response.responseText);
9306         } catch (e) {
9307             ret = {
9308                 success: false,
9309                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9310                 errors : []
9311             };
9312         }
9313         return ret;
9314         
9315     }
9316 });
9317
9318
9319 Roo.form.Action.Load = function(form, options){
9320     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9321     this.reader = this.form.reader;
9322 };
9323
9324 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9325     type : 'load',
9326
9327     run : function(){
9328         
9329         Roo.Ajax.request(Roo.apply(
9330                 this.createCallback(), {
9331                     method:this.getMethod(),
9332                     url:this.getUrl(false),
9333                     params:this.getParams()
9334         }));
9335     },
9336
9337     success : function(response){
9338         
9339         var result = this.processResponse(response);
9340         if(result === true || !result.success || !result.data){
9341             this.failureType = Roo.form.Action.LOAD_FAILURE;
9342             this.form.afterAction(this, false);
9343             return;
9344         }
9345         this.form.clearInvalid();
9346         this.form.setValues(result.data);
9347         this.form.afterAction(this, true);
9348     },
9349
9350     handleResponse : function(response){
9351         if(this.form.reader){
9352             var rs = this.form.reader.read(response);
9353             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9354             return {
9355                 success : rs.success,
9356                 data : data
9357             };
9358         }
9359         return Roo.decode(response.responseText);
9360     }
9361 });
9362
9363 Roo.form.Action.ACTION_TYPES = {
9364     'load' : Roo.form.Action.Load,
9365     'submit' : Roo.form.Action.Submit
9366 };/*
9367  * - LGPL
9368  *
9369  * form
9370  *
9371  */
9372
9373 /**
9374  * @class Roo.bootstrap.Form
9375  * @extends Roo.bootstrap.Component
9376  * Bootstrap Form class
9377  * @cfg {String} method  GET | POST (default POST)
9378  * @cfg {String} labelAlign top | left (default top)
9379  * @cfg {String} align left  | right - for navbars
9380  * @cfg {Boolean} loadMask load mask when submit (default true)
9381
9382  *
9383  * @constructor
9384  * Create a new Form
9385  * @param {Object} config The config object
9386  */
9387
9388
9389 Roo.bootstrap.Form = function(config){
9390     
9391     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9392     
9393     Roo.bootstrap.Form.popover.apply();
9394     
9395     this.addEvents({
9396         /**
9397          * @event clientvalidation
9398          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9399          * @param {Form} this
9400          * @param {Boolean} valid true if the form has passed client-side validation
9401          */
9402         clientvalidation: true,
9403         /**
9404          * @event beforeaction
9405          * Fires before any action is performed. Return false to cancel the action.
9406          * @param {Form} this
9407          * @param {Action} action The action to be performed
9408          */
9409         beforeaction: true,
9410         /**
9411          * @event actionfailed
9412          * Fires when an action fails.
9413          * @param {Form} this
9414          * @param {Action} action The action that failed
9415          */
9416         actionfailed : true,
9417         /**
9418          * @event actioncomplete
9419          * Fires when an action is completed.
9420          * @param {Form} this
9421          * @param {Action} action The action that completed
9422          */
9423         actioncomplete : true
9424     });
9425 };
9426
9427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9428
9429      /**
9430      * @cfg {String} method
9431      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9432      */
9433     method : 'POST',
9434     /**
9435      * @cfg {String} url
9436      * The URL to use for form actions if one isn't supplied in the action options.
9437      */
9438     /**
9439      * @cfg {Boolean} fileUpload
9440      * Set to true if this form is a file upload.
9441      */
9442
9443     /**
9444      * @cfg {Object} baseParams
9445      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9446      */
9447
9448     /**
9449      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9450      */
9451     timeout: 30,
9452     /**
9453      * @cfg {Sting} align (left|right) for navbar forms
9454      */
9455     align : 'left',
9456
9457     // private
9458     activeAction : null,
9459
9460     /**
9461      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9462      * element by passing it or its id or mask the form itself by passing in true.
9463      * @type Mixed
9464      */
9465     waitMsgTarget : false,
9466
9467     loadMask : true,
9468     
9469     /**
9470      * @cfg {Boolean} errorMask (true|false) default false
9471      */
9472     errorMask : false,
9473     
9474     /**
9475      * @cfg {Number} maskOffset Default 100
9476      */
9477     maskOffset : 100,
9478     
9479     /**
9480      * @cfg {Boolean} maskBody
9481      */
9482     maskBody : false,
9483
9484     getAutoCreate : function(){
9485
9486         var cfg = {
9487             tag: 'form',
9488             method : this.method || 'POST',
9489             id : this.id || Roo.id(),
9490             cls : ''
9491         };
9492         if (this.parent().xtype.match(/^Nav/)) {
9493             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9494
9495         }
9496
9497         if (this.labelAlign == 'left' ) {
9498             cfg.cls += ' form-horizontal';
9499         }
9500
9501
9502         return cfg;
9503     },
9504     initEvents : function()
9505     {
9506         this.el.on('submit', this.onSubmit, this);
9507         // this was added as random key presses on the form where triggering form submit.
9508         this.el.on('keypress', function(e) {
9509             if (e.getCharCode() != 13) {
9510                 return true;
9511             }
9512             // we might need to allow it for textareas.. and some other items.
9513             // check e.getTarget().
9514
9515             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9516                 return true;
9517             }
9518
9519             Roo.log("keypress blocked");
9520
9521             e.preventDefault();
9522             return false;
9523         });
9524         
9525     },
9526     // private
9527     onSubmit : function(e){
9528         e.stopEvent();
9529     },
9530
9531      /**
9532      * Returns true if client-side validation on the form is successful.
9533      * @return Boolean
9534      */
9535     isValid : function(){
9536         var items = this.getItems();
9537         var valid = true;
9538         var target = false;
9539         
9540         items.each(function(f){
9541             
9542             if(f.validate()){
9543                 return;
9544             }
9545             
9546             Roo.log('invalid field: ' + f.name);
9547             
9548             valid = false;
9549
9550             if(!target && f.el.isVisible(true)){
9551                 target = f;
9552             }
9553            
9554         });
9555         
9556         if(this.errorMask && !valid){
9557             Roo.bootstrap.Form.popover.mask(this, target);
9558         }
9559         
9560         return valid;
9561     },
9562     
9563     /**
9564      * Returns true if any fields in this form have changed since their original load.
9565      * @return Boolean
9566      */
9567     isDirty : function(){
9568         var dirty = false;
9569         var items = this.getItems();
9570         items.each(function(f){
9571            if(f.isDirty()){
9572                dirty = true;
9573                return false;
9574            }
9575            return true;
9576         });
9577         return dirty;
9578     },
9579      /**
9580      * Performs a predefined action (submit or load) or custom actions you define on this form.
9581      * @param {String} actionName The name of the action type
9582      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9583      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9584      * accept other config options):
9585      * <pre>
9586 Property          Type             Description
9587 ----------------  ---------------  ----------------------------------------------------------------------------------
9588 url               String           The url for the action (defaults to the form's url)
9589 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9590 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9591 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9592                                    validate the form on the client (defaults to false)
9593      * </pre>
9594      * @return {BasicForm} this
9595      */
9596     doAction : function(action, options){
9597         if(typeof action == 'string'){
9598             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9599         }
9600         if(this.fireEvent('beforeaction', this, action) !== false){
9601             this.beforeAction(action);
9602             action.run.defer(100, action);
9603         }
9604         return this;
9605     },
9606
9607     // private
9608     beforeAction : function(action){
9609         var o = action.options;
9610         
9611         if(this.loadMask){
9612             
9613             if(this.maskBody){
9614                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9615             } else {
9616                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9617             }
9618         }
9619         // not really supported yet.. ??
9620
9621         //if(this.waitMsgTarget === true){
9622         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9623         //}else if(this.waitMsgTarget){
9624         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9625         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9626         //}else {
9627         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9628        // }
9629
9630     },
9631
9632     // private
9633     afterAction : function(action, success){
9634         this.activeAction = null;
9635         var o = action.options;
9636
9637         if(this.loadMask){
9638             
9639             if(this.maskBody){
9640                 Roo.get(document.body).unmask();
9641             } else {
9642                 this.el.unmask();
9643             }
9644         }
9645         
9646         //if(this.waitMsgTarget === true){
9647 //            this.el.unmask();
9648         //}else if(this.waitMsgTarget){
9649         //    this.waitMsgTarget.unmask();
9650         //}else{
9651         //    Roo.MessageBox.updateProgress(1);
9652         //    Roo.MessageBox.hide();
9653        // }
9654         //
9655         if(success){
9656             if(o.reset){
9657                 this.reset();
9658             }
9659             Roo.callback(o.success, o.scope, [this, action]);
9660             this.fireEvent('actioncomplete', this, action);
9661
9662         }else{
9663
9664             // failure condition..
9665             // we have a scenario where updates need confirming.
9666             // eg. if a locking scenario exists..
9667             // we look for { errors : { needs_confirm : true }} in the response.
9668             if (
9669                 (typeof(action.result) != 'undefined')  &&
9670                 (typeof(action.result.errors) != 'undefined')  &&
9671                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9672            ){
9673                 var _t = this;
9674                 Roo.log("not supported yet");
9675                  /*
9676
9677                 Roo.MessageBox.confirm(
9678                     "Change requires confirmation",
9679                     action.result.errorMsg,
9680                     function(r) {
9681                         if (r != 'yes') {
9682                             return;
9683                         }
9684                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9685                     }
9686
9687                 );
9688                 */
9689
9690
9691                 return;
9692             }
9693
9694             Roo.callback(o.failure, o.scope, [this, action]);
9695             // show an error message if no failed handler is set..
9696             if (!this.hasListener('actionfailed')) {
9697                 Roo.log("need to add dialog support");
9698                 /*
9699                 Roo.MessageBox.alert("Error",
9700                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9701                         action.result.errorMsg :
9702                         "Saving Failed, please check your entries or try again"
9703                 );
9704                 */
9705             }
9706
9707             this.fireEvent('actionfailed', this, action);
9708         }
9709
9710     },
9711     /**
9712      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9713      * @param {String} id The value to search for
9714      * @return Field
9715      */
9716     findField : function(id){
9717         var items = this.getItems();
9718         var field = items.get(id);
9719         if(!field){
9720              items.each(function(f){
9721                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9722                     field = f;
9723                     return false;
9724                 }
9725                 return true;
9726             });
9727         }
9728         return field || null;
9729     },
9730      /**
9731      * Mark fields in this form invalid in bulk.
9732      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9733      * @return {BasicForm} this
9734      */
9735     markInvalid : function(errors){
9736         if(errors instanceof Array){
9737             for(var i = 0, len = errors.length; i < len; i++){
9738                 var fieldError = errors[i];
9739                 var f = this.findField(fieldError.id);
9740                 if(f){
9741                     f.markInvalid(fieldError.msg);
9742                 }
9743             }
9744         }else{
9745             var field, id;
9746             for(id in errors){
9747                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9748                     field.markInvalid(errors[id]);
9749                 }
9750             }
9751         }
9752         //Roo.each(this.childForms || [], function (f) {
9753         //    f.markInvalid(errors);
9754         //});
9755
9756         return this;
9757     },
9758
9759     /**
9760      * Set values for fields in this form in bulk.
9761      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9762      * @return {BasicForm} this
9763      */
9764     setValues : function(values){
9765         if(values instanceof Array){ // array of objects
9766             for(var i = 0, len = values.length; i < len; i++){
9767                 var v = values[i];
9768                 var f = this.findField(v.id);
9769                 if(f){
9770                     f.setValue(v.value);
9771                     if(this.trackResetOnLoad){
9772                         f.originalValue = f.getValue();
9773                     }
9774                 }
9775             }
9776         }else{ // object hash
9777             var field, id;
9778             for(id in values){
9779                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9780
9781                     if (field.setFromData &&
9782                         field.valueField &&
9783                         field.displayField &&
9784                         // combos' with local stores can
9785                         // be queried via setValue()
9786                         // to set their value..
9787                         (field.store && !field.store.isLocal)
9788                         ) {
9789                         // it's a combo
9790                         var sd = { };
9791                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9792                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9793                         field.setFromData(sd);
9794
9795                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9796                         
9797                         field.setFromData(values);
9798                         
9799                     } else {
9800                         field.setValue(values[id]);
9801                     }
9802
9803
9804                     if(this.trackResetOnLoad){
9805                         field.originalValue = field.getValue();
9806                     }
9807                 }
9808             }
9809         }
9810
9811         //Roo.each(this.childForms || [], function (f) {
9812         //    f.setValues(values);
9813         //});
9814
9815         return this;
9816     },
9817
9818     /**
9819      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9820      * they are returned as an array.
9821      * @param {Boolean} asString
9822      * @return {Object}
9823      */
9824     getValues : function(asString){
9825         //if (this.childForms) {
9826             // copy values from the child forms
9827         //    Roo.each(this.childForms, function (f) {
9828         //        this.setValues(f.getValues());
9829         //    }, this);
9830         //}
9831
9832
9833
9834         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9835         if(asString === true){
9836             return fs;
9837         }
9838         return Roo.urlDecode(fs);
9839     },
9840
9841     /**
9842      * Returns the fields in this form as an object with key/value pairs.
9843      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9844      * @return {Object}
9845      */
9846     getFieldValues : function(with_hidden)
9847     {
9848         var items = this.getItems();
9849         var ret = {};
9850         items.each(function(f){
9851             
9852             if (!f.getName()) {
9853                 return;
9854             }
9855             
9856             var v = f.getValue();
9857             
9858             if (f.inputType =='radio') {
9859                 if (typeof(ret[f.getName()]) == 'undefined') {
9860                     ret[f.getName()] = ''; // empty..
9861                 }
9862
9863                 if (!f.el.dom.checked) {
9864                     return;
9865
9866                 }
9867                 v = f.el.dom.value;
9868
9869             }
9870             
9871             if(f.xtype == 'MoneyField'){
9872                 ret[f.currencyName] = f.getCurrency();
9873             }
9874
9875             // not sure if this supported any more..
9876             if ((typeof(v) == 'object') && f.getRawValue) {
9877                 v = f.getRawValue() ; // dates..
9878             }
9879             // combo boxes where name != hiddenName...
9880             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9881                 ret[f.name] = f.getRawValue();
9882             }
9883             ret[f.getName()] = v;
9884         });
9885
9886         return ret;
9887     },
9888
9889     /**
9890      * Clears all invalid messages in this form.
9891      * @return {BasicForm} this
9892      */
9893     clearInvalid : function(){
9894         var items = this.getItems();
9895
9896         items.each(function(f){
9897            f.clearInvalid();
9898         });
9899
9900         return this;
9901     },
9902
9903     /**
9904      * Resets this form.
9905      * @return {BasicForm} this
9906      */
9907     reset : function(){
9908         var items = this.getItems();
9909         items.each(function(f){
9910             f.reset();
9911         });
9912
9913         Roo.each(this.childForms || [], function (f) {
9914             f.reset();
9915         });
9916
9917
9918         return this;
9919     },
9920     
9921     getItems : function()
9922     {
9923         var r=new Roo.util.MixedCollection(false, function(o){
9924             return o.id || (o.id = Roo.id());
9925         });
9926         var iter = function(el) {
9927             if (el.inputEl) {
9928                 r.add(el);
9929             }
9930             if (!el.items) {
9931                 return;
9932             }
9933             Roo.each(el.items,function(e) {
9934                 iter(e);
9935             });
9936         };
9937
9938         iter(this);
9939         return r;
9940     },
9941     
9942     hideFields : function(items)
9943     {
9944         Roo.each(items, function(i){
9945             
9946             var f = this.findField(i);
9947             
9948             if(!f){
9949                 return;
9950             }
9951             
9952             f.hide();
9953             
9954         }, this);
9955     },
9956     
9957     showFields : function(items)
9958     {
9959         Roo.each(items, function(i){
9960             
9961             var f = this.findField(i);
9962             
9963             if(!f){
9964                 return;
9965             }
9966             
9967             f.show();
9968             
9969         }, this);
9970     }
9971
9972 });
9973
9974 Roo.apply(Roo.bootstrap.Form, {
9975     
9976     popover : {
9977         
9978         padding : 5,
9979         
9980         isApplied : false,
9981         
9982         isMasked : false,
9983         
9984         form : false,
9985         
9986         target : false,
9987         
9988         toolTip : false,
9989         
9990         intervalID : false,
9991         
9992         maskEl : false,
9993         
9994         apply : function()
9995         {
9996             if(this.isApplied){
9997                 return;
9998             }
9999             
10000             this.maskEl = {
10001                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10002                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10003                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10004                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10005             };
10006             
10007             this.maskEl.top.enableDisplayMode("block");
10008             this.maskEl.left.enableDisplayMode("block");
10009             this.maskEl.bottom.enableDisplayMode("block");
10010             this.maskEl.right.enableDisplayMode("block");
10011             
10012             this.toolTip = new Roo.bootstrap.Tooltip({
10013                 cls : 'roo-form-error-popover',
10014                 alignment : {
10015                     'left' : ['r-l', [-2,0], 'right'],
10016                     'right' : ['l-r', [2,0], 'left'],
10017                     'bottom' : ['tl-bl', [0,2], 'top'],
10018                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10019                 }
10020             });
10021             
10022             this.toolTip.render(Roo.get(document.body));
10023
10024             this.toolTip.el.enableDisplayMode("block");
10025             
10026             Roo.get(document.body).on('click', function(){
10027                 this.unmask();
10028             }, this);
10029             
10030             Roo.get(document.body).on('touchstart', function(){
10031                 this.unmask();
10032             }, this);
10033             
10034             this.isApplied = true
10035         },
10036         
10037         mask : function(form, target)
10038         {
10039             this.form = form;
10040             
10041             this.target = target;
10042             
10043             if(!this.form.errorMask || !target.el){
10044                 return;
10045             }
10046             
10047             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10048             
10049             Roo.log(scrollable);
10050             
10051             var ot = this.target.el.calcOffsetsTo(scrollable);
10052             
10053             var scrollTo = ot[1] - this.form.maskOffset;
10054             
10055             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10056             
10057             scrollable.scrollTo('top', scrollTo);
10058             
10059             var box = this.target.el.getBox();
10060             Roo.log(box);
10061             var zIndex = Roo.bootstrap.Modal.zIndex++;
10062
10063             
10064             this.maskEl.top.setStyle('position', 'absolute');
10065             this.maskEl.top.setStyle('z-index', zIndex);
10066             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10067             this.maskEl.top.setLeft(0);
10068             this.maskEl.top.setTop(0);
10069             this.maskEl.top.show();
10070             
10071             this.maskEl.left.setStyle('position', 'absolute');
10072             this.maskEl.left.setStyle('z-index', zIndex);
10073             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10074             this.maskEl.left.setLeft(0);
10075             this.maskEl.left.setTop(box.y - this.padding);
10076             this.maskEl.left.show();
10077
10078             this.maskEl.bottom.setStyle('position', 'absolute');
10079             this.maskEl.bottom.setStyle('z-index', zIndex);
10080             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10081             this.maskEl.bottom.setLeft(0);
10082             this.maskEl.bottom.setTop(box.bottom + this.padding);
10083             this.maskEl.bottom.show();
10084
10085             this.maskEl.right.setStyle('position', 'absolute');
10086             this.maskEl.right.setStyle('z-index', zIndex);
10087             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10088             this.maskEl.right.setLeft(box.right + this.padding);
10089             this.maskEl.right.setTop(box.y - this.padding);
10090             this.maskEl.right.show();
10091
10092             this.toolTip.bindEl = this.target.el;
10093
10094             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10095
10096             var tip = this.target.blankText;
10097
10098             if(this.target.getValue() !== '' ) {
10099                 
10100                 if (this.target.invalidText.length) {
10101                     tip = this.target.invalidText;
10102                 } else if (this.target.regexText.length){
10103                     tip = this.target.regexText;
10104                 }
10105             }
10106
10107             this.toolTip.show(tip);
10108
10109             this.intervalID = window.setInterval(function() {
10110                 Roo.bootstrap.Form.popover.unmask();
10111             }, 10000);
10112
10113             window.onwheel = function(){ return false;};
10114             
10115             (function(){ this.isMasked = true; }).defer(500, this);
10116             
10117         },
10118         
10119         unmask : function()
10120         {
10121             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10122                 return;
10123             }
10124             
10125             this.maskEl.top.setStyle('position', 'absolute');
10126             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10127             this.maskEl.top.hide();
10128
10129             this.maskEl.left.setStyle('position', 'absolute');
10130             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10131             this.maskEl.left.hide();
10132
10133             this.maskEl.bottom.setStyle('position', 'absolute');
10134             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10135             this.maskEl.bottom.hide();
10136
10137             this.maskEl.right.setStyle('position', 'absolute');
10138             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10139             this.maskEl.right.hide();
10140             
10141             this.toolTip.hide();
10142             
10143             this.toolTip.el.hide();
10144             
10145             window.onwheel = function(){ return true;};
10146             
10147             if(this.intervalID){
10148                 window.clearInterval(this.intervalID);
10149                 this.intervalID = false;
10150             }
10151             
10152             this.isMasked = false;
10153             
10154         }
10155         
10156     }
10157     
10158 });
10159
10160 /*
10161  * Based on:
10162  * Ext JS Library 1.1.1
10163  * Copyright(c) 2006-2007, Ext JS, LLC.
10164  *
10165  * Originally Released Under LGPL - original licence link has changed is not relivant.
10166  *
10167  * Fork - LGPL
10168  * <script type="text/javascript">
10169  */
10170 /**
10171  * @class Roo.form.VTypes
10172  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10173  * @singleton
10174  */
10175 Roo.form.VTypes = function(){
10176     // closure these in so they are only created once.
10177     var alpha = /^[a-zA-Z_]+$/;
10178     var alphanum = /^[a-zA-Z0-9_]+$/;
10179     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10180     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10181
10182     // All these messages and functions are configurable
10183     return {
10184         /**
10185          * The function used to validate email addresses
10186          * @param {String} value The email address
10187          */
10188         'email' : function(v){
10189             return email.test(v);
10190         },
10191         /**
10192          * The error text to display when the email validation function returns false
10193          * @type String
10194          */
10195         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10196         /**
10197          * The keystroke filter mask to be applied on email input
10198          * @type RegExp
10199          */
10200         'emailMask' : /[a-z0-9_\.\-@]/i,
10201
10202         /**
10203          * The function used to validate URLs
10204          * @param {String} value The URL
10205          */
10206         'url' : function(v){
10207             return url.test(v);
10208         },
10209         /**
10210          * The error text to display when the url validation function returns false
10211          * @type String
10212          */
10213         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10214         
10215         /**
10216          * The function used to validate alpha values
10217          * @param {String} value The value
10218          */
10219         'alpha' : function(v){
10220             return alpha.test(v);
10221         },
10222         /**
10223          * The error text to display when the alpha validation function returns false
10224          * @type String
10225          */
10226         'alphaText' : 'This field should only contain letters and _',
10227         /**
10228          * The keystroke filter mask to be applied on alpha input
10229          * @type RegExp
10230          */
10231         'alphaMask' : /[a-z_]/i,
10232
10233         /**
10234          * The function used to validate alphanumeric values
10235          * @param {String} value The value
10236          */
10237         'alphanum' : function(v){
10238             return alphanum.test(v);
10239         },
10240         /**
10241          * The error text to display when the alphanumeric validation function returns false
10242          * @type String
10243          */
10244         'alphanumText' : 'This field should only contain letters, numbers and _',
10245         /**
10246          * The keystroke filter mask to be applied on alphanumeric input
10247          * @type RegExp
10248          */
10249         'alphanumMask' : /[a-z0-9_]/i
10250     };
10251 }();/*
10252  * - LGPL
10253  *
10254  * Input
10255  * 
10256  */
10257
10258 /**
10259  * @class Roo.bootstrap.Input
10260  * @extends Roo.bootstrap.Component
10261  * Bootstrap Input class
10262  * @cfg {Boolean} disabled is it disabled
10263  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10264  * @cfg {String} name name of the input
10265  * @cfg {string} fieldLabel - the label associated
10266  * @cfg {string} placeholder - placeholder to put in text.
10267  * @cfg {string}  before - input group add on before
10268  * @cfg {string} after - input group add on after
10269  * @cfg {string} size - (lg|sm) or leave empty..
10270  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10271  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10272  * @cfg {Number} md colspan out of 12 for computer-sized screens
10273  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10274  * @cfg {string} value default value of the input
10275  * @cfg {Number} labelWidth set the width of label 
10276  * @cfg {Number} labellg set the width of label (1-12)
10277  * @cfg {Number} labelmd set the width of label (1-12)
10278  * @cfg {Number} labelsm set the width of label (1-12)
10279  * @cfg {Number} labelxs set the width of label (1-12)
10280  * @cfg {String} labelAlign (top|left)
10281  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10282  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10283  * @cfg {String} indicatorpos (left|right) default left
10284  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10285  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10286  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10287
10288  * @cfg {String} align (left|center|right) Default left
10289  * @cfg {Boolean} forceFeedback (true|false) Default false
10290  * 
10291  * @constructor
10292  * Create a new Input
10293  * @param {Object} config The config object
10294  */
10295
10296 Roo.bootstrap.Input = function(config){
10297     
10298     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10299     
10300     this.addEvents({
10301         /**
10302          * @event focus
10303          * Fires when this field receives input focus.
10304          * @param {Roo.form.Field} this
10305          */
10306         focus : true,
10307         /**
10308          * @event blur
10309          * Fires when this field loses input focus.
10310          * @param {Roo.form.Field} this
10311          */
10312         blur : true,
10313         /**
10314          * @event specialkey
10315          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10316          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10317          * @param {Roo.form.Field} this
10318          * @param {Roo.EventObject} e The event object
10319          */
10320         specialkey : true,
10321         /**
10322          * @event change
10323          * Fires just before the field blurs if the field value has changed.
10324          * @param {Roo.form.Field} this
10325          * @param {Mixed} newValue The new value
10326          * @param {Mixed} oldValue The original value
10327          */
10328         change : true,
10329         /**
10330          * @event invalid
10331          * Fires after the field has been marked as invalid.
10332          * @param {Roo.form.Field} this
10333          * @param {String} msg The validation message
10334          */
10335         invalid : true,
10336         /**
10337          * @event valid
10338          * Fires after the field has been validated with no errors.
10339          * @param {Roo.form.Field} this
10340          */
10341         valid : true,
10342          /**
10343          * @event keyup
10344          * Fires after the key up
10345          * @param {Roo.form.Field} this
10346          * @param {Roo.EventObject}  e The event Object
10347          */
10348         keyup : true
10349     });
10350 };
10351
10352 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10353      /**
10354      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10355       automatic validation (defaults to "keyup").
10356      */
10357     validationEvent : "keyup",
10358      /**
10359      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10360      */
10361     validateOnBlur : true,
10362     /**
10363      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10364      */
10365     validationDelay : 250,
10366      /**
10367      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10368      */
10369     focusClass : "x-form-focus",  // not needed???
10370     
10371        
10372     /**
10373      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10374      */
10375     invalidClass : "has-warning",
10376     
10377     /**
10378      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10379      */
10380     validClass : "has-success",
10381     
10382     /**
10383      * @cfg {Boolean} hasFeedback (true|false) default true
10384      */
10385     hasFeedback : true,
10386     
10387     /**
10388      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10389      */
10390     invalidFeedbackClass : "glyphicon-warning-sign",
10391     
10392     /**
10393      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10394      */
10395     validFeedbackClass : "glyphicon-ok",
10396     
10397     /**
10398      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10399      */
10400     selectOnFocus : false,
10401     
10402      /**
10403      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10404      */
10405     maskRe : null,
10406        /**
10407      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10408      */
10409     vtype : null,
10410     
10411       /**
10412      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10413      */
10414     disableKeyFilter : false,
10415     
10416        /**
10417      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10418      */
10419     disabled : false,
10420      /**
10421      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10422      */
10423     allowBlank : true,
10424     /**
10425      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10426      */
10427     blankText : "Please complete this mandatory field",
10428     
10429      /**
10430      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10431      */
10432     minLength : 0,
10433     /**
10434      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10435      */
10436     maxLength : Number.MAX_VALUE,
10437     /**
10438      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10439      */
10440     minLengthText : "The minimum length for this field is {0}",
10441     /**
10442      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10443      */
10444     maxLengthText : "The maximum length for this field is {0}",
10445   
10446     
10447     /**
10448      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10449      * If available, this function will be called only after the basic validators all return true, and will be passed the
10450      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10451      */
10452     validator : null,
10453     /**
10454      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10455      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10456      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10457      */
10458     regex : null,
10459     /**
10460      * @cfg {String} regexText -- Depricated - use Invalid Text
10461      */
10462     regexText : "",
10463     
10464     /**
10465      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10466      */
10467     invalidText : "",
10468     
10469     
10470     
10471     autocomplete: false,
10472     
10473     
10474     fieldLabel : '',
10475     inputType : 'text',
10476     
10477     name : false,
10478     placeholder: false,
10479     before : false,
10480     after : false,
10481     size : false,
10482     hasFocus : false,
10483     preventMark: false,
10484     isFormField : true,
10485     value : '',
10486     labelWidth : 2,
10487     labelAlign : false,
10488     readOnly : false,
10489     align : false,
10490     formatedValue : false,
10491     forceFeedback : false,
10492     
10493     indicatorpos : 'left',
10494     
10495     labellg : 0,
10496     labelmd : 0,
10497     labelsm : 0,
10498     labelxs : 0,
10499     
10500     capture : '',
10501     accept : '',
10502     
10503     parentLabelAlign : function()
10504     {
10505         var parent = this;
10506         while (parent.parent()) {
10507             parent = parent.parent();
10508             if (typeof(parent.labelAlign) !='undefined') {
10509                 return parent.labelAlign;
10510             }
10511         }
10512         return 'left';
10513         
10514     },
10515     
10516     getAutoCreate : function()
10517     {
10518         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10519         
10520         var id = Roo.id();
10521         
10522         var cfg = {};
10523         
10524         if(this.inputType != 'hidden'){
10525             cfg.cls = 'form-group' //input-group
10526         }
10527         
10528         var input =  {
10529             tag: 'input',
10530             id : id,
10531             type : this.inputType,
10532             value : this.value,
10533             cls : 'form-control',
10534             placeholder : this.placeholder || '',
10535             autocomplete : this.autocomplete || 'new-password'
10536         };
10537         if (this.inputType == 'file') {
10538             input.style = 'overflow:hidden'; // why not in CSS?
10539         }
10540         
10541         if(this.capture.length){
10542             input.capture = this.capture;
10543         }
10544         
10545         if(this.accept.length){
10546             input.accept = this.accept + "/*";
10547         }
10548         
10549         if(this.align){
10550             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10551         }
10552         
10553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10554             input.maxLength = this.maxLength;
10555         }
10556         
10557         if (this.disabled) {
10558             input.disabled=true;
10559         }
10560         
10561         if (this.readOnly) {
10562             input.readonly=true;
10563         }
10564         
10565         if (this.name) {
10566             input.name = this.name;
10567         }
10568         
10569         if (this.size) {
10570             input.cls += ' input-' + this.size;
10571         }
10572         
10573         var settings=this;
10574         ['xs','sm','md','lg'].map(function(size){
10575             if (settings[size]) {
10576                 cfg.cls += ' col-' + size + '-' + settings[size];
10577             }
10578         });
10579         
10580         var inputblock = input;
10581         
10582         var feedback = {
10583             tag: 'span',
10584             cls: 'glyphicon form-control-feedback'
10585         };
10586             
10587         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10588             
10589             inputblock = {
10590                 cls : 'has-feedback',
10591                 cn :  [
10592                     input,
10593                     feedback
10594                 ] 
10595             };  
10596         }
10597         
10598         if (this.before || this.after) {
10599             
10600             inputblock = {
10601                 cls : 'input-group',
10602                 cn :  [] 
10603             };
10604             
10605             if (this.before && typeof(this.before) == 'string') {
10606                 
10607                 inputblock.cn.push({
10608                     tag :'span',
10609                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10610                     html : this.before
10611                 });
10612             }
10613             if (this.before && typeof(this.before) == 'object') {
10614                 this.before = Roo.factory(this.before);
10615                 
10616                 inputblock.cn.push({
10617                     tag :'span',
10618                     cls : 'roo-input-before input-group-prepend   input-group-' +
10619                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10620                 });
10621             }
10622             
10623             inputblock.cn.push(input);
10624             
10625             if (this.after && typeof(this.after) == 'string') {
10626                 inputblock.cn.push({
10627                     tag :'span',
10628                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10629                     html : this.after
10630                 });
10631             }
10632             if (this.after && typeof(this.after) == 'object') {
10633                 this.after = Roo.factory(this.after);
10634                 
10635                 inputblock.cn.push({
10636                     tag :'span',
10637                     cls : 'roo-input-after input-group-append  input-group-' +
10638                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10639                 });
10640             }
10641             
10642             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10643                 inputblock.cls += ' has-feedback';
10644                 inputblock.cn.push(feedback);
10645             }
10646         };
10647         var indicator = {
10648             tag : 'i',
10649             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10650             tooltip : 'This field is required'
10651         };
10652         if (this.allowBlank ) {
10653             indicator.style = this.allowBlank ? ' display:none' : '';
10654         }
10655         if (align ==='left' && this.fieldLabel.length) {
10656             
10657             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10658             
10659             cfg.cn = [
10660                 indicator,
10661                 {
10662                     tag: 'label',
10663                     'for' :  id,
10664                     cls : 'control-label col-form-label',
10665                     html : this.fieldLabel
10666
10667                 },
10668                 {
10669                     cls : "", 
10670                     cn: [
10671                         inputblock
10672                     ]
10673                 }
10674             ];
10675             
10676             var labelCfg = cfg.cn[1];
10677             var contentCfg = cfg.cn[2];
10678             
10679             if(this.indicatorpos == 'right'){
10680                 cfg.cn = [
10681                     {
10682                         tag: 'label',
10683                         'for' :  id,
10684                         cls : 'control-label col-form-label',
10685                         cn : [
10686                             {
10687                                 tag : 'span',
10688                                 html : this.fieldLabel
10689                             },
10690                             indicator
10691                         ]
10692                     },
10693                     {
10694                         cls : "",
10695                         cn: [
10696                             inputblock
10697                         ]
10698                     }
10699
10700                 ];
10701                 
10702                 labelCfg = cfg.cn[0];
10703                 contentCfg = cfg.cn[1];
10704             
10705             }
10706             
10707             if(this.labelWidth > 12){
10708                 labelCfg.style = "width: " + this.labelWidth + 'px';
10709             }
10710             
10711             if(this.labelWidth < 13 && this.labelmd == 0){
10712                 this.labelmd = this.labelWidth;
10713             }
10714             
10715             if(this.labellg > 0){
10716                 labelCfg.cls += ' col-lg-' + this.labellg;
10717                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10718             }
10719             
10720             if(this.labelmd > 0){
10721                 labelCfg.cls += ' col-md-' + this.labelmd;
10722                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10723             }
10724             
10725             if(this.labelsm > 0){
10726                 labelCfg.cls += ' col-sm-' + this.labelsm;
10727                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10728             }
10729             
10730             if(this.labelxs > 0){
10731                 labelCfg.cls += ' col-xs-' + this.labelxs;
10732                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10733             }
10734             
10735             
10736         } else if ( this.fieldLabel.length) {
10737                 
10738             
10739             
10740             cfg.cn = [
10741                 {
10742                     tag : 'i',
10743                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10744                     tooltip : 'This field is required',
10745                     style : this.allowBlank ? ' display:none' : '' 
10746                 },
10747                 {
10748                     tag: 'label',
10749                    //cls : 'input-group-addon',
10750                     html : this.fieldLabel
10751
10752                 },
10753
10754                inputblock
10755
10756            ];
10757            
10758            if(this.indicatorpos == 'right'){
10759        
10760                 cfg.cn = [
10761                     {
10762                         tag: 'label',
10763                        //cls : 'input-group-addon',
10764                         html : this.fieldLabel
10765
10766                     },
10767                     {
10768                         tag : 'i',
10769                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10770                         tooltip : 'This field is required',
10771                         style : this.allowBlank ? ' display:none' : '' 
10772                     },
10773
10774                    inputblock
10775
10776                ];
10777
10778             }
10779
10780         } else {
10781             
10782             cfg.cn = [
10783
10784                     inputblock
10785
10786             ];
10787                 
10788                 
10789         };
10790         
10791         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10792            cfg.cls += ' navbar-form';
10793         }
10794         
10795         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10796             // on BS4 we do this only if not form 
10797             cfg.cls += ' navbar-form';
10798             cfg.tag = 'li';
10799         }
10800         
10801         return cfg;
10802         
10803     },
10804     /**
10805      * return the real input element.
10806      */
10807     inputEl: function ()
10808     {
10809         return this.el.select('input.form-control',true).first();
10810     },
10811     
10812     tooltipEl : function()
10813     {
10814         return this.inputEl();
10815     },
10816     
10817     indicatorEl : function()
10818     {
10819         if (Roo.bootstrap.version == 4) {
10820             return false; // not enabled in v4 yet.
10821         }
10822         
10823         var indicator = this.el.select('i.roo-required-indicator',true).first();
10824         
10825         if(!indicator){
10826             return false;
10827         }
10828         
10829         return indicator;
10830         
10831     },
10832     
10833     setDisabled : function(v)
10834     {
10835         var i  = this.inputEl().dom;
10836         if (!v) {
10837             i.removeAttribute('disabled');
10838             return;
10839             
10840         }
10841         i.setAttribute('disabled','true');
10842     },
10843     initEvents : function()
10844     {
10845           
10846         this.inputEl().on("keydown" , this.fireKey,  this);
10847         this.inputEl().on("focus", this.onFocus,  this);
10848         this.inputEl().on("blur", this.onBlur,  this);
10849         
10850         this.inputEl().relayEvent('keyup', this);
10851         
10852         this.indicator = this.indicatorEl();
10853         
10854         if(this.indicator){
10855             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10856         }
10857  
10858         // reference to original value for reset
10859         this.originalValue = this.getValue();
10860         //Roo.form.TextField.superclass.initEvents.call(this);
10861         if(this.validationEvent == 'keyup'){
10862             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10863             this.inputEl().on('keyup', this.filterValidation, this);
10864         }
10865         else if(this.validationEvent !== false){
10866             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10867         }
10868         
10869         if(this.selectOnFocus){
10870             this.on("focus", this.preFocus, this);
10871             
10872         }
10873         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10874             this.inputEl().on("keypress", this.filterKeys, this);
10875         } else {
10876             this.inputEl().relayEvent('keypress', this);
10877         }
10878        /* if(this.grow){
10879             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10880             this.el.on("click", this.autoSize,  this);
10881         }
10882         */
10883         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10884             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10885         }
10886         
10887         if (typeof(this.before) == 'object') {
10888             this.before.render(this.el.select('.roo-input-before',true).first());
10889         }
10890         if (typeof(this.after) == 'object') {
10891             this.after.render(this.el.select('.roo-input-after',true).first());
10892         }
10893         
10894         this.inputEl().on('change', this.onChange, this);
10895         
10896     },
10897     filterValidation : function(e){
10898         if(!e.isNavKeyPress()){
10899             this.validationTask.delay(this.validationDelay);
10900         }
10901     },
10902      /**
10903      * Validates the field value
10904      * @return {Boolean} True if the value is valid, else false
10905      */
10906     validate : function(){
10907         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10908         if(this.disabled || this.validateValue(this.getRawValue())){
10909             this.markValid();
10910             return true;
10911         }
10912         
10913         this.markInvalid();
10914         return false;
10915     },
10916     
10917     
10918     /**
10919      * Validates a value according to the field's validation rules and marks the field as invalid
10920      * if the validation fails
10921      * @param {Mixed} value The value to validate
10922      * @return {Boolean} True if the value is valid, else false
10923      */
10924     validateValue : function(value)
10925     {
10926         if(this.getVisibilityEl().hasClass('hidden')){
10927             return true;
10928         }
10929         
10930         if(value.length < 1)  { // if it's blank
10931             if(this.allowBlank){
10932                 return true;
10933             }
10934             return false;
10935         }
10936         
10937         if(value.length < this.minLength){
10938             return false;
10939         }
10940         if(value.length > this.maxLength){
10941             return false;
10942         }
10943         if(this.vtype){
10944             var vt = Roo.form.VTypes;
10945             if(!vt[this.vtype](value, this)){
10946                 return false;
10947             }
10948         }
10949         if(typeof this.validator == "function"){
10950             var msg = this.validator(value);
10951             if(msg !== true){
10952                 return false;
10953             }
10954             if (typeof(msg) == 'string') {
10955                 this.invalidText = msg;
10956             }
10957         }
10958         
10959         if(this.regex && !this.regex.test(value)){
10960             return false;
10961         }
10962         
10963         return true;
10964     },
10965     
10966      // private
10967     fireKey : function(e){
10968         //Roo.log('field ' + e.getKey());
10969         if(e.isNavKeyPress()){
10970             this.fireEvent("specialkey", this, e);
10971         }
10972     },
10973     focus : function (selectText){
10974         if(this.rendered){
10975             this.inputEl().focus();
10976             if(selectText === true){
10977                 this.inputEl().dom.select();
10978             }
10979         }
10980         return this;
10981     } ,
10982     
10983     onFocus : function(){
10984         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10985            // this.el.addClass(this.focusClass);
10986         }
10987         if(!this.hasFocus){
10988             this.hasFocus = true;
10989             this.startValue = this.getValue();
10990             this.fireEvent("focus", this);
10991         }
10992     },
10993     
10994     beforeBlur : Roo.emptyFn,
10995
10996     
10997     // private
10998     onBlur : function(){
10999         this.beforeBlur();
11000         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11001             //this.el.removeClass(this.focusClass);
11002         }
11003         this.hasFocus = false;
11004         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11005             this.validate();
11006         }
11007         var v = this.getValue();
11008         if(String(v) !== String(this.startValue)){
11009             this.fireEvent('change', this, v, this.startValue);
11010         }
11011         this.fireEvent("blur", this);
11012     },
11013     
11014     onChange : function(e)
11015     {
11016         var v = this.getValue();
11017         if(String(v) !== String(this.startValue)){
11018             this.fireEvent('change', this, v, this.startValue);
11019         }
11020         
11021     },
11022     
11023     /**
11024      * Resets the current field value to the originally loaded value and clears any validation messages
11025      */
11026     reset : function(){
11027         this.setValue(this.originalValue);
11028         this.validate();
11029     },
11030      /**
11031      * Returns the name of the field
11032      * @return {Mixed} name The name field
11033      */
11034     getName: function(){
11035         return this.name;
11036     },
11037      /**
11038      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11039      * @return {Mixed} value The field value
11040      */
11041     getValue : function(){
11042         
11043         var v = this.inputEl().getValue();
11044         
11045         return v;
11046     },
11047     /**
11048      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11049      * @return {Mixed} value The field value
11050      */
11051     getRawValue : function(){
11052         var v = this.inputEl().getValue();
11053         
11054         return v;
11055     },
11056     
11057     /**
11058      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11059      * @param {Mixed} value The value to set
11060      */
11061     setRawValue : function(v){
11062         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11063     },
11064     
11065     selectText : function(start, end){
11066         var v = this.getRawValue();
11067         if(v.length > 0){
11068             start = start === undefined ? 0 : start;
11069             end = end === undefined ? v.length : end;
11070             var d = this.inputEl().dom;
11071             if(d.setSelectionRange){
11072                 d.setSelectionRange(start, end);
11073             }else if(d.createTextRange){
11074                 var range = d.createTextRange();
11075                 range.moveStart("character", start);
11076                 range.moveEnd("character", v.length-end);
11077                 range.select();
11078             }
11079         }
11080     },
11081     
11082     /**
11083      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11084      * @param {Mixed} value The value to set
11085      */
11086     setValue : function(v){
11087         this.value = v;
11088         if(this.rendered){
11089             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11090             this.validate();
11091         }
11092     },
11093     
11094     /*
11095     processValue : function(value){
11096         if(this.stripCharsRe){
11097             var newValue = value.replace(this.stripCharsRe, '');
11098             if(newValue !== value){
11099                 this.setRawValue(newValue);
11100                 return newValue;
11101             }
11102         }
11103         return value;
11104     },
11105   */
11106     preFocus : function(){
11107         
11108         if(this.selectOnFocus){
11109             this.inputEl().dom.select();
11110         }
11111     },
11112     filterKeys : function(e){
11113         var k = e.getKey();
11114         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11115             return;
11116         }
11117         var c = e.getCharCode(), cc = String.fromCharCode(c);
11118         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11119             return;
11120         }
11121         if(!this.maskRe.test(cc)){
11122             e.stopEvent();
11123         }
11124     },
11125      /**
11126      * Clear any invalid styles/messages for this field
11127      */
11128     clearInvalid : function(){
11129         
11130         if(!this.el || this.preventMark){ // not rendered
11131             return;
11132         }
11133         
11134         
11135         this.el.removeClass([this.invalidClass, 'is-invalid']);
11136         
11137         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11138             
11139             var feedback = this.el.select('.form-control-feedback', true).first();
11140             
11141             if(feedback){
11142                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11143             }
11144             
11145         }
11146         
11147         if(this.indicator){
11148             this.indicator.removeClass('visible');
11149             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11150         }
11151         
11152         this.fireEvent('valid', this);
11153     },
11154     
11155      /**
11156      * Mark this field as valid
11157      */
11158     markValid : function()
11159     {
11160         if(!this.el  || this.preventMark){ // not rendered...
11161             return;
11162         }
11163         
11164         this.el.removeClass([this.invalidClass, this.validClass]);
11165         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11166
11167         var feedback = this.el.select('.form-control-feedback', true).first();
11168             
11169         if(feedback){
11170             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11171         }
11172         
11173         if(this.indicator){
11174             this.indicator.removeClass('visible');
11175             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11176         }
11177         
11178         if(this.disabled){
11179             return;
11180         }
11181         
11182            
11183         if(this.allowBlank && !this.getRawValue().length){
11184             return;
11185         }
11186         if (Roo.bootstrap.version == 3) {
11187             this.el.addClass(this.validClass);
11188         } else {
11189             this.inputEl().addClass('is-valid');
11190         }
11191
11192         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11193             
11194             var feedback = this.el.select('.form-control-feedback', true).first();
11195             
11196             if(feedback){
11197                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11198                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11199             }
11200             
11201         }
11202         
11203         this.fireEvent('valid', this);
11204     },
11205     
11206      /**
11207      * Mark this field as invalid
11208      * @param {String} msg The validation message
11209      */
11210     markInvalid : function(msg)
11211     {
11212         if(!this.el  || this.preventMark){ // not rendered
11213             return;
11214         }
11215         
11216         this.el.removeClass([this.invalidClass, this.validClass]);
11217         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11218         
11219         var feedback = this.el.select('.form-control-feedback', true).first();
11220             
11221         if(feedback){
11222             this.el.select('.form-control-feedback', true).first().removeClass(
11223                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11224         }
11225
11226         if(this.disabled){
11227             return;
11228         }
11229         
11230         if(this.allowBlank && !this.getRawValue().length){
11231             return;
11232         }
11233         
11234         if(this.indicator){
11235             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11236             this.indicator.addClass('visible');
11237         }
11238         if (Roo.bootstrap.version == 3) {
11239             this.el.addClass(this.invalidClass);
11240         } else {
11241             this.inputEl().addClass('is-invalid');
11242         }
11243         
11244         
11245         
11246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11247             
11248             var feedback = this.el.select('.form-control-feedback', true).first();
11249             
11250             if(feedback){
11251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11252                 
11253                 if(this.getValue().length || this.forceFeedback){
11254                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11255                 }
11256                 
11257             }
11258             
11259         }
11260         
11261         this.fireEvent('invalid', this, msg);
11262     },
11263     // private
11264     SafariOnKeyDown : function(event)
11265     {
11266         // this is a workaround for a password hang bug on chrome/ webkit.
11267         if (this.inputEl().dom.type != 'password') {
11268             return;
11269         }
11270         
11271         var isSelectAll = false;
11272         
11273         if(this.inputEl().dom.selectionEnd > 0){
11274             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11275         }
11276         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11277             event.preventDefault();
11278             this.setValue('');
11279             return;
11280         }
11281         
11282         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11283             
11284             event.preventDefault();
11285             // this is very hacky as keydown always get's upper case.
11286             //
11287             var cc = String.fromCharCode(event.getCharCode());
11288             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11289             
11290         }
11291     },
11292     adjustWidth : function(tag, w){
11293         tag = tag.toLowerCase();
11294         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11295             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11296                 if(tag == 'input'){
11297                     return w + 2;
11298                 }
11299                 if(tag == 'textarea'){
11300                     return w-2;
11301                 }
11302             }else if(Roo.isOpera){
11303                 if(tag == 'input'){
11304                     return w + 2;
11305                 }
11306                 if(tag == 'textarea'){
11307                     return w-2;
11308                 }
11309             }
11310         }
11311         return w;
11312     },
11313     
11314     setFieldLabel : function(v)
11315     {
11316         if(!this.rendered){
11317             return;
11318         }
11319         
11320         if(this.indicatorEl()){
11321             var ar = this.el.select('label > span',true);
11322             
11323             if (ar.elements.length) {
11324                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11325                 this.fieldLabel = v;
11326                 return;
11327             }
11328             
11329             var br = this.el.select('label',true);
11330             
11331             if(br.elements.length) {
11332                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11333                 this.fieldLabel = v;
11334                 return;
11335             }
11336             
11337             Roo.log('Cannot Found any of label > span || label in input');
11338             return;
11339         }
11340         
11341         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11342         this.fieldLabel = v;
11343         
11344         
11345     }
11346 });
11347
11348  
11349 /*
11350  * - LGPL
11351  *
11352  * Input
11353  * 
11354  */
11355
11356 /**
11357  * @class Roo.bootstrap.TextArea
11358  * @extends Roo.bootstrap.Input
11359  * Bootstrap TextArea class
11360  * @cfg {Number} cols Specifies the visible width of a text area
11361  * @cfg {Number} rows Specifies the visible number of lines in a text area
11362  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11363  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11364  * @cfg {string} html text
11365  * 
11366  * @constructor
11367  * Create a new TextArea
11368  * @param {Object} config The config object
11369  */
11370
11371 Roo.bootstrap.TextArea = function(config){
11372     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11373    
11374 };
11375
11376 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11377      
11378     cols : false,
11379     rows : 5,
11380     readOnly : false,
11381     warp : 'soft',
11382     resize : false,
11383     value: false,
11384     html: false,
11385     
11386     getAutoCreate : function(){
11387         
11388         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11389         
11390         var id = Roo.id();
11391         
11392         var cfg = {};
11393         
11394         if(this.inputType != 'hidden'){
11395             cfg.cls = 'form-group' //input-group
11396         }
11397         
11398         var input =  {
11399             tag: 'textarea',
11400             id : id,
11401             warp : this.warp,
11402             rows : this.rows,
11403             value : this.value || '',
11404             html: this.html || '',
11405             cls : 'form-control',
11406             placeholder : this.placeholder || '' 
11407             
11408         };
11409         
11410         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11411             input.maxLength = this.maxLength;
11412         }
11413         
11414         if(this.resize){
11415             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11416         }
11417         
11418         if(this.cols){
11419             input.cols = this.cols;
11420         }
11421         
11422         if (this.readOnly) {
11423             input.readonly = true;
11424         }
11425         
11426         if (this.name) {
11427             input.name = this.name;
11428         }
11429         
11430         if (this.size) {
11431             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11432         }
11433         
11434         var settings=this;
11435         ['xs','sm','md','lg'].map(function(size){
11436             if (settings[size]) {
11437                 cfg.cls += ' col-' + size + '-' + settings[size];
11438             }
11439         });
11440         
11441         var inputblock = input;
11442         
11443         if(this.hasFeedback && !this.allowBlank){
11444             
11445             var feedback = {
11446                 tag: 'span',
11447                 cls: 'glyphicon form-control-feedback'
11448             };
11449
11450             inputblock = {
11451                 cls : 'has-feedback',
11452                 cn :  [
11453                     input,
11454                     feedback
11455                 ] 
11456             };  
11457         }
11458         
11459         
11460         if (this.before || this.after) {
11461             
11462             inputblock = {
11463                 cls : 'input-group',
11464                 cn :  [] 
11465             };
11466             if (this.before) {
11467                 inputblock.cn.push({
11468                     tag :'span',
11469                     cls : 'input-group-addon',
11470                     html : this.before
11471                 });
11472             }
11473             
11474             inputblock.cn.push(input);
11475             
11476             if(this.hasFeedback && !this.allowBlank){
11477                 inputblock.cls += ' has-feedback';
11478                 inputblock.cn.push(feedback);
11479             }
11480             
11481             if (this.after) {
11482                 inputblock.cn.push({
11483                     tag :'span',
11484                     cls : 'input-group-addon',
11485                     html : this.after
11486                 });
11487             }
11488             
11489         }
11490         
11491         if (align ==='left' && this.fieldLabel.length) {
11492             cfg.cn = [
11493                 {
11494                     tag: 'label',
11495                     'for' :  id,
11496                     cls : 'control-label',
11497                     html : this.fieldLabel
11498                 },
11499                 {
11500                     cls : "",
11501                     cn: [
11502                         inputblock
11503                     ]
11504                 }
11505
11506             ];
11507             
11508             if(this.labelWidth > 12){
11509                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11510             }
11511
11512             if(this.labelWidth < 13 && this.labelmd == 0){
11513                 this.labelmd = this.labelWidth;
11514             }
11515
11516             if(this.labellg > 0){
11517                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11518                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11519             }
11520
11521             if(this.labelmd > 0){
11522                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11523                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11524             }
11525
11526             if(this.labelsm > 0){
11527                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11528                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11529             }
11530
11531             if(this.labelxs > 0){
11532                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11533                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11534             }
11535             
11536         } else if ( this.fieldLabel.length) {
11537             cfg.cn = [
11538
11539                {
11540                    tag: 'label',
11541                    //cls : 'input-group-addon',
11542                    html : this.fieldLabel
11543
11544                },
11545
11546                inputblock
11547
11548            ];
11549
11550         } else {
11551
11552             cfg.cn = [
11553
11554                 inputblock
11555
11556             ];
11557                 
11558         }
11559         
11560         if (this.disabled) {
11561             input.disabled=true;
11562         }
11563         
11564         return cfg;
11565         
11566     },
11567     /**
11568      * return the real textarea element.
11569      */
11570     inputEl: function ()
11571     {
11572         return this.el.select('textarea.form-control',true).first();
11573     },
11574     
11575     /**
11576      * Clear any invalid styles/messages for this field
11577      */
11578     clearInvalid : function()
11579     {
11580         
11581         if(!this.el || this.preventMark){ // not rendered
11582             return;
11583         }
11584         
11585         var label = this.el.select('label', true).first();
11586         var icon = this.el.select('i.fa-star', true).first();
11587         
11588         if(label && icon){
11589             icon.remove();
11590         }
11591         this.el.removeClass( this.validClass);
11592         this.inputEl().removeClass('is-invalid');
11593          
11594         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11595             
11596             var feedback = this.el.select('.form-control-feedback', true).first();
11597             
11598             if(feedback){
11599                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11600             }
11601             
11602         }
11603         
11604         this.fireEvent('valid', this);
11605     },
11606     
11607      /**
11608      * Mark this field as valid
11609      */
11610     markValid : function()
11611     {
11612         if(!this.el  || this.preventMark){ // not rendered
11613             return;
11614         }
11615         
11616         this.el.removeClass([this.invalidClass, this.validClass]);
11617         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11618         
11619         var feedback = this.el.select('.form-control-feedback', true).first();
11620             
11621         if(feedback){
11622             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11623         }
11624
11625         if(this.disabled || this.allowBlank){
11626             return;
11627         }
11628         
11629         var label = this.el.select('label', true).first();
11630         var icon = this.el.select('i.fa-star', true).first();
11631         
11632         if(label && icon){
11633             icon.remove();
11634         }
11635         if (Roo.bootstrap.version == 3) {
11636             this.el.addClass(this.validClass);
11637         } else {
11638             this.inputEl().addClass('is-valid');
11639         }
11640         
11641         
11642         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11643             
11644             var feedback = this.el.select('.form-control-feedback', true).first();
11645             
11646             if(feedback){
11647                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11648                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11649             }
11650             
11651         }
11652         
11653         this.fireEvent('valid', this);
11654     },
11655     
11656      /**
11657      * Mark this field as invalid
11658      * @param {String} msg The validation message
11659      */
11660     markInvalid : function(msg)
11661     {
11662         if(!this.el  || this.preventMark){ // not rendered
11663             return;
11664         }
11665         
11666         this.el.removeClass([this.invalidClass, this.validClass]);
11667         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11668         
11669         var feedback = this.el.select('.form-control-feedback', true).first();
11670             
11671         if(feedback){
11672             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11673         }
11674
11675         if(this.disabled || this.allowBlank){
11676             return;
11677         }
11678         
11679         var label = this.el.select('label', true).first();
11680         var icon = this.el.select('i.fa-star', true).first();
11681         
11682         if(!this.getValue().length && label && !icon){
11683             this.el.createChild({
11684                 tag : 'i',
11685                 cls : 'text-danger fa fa-lg fa-star',
11686                 tooltip : 'This field is required',
11687                 style : 'margin-right:5px;'
11688             }, label, true);
11689         }
11690         
11691         if (Roo.bootstrap.version == 3) {
11692             this.el.addClass(this.invalidClass);
11693         } else {
11694             this.inputEl().addClass('is-invalid');
11695         }
11696         
11697         // fixme ... this may be depricated need to test..
11698         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11699             
11700             var feedback = this.el.select('.form-control-feedback', true).first();
11701             
11702             if(feedback){
11703                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11704                 
11705                 if(this.getValue().length || this.forceFeedback){
11706                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11707                 }
11708                 
11709             }
11710             
11711         }
11712         
11713         this.fireEvent('invalid', this, msg);
11714     }
11715 });
11716
11717  
11718 /*
11719  * - LGPL
11720  *
11721  * trigger field - base class for combo..
11722  * 
11723  */
11724  
11725 /**
11726  * @class Roo.bootstrap.TriggerField
11727  * @extends Roo.bootstrap.Input
11728  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11729  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11730  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11731  * for which you can provide a custom implementation.  For example:
11732  * <pre><code>
11733 var trigger = new Roo.bootstrap.TriggerField();
11734 trigger.onTriggerClick = myTriggerFn;
11735 trigger.applyTo('my-field');
11736 </code></pre>
11737  *
11738  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11739  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11740  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11741  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11742  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11743
11744  * @constructor
11745  * Create a new TriggerField.
11746  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11747  * to the base TextField)
11748  */
11749 Roo.bootstrap.TriggerField = function(config){
11750     this.mimicing = false;
11751     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11752 };
11753
11754 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11755     /**
11756      * @cfg {String} triggerClass A CSS class to apply to the trigger
11757      */
11758      /**
11759      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11760      */
11761     hideTrigger:false,
11762
11763     /**
11764      * @cfg {Boolean} removable (true|false) special filter default false
11765      */
11766     removable : false,
11767     
11768     /** @cfg {Boolean} grow @hide */
11769     /** @cfg {Number} growMin @hide */
11770     /** @cfg {Number} growMax @hide */
11771
11772     /**
11773      * @hide 
11774      * @method
11775      */
11776     autoSize: Roo.emptyFn,
11777     // private
11778     monitorTab : true,
11779     // private
11780     deferHeight : true,
11781
11782     
11783     actionMode : 'wrap',
11784     
11785     caret : false,
11786     
11787     
11788     getAutoCreate : function(){
11789        
11790         var align = this.labelAlign || this.parentLabelAlign();
11791         
11792         var id = Roo.id();
11793         
11794         var cfg = {
11795             cls: 'form-group' //input-group
11796         };
11797         
11798         
11799         var input =  {
11800             tag: 'input',
11801             id : id,
11802             type : this.inputType,
11803             cls : 'form-control',
11804             autocomplete: 'new-password',
11805             placeholder : this.placeholder || '' 
11806             
11807         };
11808         if (this.name) {
11809             input.name = this.name;
11810         }
11811         if (this.size) {
11812             input.cls += ' input-' + this.size;
11813         }
11814         
11815         if (this.disabled) {
11816             input.disabled=true;
11817         }
11818         
11819         var inputblock = input;
11820         
11821         if(this.hasFeedback && !this.allowBlank){
11822             
11823             var feedback = {
11824                 tag: 'span',
11825                 cls: 'glyphicon form-control-feedback'
11826             };
11827             
11828             if(this.removable && !this.editable  ){
11829                 inputblock = {
11830                     cls : 'has-feedback',
11831                     cn :  [
11832                         inputblock,
11833                         {
11834                             tag: 'button',
11835                             html : 'x',
11836                             cls : 'roo-combo-removable-btn close'
11837                         },
11838                         feedback
11839                     ] 
11840                 };
11841             } else {
11842                 inputblock = {
11843                     cls : 'has-feedback',
11844                     cn :  [
11845                         inputblock,
11846                         feedback
11847                     ] 
11848                 };
11849             }
11850
11851         } else {
11852             if(this.removable && !this.editable ){
11853                 inputblock = {
11854                     cls : 'roo-removable',
11855                     cn :  [
11856                         inputblock,
11857                         {
11858                             tag: 'button',
11859                             html : 'x',
11860                             cls : 'roo-combo-removable-btn close'
11861                         }
11862                     ] 
11863                 };
11864             }
11865         }
11866         
11867         if (this.before || this.after) {
11868             
11869             inputblock = {
11870                 cls : 'input-group',
11871                 cn :  [] 
11872             };
11873             if (this.before) {
11874                 inputblock.cn.push({
11875                     tag :'span',
11876                     cls : 'input-group-addon input-group-prepend input-group-text',
11877                     html : this.before
11878                 });
11879             }
11880             
11881             inputblock.cn.push(input);
11882             
11883             if(this.hasFeedback && !this.allowBlank){
11884                 inputblock.cls += ' has-feedback';
11885                 inputblock.cn.push(feedback);
11886             }
11887             
11888             if (this.after) {
11889                 inputblock.cn.push({
11890                     tag :'span',
11891                     cls : 'input-group-addon input-group-append input-group-text',
11892                     html : this.after
11893                 });
11894             }
11895             
11896         };
11897         
11898       
11899         
11900         var ibwrap = inputblock;
11901         
11902         if(this.multiple){
11903             ibwrap = {
11904                 tag: 'ul',
11905                 cls: 'roo-select2-choices',
11906                 cn:[
11907                     {
11908                         tag: 'li',
11909                         cls: 'roo-select2-search-field',
11910                         cn: [
11911
11912                             inputblock
11913                         ]
11914                     }
11915                 ]
11916             };
11917                 
11918         }
11919         
11920         var combobox = {
11921             cls: 'roo-select2-container input-group',
11922             cn: [
11923                  {
11924                     tag: 'input',
11925                     type : 'hidden',
11926                     cls: 'form-hidden-field'
11927                 },
11928                 ibwrap
11929             ]
11930         };
11931         
11932         if(!this.multiple && this.showToggleBtn){
11933             
11934             var caret = {
11935                         tag: 'span',
11936                         cls: 'caret'
11937              };
11938             if (this.caret != false) {
11939                 caret = {
11940                      tag: 'i',
11941                      cls: 'fa fa-' + this.caret
11942                 };
11943                 
11944             }
11945             
11946             combobox.cn.push({
11947                 tag :'span',
11948                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11949                 cn : [
11950                     Roo.bootstrap.version == 3 ? caret : '',
11951                     {
11952                         tag: 'span',
11953                         cls: 'combobox-clear',
11954                         cn  : [
11955                             {
11956                                 tag : 'i',
11957                                 cls: 'icon-remove'
11958                             }
11959                         ]
11960                     }
11961                 ]
11962
11963             })
11964         }
11965         
11966         if(this.multiple){
11967             combobox.cls += ' roo-select2-container-multi';
11968         }
11969          var indicator = {
11970             tag : 'i',
11971             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11972             tooltip : 'This field is required'
11973         };
11974         if (Roo.bootstrap.version == 4) {
11975             indicator = {
11976                 tag : 'i',
11977                 style : 'display:none'
11978             };
11979         }
11980         
11981         
11982         if (align ==='left' && this.fieldLabel.length) {
11983             
11984             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11985
11986             cfg.cn = [
11987                 indicator,
11988                 {
11989                     tag: 'label',
11990                     'for' :  id,
11991                     cls : 'control-label',
11992                     html : this.fieldLabel
11993
11994                 },
11995                 {
11996                     cls : "", 
11997                     cn: [
11998                         combobox
11999                     ]
12000                 }
12001
12002             ];
12003             
12004             var labelCfg = cfg.cn[1];
12005             var contentCfg = cfg.cn[2];
12006             
12007             if(this.indicatorpos == 'right'){
12008                 cfg.cn = [
12009                     {
12010                         tag: 'label',
12011                         'for' :  id,
12012                         cls : 'control-label',
12013                         cn : [
12014                             {
12015                                 tag : 'span',
12016                                 html : this.fieldLabel
12017                             },
12018                             indicator
12019                         ]
12020                     },
12021                     {
12022                         cls : "", 
12023                         cn: [
12024                             combobox
12025                         ]
12026                     }
12027
12028                 ];
12029                 
12030                 labelCfg = cfg.cn[0];
12031                 contentCfg = cfg.cn[1];
12032             }
12033             
12034             if(this.labelWidth > 12){
12035                 labelCfg.style = "width: " + this.labelWidth + 'px';
12036             }
12037             
12038             if(this.labelWidth < 13 && this.labelmd == 0){
12039                 this.labelmd = this.labelWidth;
12040             }
12041             
12042             if(this.labellg > 0){
12043                 labelCfg.cls += ' col-lg-' + this.labellg;
12044                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12045             }
12046             
12047             if(this.labelmd > 0){
12048                 labelCfg.cls += ' col-md-' + this.labelmd;
12049                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12050             }
12051             
12052             if(this.labelsm > 0){
12053                 labelCfg.cls += ' col-sm-' + this.labelsm;
12054                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12055             }
12056             
12057             if(this.labelxs > 0){
12058                 labelCfg.cls += ' col-xs-' + this.labelxs;
12059                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12060             }
12061             
12062         } else if ( this.fieldLabel.length) {
12063 //                Roo.log(" label");
12064             cfg.cn = [
12065                 indicator,
12066                {
12067                    tag: 'label',
12068                    //cls : 'input-group-addon',
12069                    html : this.fieldLabel
12070
12071                },
12072
12073                combobox
12074
12075             ];
12076             
12077             if(this.indicatorpos == 'right'){
12078                 
12079                 cfg.cn = [
12080                     {
12081                        tag: 'label',
12082                        cn : [
12083                            {
12084                                tag : 'span',
12085                                html : this.fieldLabel
12086                            },
12087                            indicator
12088                        ]
12089
12090                     },
12091                     combobox
12092
12093                 ];
12094
12095             }
12096
12097         } else {
12098             
12099 //                Roo.log(" no label && no align");
12100                 cfg = combobox
12101                      
12102                 
12103         }
12104         
12105         var settings=this;
12106         ['xs','sm','md','lg'].map(function(size){
12107             if (settings[size]) {
12108                 cfg.cls += ' col-' + size + '-' + settings[size];
12109             }
12110         });
12111         
12112         return cfg;
12113         
12114     },
12115     
12116     
12117     
12118     // private
12119     onResize : function(w, h){
12120 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12121 //        if(typeof w == 'number'){
12122 //            var x = w - this.trigger.getWidth();
12123 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12124 //            this.trigger.setStyle('left', x+'px');
12125 //        }
12126     },
12127
12128     // private
12129     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12130
12131     // private
12132     getResizeEl : function(){
12133         return this.inputEl();
12134     },
12135
12136     // private
12137     getPositionEl : function(){
12138         return this.inputEl();
12139     },
12140
12141     // private
12142     alignErrorIcon : function(){
12143         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12144     },
12145
12146     // private
12147     initEvents : function(){
12148         
12149         this.createList();
12150         
12151         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12152         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12153         if(!this.multiple && this.showToggleBtn){
12154             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12155             if(this.hideTrigger){
12156                 this.trigger.setDisplayed(false);
12157             }
12158             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12159         }
12160         
12161         if(this.multiple){
12162             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12163         }
12164         
12165         if(this.removable && !this.editable && !this.tickable){
12166             var close = this.closeTriggerEl();
12167             
12168             if(close){
12169                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12170                 close.on('click', this.removeBtnClick, this, close);
12171             }
12172         }
12173         
12174         //this.trigger.addClassOnOver('x-form-trigger-over');
12175         //this.trigger.addClassOnClick('x-form-trigger-click');
12176         
12177         //if(!this.width){
12178         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12179         //}
12180     },
12181     
12182     closeTriggerEl : function()
12183     {
12184         var close = this.el.select('.roo-combo-removable-btn', true).first();
12185         return close ? close : false;
12186     },
12187     
12188     removeBtnClick : function(e, h, el)
12189     {
12190         e.preventDefault();
12191         
12192         if(this.fireEvent("remove", this) !== false){
12193             this.reset();
12194             this.fireEvent("afterremove", this)
12195         }
12196     },
12197     
12198     createList : function()
12199     {
12200         this.list = Roo.get(document.body).createChild({
12201             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12202             cls: 'typeahead typeahead-long dropdown-menu',
12203             style: 'display:none'
12204         });
12205         
12206         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12207         
12208     },
12209
12210     // private
12211     initTrigger : function(){
12212        
12213     },
12214
12215     // private
12216     onDestroy : function(){
12217         if(this.trigger){
12218             this.trigger.removeAllListeners();
12219           //  this.trigger.remove();
12220         }
12221         //if(this.wrap){
12222         //    this.wrap.remove();
12223         //}
12224         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12225     },
12226
12227     // private
12228     onFocus : function(){
12229         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12230         /*
12231         if(!this.mimicing){
12232             this.wrap.addClass('x-trigger-wrap-focus');
12233             this.mimicing = true;
12234             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12235             if(this.monitorTab){
12236                 this.el.on("keydown", this.checkTab, this);
12237             }
12238         }
12239         */
12240     },
12241
12242     // private
12243     checkTab : function(e){
12244         if(e.getKey() == e.TAB){
12245             this.triggerBlur();
12246         }
12247     },
12248
12249     // private
12250     onBlur : function(){
12251         // do nothing
12252     },
12253
12254     // private
12255     mimicBlur : function(e, t){
12256         /*
12257         if(!this.wrap.contains(t) && this.validateBlur()){
12258             this.triggerBlur();
12259         }
12260         */
12261     },
12262
12263     // private
12264     triggerBlur : function(){
12265         this.mimicing = false;
12266         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12267         if(this.monitorTab){
12268             this.el.un("keydown", this.checkTab, this);
12269         }
12270         //this.wrap.removeClass('x-trigger-wrap-focus');
12271         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12272     },
12273
12274     // private
12275     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12276     validateBlur : function(e, t){
12277         return true;
12278     },
12279
12280     // private
12281     onDisable : function(){
12282         this.inputEl().dom.disabled = true;
12283         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12284         //if(this.wrap){
12285         //    this.wrap.addClass('x-item-disabled');
12286         //}
12287     },
12288
12289     // private
12290     onEnable : function(){
12291         this.inputEl().dom.disabled = false;
12292         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12293         //if(this.wrap){
12294         //    this.el.removeClass('x-item-disabled');
12295         //}
12296     },
12297
12298     // private
12299     onShow : function(){
12300         var ae = this.getActionEl();
12301         
12302         if(ae){
12303             ae.dom.style.display = '';
12304             ae.dom.style.visibility = 'visible';
12305         }
12306     },
12307
12308     // private
12309     
12310     onHide : function(){
12311         var ae = this.getActionEl();
12312         ae.dom.style.display = 'none';
12313     },
12314
12315     /**
12316      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12317      * by an implementing function.
12318      * @method
12319      * @param {EventObject} e
12320      */
12321     onTriggerClick : Roo.emptyFn
12322 });
12323  
12324 /*
12325 * Licence: LGPL
12326 */
12327
12328 /**
12329  * @class Roo.bootstrap.CardUploader
12330  * @extends Roo.bootstrap.Button
12331  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12332  * @cfg {Number} errorTimeout default 3000
12333  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12334  * @cfg {Array}  html The button text.
12335
12336  *
12337  * @constructor
12338  * Create a new CardUploader
12339  * @param {Object} config The config object
12340  */
12341
12342 Roo.bootstrap.CardUploader = function(config){
12343     
12344  
12345     
12346     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12347     
12348     
12349     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12350         return r.data.id
12351         });
12352     
12353     
12354 };
12355
12356 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12357     
12358      
12359     errorTimeout : 3000,
12360      
12361     images : false,
12362    
12363     fileCollection : false,
12364     allowBlank : true,
12365     
12366     getAutoCreate : function()
12367     {
12368         
12369         var cfg =  {
12370             cls :'form-group' ,
12371             cn : [
12372                
12373                 {
12374                     tag: 'label',
12375                    //cls : 'input-group-addon',
12376                     html : this.fieldLabel
12377
12378                 },
12379
12380                 {
12381                     tag: 'input',
12382                     type : 'hidden',
12383                     value : this.value,
12384                     cls : 'd-none  form-control'
12385                 },
12386                 
12387                 {
12388                     tag: 'input',
12389                     multiple : 'multiple',
12390                     type : 'file',
12391                     cls : 'd-none  roo-card-upload-selector'
12392                 },
12393                 
12394                 {
12395                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12396                 },
12397                 {
12398                     cls : 'card-columns roo-card-uploader-container'
12399                 }
12400
12401             ]
12402         };
12403            
12404          
12405         return cfg;
12406     },
12407     
12408     getChildContainer : function() /// what children are added to.
12409     {
12410         return this.containerEl;
12411     },
12412    
12413     getButtonContainer : function() /// what children are added to.
12414     {
12415         return this.el.select(".roo-card-uploader-button-container").first();
12416     },
12417    
12418     initEvents : function()
12419     {
12420         
12421         Roo.bootstrap.Input.prototype.initEvents.call(this);
12422         
12423         var t = this;
12424         this.addxtype({
12425             xns: Roo.bootstrap,
12426
12427             xtype : 'Button',
12428             container_method : 'getButtonContainer' ,            
12429             html :  this.html, // fix changable?
12430             cls : 'w-100 ',
12431             listeners : {
12432                 'click' : function(btn, e) {
12433                     t.onClick(e);
12434                 }
12435             }
12436         });
12437         
12438         
12439         
12440         
12441         this.urlAPI = (window.createObjectURL && window) || 
12442                                 (window.URL && URL.revokeObjectURL && URL) || 
12443                                 (window.webkitURL && webkitURL);
12444                         
12445          
12446          
12447          
12448         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12449         
12450         this.selectorEl.on('change', this.onFileSelected, this);
12451         if (this.images) {
12452             var t = this;
12453             this.images.forEach(function(img) {
12454                 t.addCard(img)
12455             });
12456             this.images = false;
12457         }
12458         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12459          
12460        
12461     },
12462     
12463    
12464     onClick : function(e)
12465     {
12466         e.preventDefault();
12467          
12468         this.selectorEl.dom.click();
12469          
12470     },
12471     
12472     onFileSelected : function(e)
12473     {
12474         e.preventDefault();
12475         
12476         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12477             return;
12478         }
12479         
12480         Roo.each(this.selectorEl.dom.files, function(file){    
12481             this.addFile(file);
12482         }, this);
12483          
12484     },
12485     
12486       
12487     
12488       
12489     
12490     addFile : function(file)
12491     {
12492            
12493         if(typeof(file) === 'string'){
12494             throw "Add file by name?"; // should not happen
12495             return;
12496         }
12497         
12498         if(!file || !this.urlAPI){
12499             return;
12500         }
12501         
12502         // file;
12503         // file.type;
12504         
12505         var _this = this;
12506         
12507         
12508         var url = _this.urlAPI.createObjectURL( file);
12509            
12510         this.addCard({
12511             id : Roo.bootstrap.CardUploader.ID--,
12512             is_uploaded : false,
12513             src : url,
12514             title : file.name,
12515             mimetype : file.type,
12516             preview : false,
12517             is_deleted : 0
12518         })
12519         
12520     },
12521     
12522     addCard : function (data)
12523     {
12524         // hidden input element?
12525         // if the file is not an image...
12526         //then we need to use something other that and header_image
12527         var t = this;
12528         //   remove.....
12529         var footer = [
12530             {
12531                 xns : Roo.bootstrap,
12532                 xtype : 'CardFooter',
12533                 items: [
12534                     {
12535                         xns : Roo.bootstrap,
12536                         xtype : 'Element',
12537                         cls : 'd-flex',
12538                         items : [
12539                             
12540                             {
12541                                 xns : Roo.bootstrap,
12542                                 xtype : 'Button',
12543                                 html : String.format("<small>{0}</small>", data.title),
12544                                 cls : 'col-11 text-left',
12545                                 size: 'sm',
12546                                 weight: 'link',
12547                                 fa : 'download',
12548                                 listeners : {
12549                                     click : function() {
12550                                         this.downloadCard(data.id)
12551                                     }
12552                                 }
12553                             },
12554                           
12555                             {
12556                                 xns : Roo.bootstrap,
12557                                 xtype : 'Button',
12558                                 
12559                                 size : 'sm',
12560                                 weight: 'danger',
12561                                 cls : 'col-1',
12562                                 fa : 'times',
12563                                 listeners : {
12564                                     click : function() {
12565                                         t.removeCard(data.id)
12566                                     }
12567                                 }
12568                             }
12569                         ]
12570                     }
12571                     
12572                 ] 
12573             }
12574             
12575         ];
12576
12577         var cn = this.addxtype(
12578             {
12579                  
12580                 xns : Roo.bootstrap,
12581                 xtype : 'Card',
12582                 closeable : true,
12583                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12584                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12585                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12586                 data : data,
12587                 html : false,
12588                  
12589                 items : footer,
12590                 initEvents : function() {
12591                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12592                     this.imgEl = this.el.select('.card-img-top').first();
12593                     if (this.imgEl) {
12594                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12595                         this.imgEl.set({ 'pointer' : 'cursor' });
12596                                   
12597                     }
12598                     
12599                   
12600                 }
12601                 
12602             }
12603         );
12604         // dont' really need ot update items.
12605         // this.items.push(cn);
12606         this.fileCollection.add(cn);
12607         this.updateInput();
12608         
12609     },
12610     removeCard : function(id)
12611     {
12612         
12613         var card  = this.fileCollection.get(id);
12614         card.data.is_deleted = 1;
12615         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12616         this.fileCollection.remove(card);
12617         //this.items = this.items.filter(function(e) { return e != card });
12618         // dont' really need ot update items.
12619         card.el.dom.parentNode.removeChild(card.el.dom);
12620         
12621     },
12622     reset: function()
12623     {
12624         this.fileCollection.each(function(card) {
12625             card.el.dom.parentNode.removeChild(card.el.dom);    
12626         });
12627         this.fileCollection.clear();
12628         this.updateInput();
12629     },
12630     
12631     updateInput : function()
12632     {
12633         var data = [];
12634         this.fileCollection.each(function(e) {
12635             data.push(e.data);
12636         });
12637         
12638         this.inputEl().dom.value = JSON.stringify(data);
12639     }
12640     
12641     
12642 });
12643
12644
12645 Roo.bootstrap.CardUploader.ID = -1;/*
12646  * Based on:
12647  * Ext JS Library 1.1.1
12648  * Copyright(c) 2006-2007, Ext JS, LLC.
12649  *
12650  * Originally Released Under LGPL - original licence link has changed is not relivant.
12651  *
12652  * Fork - LGPL
12653  * <script type="text/javascript">
12654  */
12655
12656
12657 /**
12658  * @class Roo.data.SortTypes
12659  * @singleton
12660  * Defines the default sorting (casting?) comparison functions used when sorting data.
12661  */
12662 Roo.data.SortTypes = {
12663     /**
12664      * Default sort that does nothing
12665      * @param {Mixed} s The value being converted
12666      * @return {Mixed} The comparison value
12667      */
12668     none : function(s){
12669         return s;
12670     },
12671     
12672     /**
12673      * The regular expression used to strip tags
12674      * @type {RegExp}
12675      * @property
12676      */
12677     stripTagsRE : /<\/?[^>]+>/gi,
12678     
12679     /**
12680      * Strips all HTML tags to sort on text only
12681      * @param {Mixed} s The value being converted
12682      * @return {String} The comparison value
12683      */
12684     asText : function(s){
12685         return String(s).replace(this.stripTagsRE, "");
12686     },
12687     
12688     /**
12689      * Strips all HTML tags to sort on text only - Case insensitive
12690      * @param {Mixed} s The value being converted
12691      * @return {String} The comparison value
12692      */
12693     asUCText : function(s){
12694         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12695     },
12696     
12697     /**
12698      * Case insensitive string
12699      * @param {Mixed} s The value being converted
12700      * @return {String} The comparison value
12701      */
12702     asUCString : function(s) {
12703         return String(s).toUpperCase();
12704     },
12705     
12706     /**
12707      * Date sorting
12708      * @param {Mixed} s The value being converted
12709      * @return {Number} The comparison value
12710      */
12711     asDate : function(s) {
12712         if(!s){
12713             return 0;
12714         }
12715         if(s instanceof Date){
12716             return s.getTime();
12717         }
12718         return Date.parse(String(s));
12719     },
12720     
12721     /**
12722      * Float sorting
12723      * @param {Mixed} s The value being converted
12724      * @return {Float} The comparison value
12725      */
12726     asFloat : function(s) {
12727         var val = parseFloat(String(s).replace(/,/g, ""));
12728         if(isNaN(val)) {
12729             val = 0;
12730         }
12731         return val;
12732     },
12733     
12734     /**
12735      * Integer sorting
12736      * @param {Mixed} s The value being converted
12737      * @return {Number} The comparison value
12738      */
12739     asInt : function(s) {
12740         var val = parseInt(String(s).replace(/,/g, ""));
12741         if(isNaN(val)) {
12742             val = 0;
12743         }
12744         return val;
12745     }
12746 };/*
12747  * Based on:
12748  * Ext JS Library 1.1.1
12749  * Copyright(c) 2006-2007, Ext JS, LLC.
12750  *
12751  * Originally Released Under LGPL - original licence link has changed is not relivant.
12752  *
12753  * Fork - LGPL
12754  * <script type="text/javascript">
12755  */
12756
12757 /**
12758 * @class Roo.data.Record
12759  * Instances of this class encapsulate both record <em>definition</em> information, and record
12760  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12761  * to access Records cached in an {@link Roo.data.Store} object.<br>
12762  * <p>
12763  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12764  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12765  * objects.<br>
12766  * <p>
12767  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12768  * @constructor
12769  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12770  * {@link #create}. The parameters are the same.
12771  * @param {Array} data An associative Array of data values keyed by the field name.
12772  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12773  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12774  * not specified an integer id is generated.
12775  */
12776 Roo.data.Record = function(data, id){
12777     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12778     this.data = data;
12779 };
12780
12781 /**
12782  * Generate a constructor for a specific record layout.
12783  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12784  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12785  * Each field definition object may contain the following properties: <ul>
12786  * <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,
12787  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12788  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12789  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12790  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12791  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12792  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12793  * this may be omitted.</p></li>
12794  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12795  * <ul><li>auto (Default, implies no conversion)</li>
12796  * <li>string</li>
12797  * <li>int</li>
12798  * <li>float</li>
12799  * <li>boolean</li>
12800  * <li>date</li></ul></p></li>
12801  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12802  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12803  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12804  * by the Reader into an object that will be stored in the Record. It is passed the
12805  * following parameters:<ul>
12806  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12807  * </ul></p></li>
12808  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12809  * </ul>
12810  * <br>usage:<br><pre><code>
12811 var TopicRecord = Roo.data.Record.create(
12812     {name: 'title', mapping: 'topic_title'},
12813     {name: 'author', mapping: 'username'},
12814     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12815     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12816     {name: 'lastPoster', mapping: 'user2'},
12817     {name: 'excerpt', mapping: 'post_text'}
12818 );
12819
12820 var myNewRecord = new TopicRecord({
12821     title: 'Do my job please',
12822     author: 'noobie',
12823     totalPosts: 1,
12824     lastPost: new Date(),
12825     lastPoster: 'Animal',
12826     excerpt: 'No way dude!'
12827 });
12828 myStore.add(myNewRecord);
12829 </code></pre>
12830  * @method create
12831  * @static
12832  */
12833 Roo.data.Record.create = function(o){
12834     var f = function(){
12835         f.superclass.constructor.apply(this, arguments);
12836     };
12837     Roo.extend(f, Roo.data.Record);
12838     var p = f.prototype;
12839     p.fields = new Roo.util.MixedCollection(false, function(field){
12840         return field.name;
12841     });
12842     for(var i = 0, len = o.length; i < len; i++){
12843         p.fields.add(new Roo.data.Field(o[i]));
12844     }
12845     f.getField = function(name){
12846         return p.fields.get(name);  
12847     };
12848     return f;
12849 };
12850
12851 Roo.data.Record.AUTO_ID = 1000;
12852 Roo.data.Record.EDIT = 'edit';
12853 Roo.data.Record.REJECT = 'reject';
12854 Roo.data.Record.COMMIT = 'commit';
12855
12856 Roo.data.Record.prototype = {
12857     /**
12858      * Readonly flag - true if this record has been modified.
12859      * @type Boolean
12860      */
12861     dirty : false,
12862     editing : false,
12863     error: null,
12864     modified: null,
12865
12866     // private
12867     join : function(store){
12868         this.store = store;
12869     },
12870
12871     /**
12872      * Set the named field to the specified value.
12873      * @param {String} name The name of the field to set.
12874      * @param {Object} value The value to set the field to.
12875      */
12876     set : function(name, value){
12877         if(this.data[name] == value){
12878             return;
12879         }
12880         this.dirty = true;
12881         if(!this.modified){
12882             this.modified = {};
12883         }
12884         if(typeof this.modified[name] == 'undefined'){
12885             this.modified[name] = this.data[name];
12886         }
12887         this.data[name] = value;
12888         if(!this.editing && this.store){
12889             this.store.afterEdit(this);
12890         }       
12891     },
12892
12893     /**
12894      * Get the value of the named field.
12895      * @param {String} name The name of the field to get the value of.
12896      * @return {Object} The value of the field.
12897      */
12898     get : function(name){
12899         return this.data[name]; 
12900     },
12901
12902     // private
12903     beginEdit : function(){
12904         this.editing = true;
12905         this.modified = {}; 
12906     },
12907
12908     // private
12909     cancelEdit : function(){
12910         this.editing = false;
12911         delete this.modified;
12912     },
12913
12914     // private
12915     endEdit : function(){
12916         this.editing = false;
12917         if(this.dirty && this.store){
12918             this.store.afterEdit(this);
12919         }
12920     },
12921
12922     /**
12923      * Usually called by the {@link Roo.data.Store} which owns the Record.
12924      * Rejects all changes made to the Record since either creation, or the last commit operation.
12925      * Modified fields are reverted to their original values.
12926      * <p>
12927      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12928      * of reject operations.
12929      */
12930     reject : function(){
12931         var m = this.modified;
12932         for(var n in m){
12933             if(typeof m[n] != "function"){
12934                 this.data[n] = m[n];
12935             }
12936         }
12937         this.dirty = false;
12938         delete this.modified;
12939         this.editing = false;
12940         if(this.store){
12941             this.store.afterReject(this);
12942         }
12943     },
12944
12945     /**
12946      * Usually called by the {@link Roo.data.Store} which owns the Record.
12947      * Commits all changes made to the Record since either creation, or the last commit operation.
12948      * <p>
12949      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12950      * of commit operations.
12951      */
12952     commit : function(){
12953         this.dirty = false;
12954         delete this.modified;
12955         this.editing = false;
12956         if(this.store){
12957             this.store.afterCommit(this);
12958         }
12959     },
12960
12961     // private
12962     hasError : function(){
12963         return this.error != null;
12964     },
12965
12966     // private
12967     clearError : function(){
12968         this.error = null;
12969     },
12970
12971     /**
12972      * Creates a copy of this record.
12973      * @param {String} id (optional) A new record id if you don't want to use this record's id
12974      * @return {Record}
12975      */
12976     copy : function(newId) {
12977         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12978     }
12979 };/*
12980  * Based on:
12981  * Ext JS Library 1.1.1
12982  * Copyright(c) 2006-2007, Ext JS, LLC.
12983  *
12984  * Originally Released Under LGPL - original licence link has changed is not relivant.
12985  *
12986  * Fork - LGPL
12987  * <script type="text/javascript">
12988  */
12989
12990
12991
12992 /**
12993  * @class Roo.data.Store
12994  * @extends Roo.util.Observable
12995  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12996  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12997  * <p>
12998  * 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
12999  * has no knowledge of the format of the data returned by the Proxy.<br>
13000  * <p>
13001  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13002  * instances from the data object. These records are cached and made available through accessor functions.
13003  * @constructor
13004  * Creates a new Store.
13005  * @param {Object} config A config object containing the objects needed for the Store to access data,
13006  * and read the data into Records.
13007  */
13008 Roo.data.Store = function(config){
13009     this.data = new Roo.util.MixedCollection(false);
13010     this.data.getKey = function(o){
13011         return o.id;
13012     };
13013     this.baseParams = {};
13014     // private
13015     this.paramNames = {
13016         "start" : "start",
13017         "limit" : "limit",
13018         "sort" : "sort",
13019         "dir" : "dir",
13020         "multisort" : "_multisort"
13021     };
13022
13023     if(config && config.data){
13024         this.inlineData = config.data;
13025         delete config.data;
13026     }
13027
13028     Roo.apply(this, config);
13029     
13030     if(this.reader){ // reader passed
13031         this.reader = Roo.factory(this.reader, Roo.data);
13032         this.reader.xmodule = this.xmodule || false;
13033         if(!this.recordType){
13034             this.recordType = this.reader.recordType;
13035         }
13036         if(this.reader.onMetaChange){
13037             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13038         }
13039     }
13040
13041     if(this.recordType){
13042         this.fields = this.recordType.prototype.fields;
13043     }
13044     this.modified = [];
13045
13046     this.addEvents({
13047         /**
13048          * @event datachanged
13049          * Fires when the data cache has changed, and a widget which is using this Store
13050          * as a Record cache should refresh its view.
13051          * @param {Store} this
13052          */
13053         datachanged : true,
13054         /**
13055          * @event metachange
13056          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13057          * @param {Store} this
13058          * @param {Object} meta The JSON metadata
13059          */
13060         metachange : true,
13061         /**
13062          * @event add
13063          * Fires when Records have been added to the Store
13064          * @param {Store} this
13065          * @param {Roo.data.Record[]} records The array of Records added
13066          * @param {Number} index The index at which the record(s) were added
13067          */
13068         add : true,
13069         /**
13070          * @event remove
13071          * Fires when a Record has been removed from the Store
13072          * @param {Store} this
13073          * @param {Roo.data.Record} record The Record that was removed
13074          * @param {Number} index The index at which the record was removed
13075          */
13076         remove : true,
13077         /**
13078          * @event update
13079          * Fires when a Record has been updated
13080          * @param {Store} this
13081          * @param {Roo.data.Record} record The Record that was updated
13082          * @param {String} operation The update operation being performed.  Value may be one of:
13083          * <pre><code>
13084  Roo.data.Record.EDIT
13085  Roo.data.Record.REJECT
13086  Roo.data.Record.COMMIT
13087          * </code></pre>
13088          */
13089         update : true,
13090         /**
13091          * @event clear
13092          * Fires when the data cache has been cleared.
13093          * @param {Store} this
13094          */
13095         clear : true,
13096         /**
13097          * @event beforeload
13098          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13099          * the load action will be canceled.
13100          * @param {Store} this
13101          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13102          */
13103         beforeload : true,
13104         /**
13105          * @event beforeloadadd
13106          * Fires after a new set of Records has been loaded.
13107          * @param {Store} this
13108          * @param {Roo.data.Record[]} records The Records that were loaded
13109          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13110          */
13111         beforeloadadd : true,
13112         /**
13113          * @event load
13114          * Fires after a new set of Records has been loaded, before they are added to the store.
13115          * @param {Store} this
13116          * @param {Roo.data.Record[]} records The Records that were loaded
13117          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13118          * @params {Object} return from reader
13119          */
13120         load : true,
13121         /**
13122          * @event loadexception
13123          * Fires if an exception occurs in the Proxy during loading.
13124          * Called with the signature of the Proxy's "loadexception" event.
13125          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13126          * 
13127          * @param {Proxy} 
13128          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13129          * @param {Object} load options 
13130          * @param {Object} jsonData from your request (normally this contains the Exception)
13131          */
13132         loadexception : true
13133     });
13134     
13135     if(this.proxy){
13136         this.proxy = Roo.factory(this.proxy, Roo.data);
13137         this.proxy.xmodule = this.xmodule || false;
13138         this.relayEvents(this.proxy,  ["loadexception"]);
13139     }
13140     this.sortToggle = {};
13141     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13142
13143     Roo.data.Store.superclass.constructor.call(this);
13144
13145     if(this.inlineData){
13146         this.loadData(this.inlineData);
13147         delete this.inlineData;
13148     }
13149 };
13150
13151 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13152      /**
13153     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13154     * without a remote query - used by combo/forms at present.
13155     */
13156     
13157     /**
13158     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13159     */
13160     /**
13161     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13162     */
13163     /**
13164     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13165     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13166     */
13167     /**
13168     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13169     * on any HTTP request
13170     */
13171     /**
13172     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13173     */
13174     /**
13175     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13176     */
13177     multiSort: false,
13178     /**
13179     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13180     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13181     */
13182     remoteSort : false,
13183
13184     /**
13185     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13186      * loaded or when a record is removed. (defaults to false).
13187     */
13188     pruneModifiedRecords : false,
13189
13190     // private
13191     lastOptions : null,
13192
13193     /**
13194      * Add Records to the Store and fires the add event.
13195      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13196      */
13197     add : function(records){
13198         records = [].concat(records);
13199         for(var i = 0, len = records.length; i < len; i++){
13200             records[i].join(this);
13201         }
13202         var index = this.data.length;
13203         this.data.addAll(records);
13204         this.fireEvent("add", this, records, index);
13205     },
13206
13207     /**
13208      * Remove a Record from the Store and fires the remove event.
13209      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13210      */
13211     remove : function(record){
13212         var index = this.data.indexOf(record);
13213         this.data.removeAt(index);
13214  
13215         if(this.pruneModifiedRecords){
13216             this.modified.remove(record);
13217         }
13218         this.fireEvent("remove", this, record, index);
13219     },
13220
13221     /**
13222      * Remove all Records from the Store and fires the clear event.
13223      */
13224     removeAll : function(){
13225         this.data.clear();
13226         if(this.pruneModifiedRecords){
13227             this.modified = [];
13228         }
13229         this.fireEvent("clear", this);
13230     },
13231
13232     /**
13233      * Inserts Records to the Store at the given index and fires the add event.
13234      * @param {Number} index The start index at which to insert the passed Records.
13235      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13236      */
13237     insert : function(index, records){
13238         records = [].concat(records);
13239         for(var i = 0, len = records.length; i < len; i++){
13240             this.data.insert(index, records[i]);
13241             records[i].join(this);
13242         }
13243         this.fireEvent("add", this, records, index);
13244     },
13245
13246     /**
13247      * Get the index within the cache of the passed Record.
13248      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13249      * @return {Number} The index of the passed Record. Returns -1 if not found.
13250      */
13251     indexOf : function(record){
13252         return this.data.indexOf(record);
13253     },
13254
13255     /**
13256      * Get the index within the cache of the Record with the passed id.
13257      * @param {String} id The id of the Record to find.
13258      * @return {Number} The index of the Record. Returns -1 if not found.
13259      */
13260     indexOfId : function(id){
13261         return this.data.indexOfKey(id);
13262     },
13263
13264     /**
13265      * Get the Record with the specified id.
13266      * @param {String} id The id of the Record to find.
13267      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13268      */
13269     getById : function(id){
13270         return this.data.key(id);
13271     },
13272
13273     /**
13274      * Get the Record at the specified index.
13275      * @param {Number} index The index of the Record to find.
13276      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13277      */
13278     getAt : function(index){
13279         return this.data.itemAt(index);
13280     },
13281
13282     /**
13283      * Returns a range of Records between specified indices.
13284      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13285      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13286      * @return {Roo.data.Record[]} An array of Records
13287      */
13288     getRange : function(start, end){
13289         return this.data.getRange(start, end);
13290     },
13291
13292     // private
13293     storeOptions : function(o){
13294         o = Roo.apply({}, o);
13295         delete o.callback;
13296         delete o.scope;
13297         this.lastOptions = o;
13298     },
13299
13300     /**
13301      * Loads the Record cache from the configured Proxy using the configured Reader.
13302      * <p>
13303      * If using remote paging, then the first load call must specify the <em>start</em>
13304      * and <em>limit</em> properties in the options.params property to establish the initial
13305      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13306      * <p>
13307      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13308      * and this call will return before the new data has been loaded. Perform any post-processing
13309      * in a callback function, or in a "load" event handler.</strong>
13310      * <p>
13311      * @param {Object} options An object containing properties which control loading options:<ul>
13312      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13313      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13314      * passed the following arguments:<ul>
13315      * <li>r : Roo.data.Record[]</li>
13316      * <li>options: Options object from the load call</li>
13317      * <li>success: Boolean success indicator</li></ul></li>
13318      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13319      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13320      * </ul>
13321      */
13322     load : function(options){
13323         options = options || {};
13324         if(this.fireEvent("beforeload", this, options) !== false){
13325             this.storeOptions(options);
13326             var p = Roo.apply(options.params || {}, this.baseParams);
13327             // if meta was not loaded from remote source.. try requesting it.
13328             if (!this.reader.metaFromRemote) {
13329                 p._requestMeta = 1;
13330             }
13331             if(this.sortInfo && this.remoteSort){
13332                 var pn = this.paramNames;
13333                 p[pn["sort"]] = this.sortInfo.field;
13334                 p[pn["dir"]] = this.sortInfo.direction;
13335             }
13336             if (this.multiSort) {
13337                 var pn = this.paramNames;
13338                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13339             }
13340             
13341             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13342         }
13343     },
13344
13345     /**
13346      * Reloads the Record cache from the configured Proxy using the configured Reader and
13347      * the options from the last load operation performed.
13348      * @param {Object} options (optional) An object containing properties which may override the options
13349      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13350      * the most recently used options are reused).
13351      */
13352     reload : function(options){
13353         this.load(Roo.applyIf(options||{}, this.lastOptions));
13354     },
13355
13356     // private
13357     // Called as a callback by the Reader during a load operation.
13358     loadRecords : function(o, options, success){
13359         if(!o || success === false){
13360             if(success !== false){
13361                 this.fireEvent("load", this, [], options, o);
13362             }
13363             if(options.callback){
13364                 options.callback.call(options.scope || this, [], options, false);
13365             }
13366             return;
13367         }
13368         // if data returned failure - throw an exception.
13369         if (o.success === false) {
13370             // show a message if no listener is registered.
13371             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13372                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13373             }
13374             // loadmask wil be hooked into this..
13375             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13376             return;
13377         }
13378         var r = o.records, t = o.totalRecords || r.length;
13379         
13380         this.fireEvent("beforeloadadd", this, r, options, o);
13381         
13382         if(!options || options.add !== true){
13383             if(this.pruneModifiedRecords){
13384                 this.modified = [];
13385             }
13386             for(var i = 0, len = r.length; i < len; i++){
13387                 r[i].join(this);
13388             }
13389             if(this.snapshot){
13390                 this.data = this.snapshot;
13391                 delete this.snapshot;
13392             }
13393             this.data.clear();
13394             this.data.addAll(r);
13395             this.totalLength = t;
13396             this.applySort();
13397             this.fireEvent("datachanged", this);
13398         }else{
13399             this.totalLength = Math.max(t, this.data.length+r.length);
13400             this.add(r);
13401         }
13402         
13403         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13404                 
13405             var e = new Roo.data.Record({});
13406
13407             e.set(this.parent.displayField, this.parent.emptyTitle);
13408             e.set(this.parent.valueField, '');
13409
13410             this.insert(0, e);
13411         }
13412             
13413         this.fireEvent("load", this, r, options, o);
13414         if(options.callback){
13415             options.callback.call(options.scope || this, r, options, true);
13416         }
13417     },
13418
13419
13420     /**
13421      * Loads data from a passed data block. A Reader which understands the format of the data
13422      * must have been configured in the constructor.
13423      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13424      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13425      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13426      */
13427     loadData : function(o, append){
13428         var r = this.reader.readRecords(o);
13429         this.loadRecords(r, {add: append}, true);
13430     },
13431     
13432      /**
13433      * using 'cn' the nested child reader read the child array into it's child stores.
13434      * @param {Object} rec The record with a 'children array
13435      */
13436     loadDataFromChildren : function(rec)
13437     {
13438         this.loadData(this.reader.toLoadData(rec));
13439     },
13440     
13441
13442     /**
13443      * Gets the number of cached records.
13444      * <p>
13445      * <em>If using paging, this may not be the total size of the dataset. If the data object
13446      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13447      * the data set size</em>
13448      */
13449     getCount : function(){
13450         return this.data.length || 0;
13451     },
13452
13453     /**
13454      * Gets the total number of records in the dataset as returned by the server.
13455      * <p>
13456      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13457      * the dataset size</em>
13458      */
13459     getTotalCount : function(){
13460         return this.totalLength || 0;
13461     },
13462
13463     /**
13464      * Returns the sort state of the Store as an object with two properties:
13465      * <pre><code>
13466  field {String} The name of the field by which the Records are sorted
13467  direction {String} The sort order, "ASC" or "DESC"
13468      * </code></pre>
13469      */
13470     getSortState : function(){
13471         return this.sortInfo;
13472     },
13473
13474     // private
13475     applySort : function(){
13476         if(this.sortInfo && !this.remoteSort){
13477             var s = this.sortInfo, f = s.field;
13478             var st = this.fields.get(f).sortType;
13479             var fn = function(r1, r2){
13480                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13481                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13482             };
13483             this.data.sort(s.direction, fn);
13484             if(this.snapshot && this.snapshot != this.data){
13485                 this.snapshot.sort(s.direction, fn);
13486             }
13487         }
13488     },
13489
13490     /**
13491      * Sets the default sort column and order to be used by the next load operation.
13492      * @param {String} fieldName The name of the field to sort by.
13493      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13494      */
13495     setDefaultSort : function(field, dir){
13496         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13497     },
13498
13499     /**
13500      * Sort the Records.
13501      * If remote sorting is used, the sort is performed on the server, and the cache is
13502      * reloaded. If local sorting is used, the cache is sorted internally.
13503      * @param {String} fieldName The name of the field to sort by.
13504      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13505      */
13506     sort : function(fieldName, dir){
13507         var f = this.fields.get(fieldName);
13508         if(!dir){
13509             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13510             
13511             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13512                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13513             }else{
13514                 dir = f.sortDir;
13515             }
13516         }
13517         this.sortToggle[f.name] = dir;
13518         this.sortInfo = {field: f.name, direction: dir};
13519         if(!this.remoteSort){
13520             this.applySort();
13521             this.fireEvent("datachanged", this);
13522         }else{
13523             this.load(this.lastOptions);
13524         }
13525     },
13526
13527     /**
13528      * Calls the specified function for each of the Records in the cache.
13529      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13530      * Returning <em>false</em> aborts and exits the iteration.
13531      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13532      */
13533     each : function(fn, scope){
13534         this.data.each(fn, scope);
13535     },
13536
13537     /**
13538      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13539      * (e.g., during paging).
13540      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13541      */
13542     getModifiedRecords : function(){
13543         return this.modified;
13544     },
13545
13546     // private
13547     createFilterFn : function(property, value, anyMatch){
13548         if(!value.exec){ // not a regex
13549             value = String(value);
13550             if(value.length == 0){
13551                 return false;
13552             }
13553             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13554         }
13555         return function(r){
13556             return value.test(r.data[property]);
13557         };
13558     },
13559
13560     /**
13561      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13562      * @param {String} property A field on your records
13563      * @param {Number} start The record index to start at (defaults to 0)
13564      * @param {Number} end The last record index to include (defaults to length - 1)
13565      * @return {Number} The sum
13566      */
13567     sum : function(property, start, end){
13568         var rs = this.data.items, v = 0;
13569         start = start || 0;
13570         end = (end || end === 0) ? end : rs.length-1;
13571
13572         for(var i = start; i <= end; i++){
13573             v += (rs[i].data[property] || 0);
13574         }
13575         return v;
13576     },
13577
13578     /**
13579      * Filter the records by a specified property.
13580      * @param {String} field A field on your records
13581      * @param {String/RegExp} value Either a string that the field
13582      * should start with or a RegExp to test against the field
13583      * @param {Boolean} anyMatch True to match any part not just the beginning
13584      */
13585     filter : function(property, value, anyMatch){
13586         var fn = this.createFilterFn(property, value, anyMatch);
13587         return fn ? this.filterBy(fn) : this.clearFilter();
13588     },
13589
13590     /**
13591      * Filter by a function. The specified function will be called with each
13592      * record in this data source. If the function returns true the record is included,
13593      * otherwise it is filtered.
13594      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13595      * @param {Object} scope (optional) The scope of the function (defaults to this)
13596      */
13597     filterBy : function(fn, scope){
13598         this.snapshot = this.snapshot || this.data;
13599         this.data = this.queryBy(fn, scope||this);
13600         this.fireEvent("datachanged", this);
13601     },
13602
13603     /**
13604      * Query the records by a specified property.
13605      * @param {String} field A field on your records
13606      * @param {String/RegExp} value Either a string that the field
13607      * should start with or a RegExp to test against the field
13608      * @param {Boolean} anyMatch True to match any part not just the beginning
13609      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13610      */
13611     query : function(property, value, anyMatch){
13612         var fn = this.createFilterFn(property, value, anyMatch);
13613         return fn ? this.queryBy(fn) : this.data.clone();
13614     },
13615
13616     /**
13617      * Query by a function. The specified function will be called with each
13618      * record in this data source. If the function returns true the record is included
13619      * in the results.
13620      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13621      * @param {Object} scope (optional) The scope of the function (defaults to this)
13622       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13623      **/
13624     queryBy : function(fn, scope){
13625         var data = this.snapshot || this.data;
13626         return data.filterBy(fn, scope||this);
13627     },
13628
13629     /**
13630      * Collects unique values for a particular dataIndex from this store.
13631      * @param {String} dataIndex The property to collect
13632      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13633      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13634      * @return {Array} An array of the unique values
13635      **/
13636     collect : function(dataIndex, allowNull, bypassFilter){
13637         var d = (bypassFilter === true && this.snapshot) ?
13638                 this.snapshot.items : this.data.items;
13639         var v, sv, r = [], l = {};
13640         for(var i = 0, len = d.length; i < len; i++){
13641             v = d[i].data[dataIndex];
13642             sv = String(v);
13643             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13644                 l[sv] = true;
13645                 r[r.length] = v;
13646             }
13647         }
13648         return r;
13649     },
13650
13651     /**
13652      * Revert to a view of the Record cache with no filtering applied.
13653      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13654      */
13655     clearFilter : function(suppressEvent){
13656         if(this.snapshot && this.snapshot != this.data){
13657             this.data = this.snapshot;
13658             delete this.snapshot;
13659             if(suppressEvent !== true){
13660                 this.fireEvent("datachanged", this);
13661             }
13662         }
13663     },
13664
13665     // private
13666     afterEdit : function(record){
13667         if(this.modified.indexOf(record) == -1){
13668             this.modified.push(record);
13669         }
13670         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13671     },
13672     
13673     // private
13674     afterReject : function(record){
13675         this.modified.remove(record);
13676         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13677     },
13678
13679     // private
13680     afterCommit : function(record){
13681         this.modified.remove(record);
13682         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13683     },
13684
13685     /**
13686      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13687      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13688      */
13689     commitChanges : function(){
13690         var m = this.modified.slice(0);
13691         this.modified = [];
13692         for(var i = 0, len = m.length; i < len; i++){
13693             m[i].commit();
13694         }
13695     },
13696
13697     /**
13698      * Cancel outstanding changes on all changed records.
13699      */
13700     rejectChanges : function(){
13701         var m = this.modified.slice(0);
13702         this.modified = [];
13703         for(var i = 0, len = m.length; i < len; i++){
13704             m[i].reject();
13705         }
13706     },
13707
13708     onMetaChange : function(meta, rtype, o){
13709         this.recordType = rtype;
13710         this.fields = rtype.prototype.fields;
13711         delete this.snapshot;
13712         this.sortInfo = meta.sortInfo || this.sortInfo;
13713         this.modified = [];
13714         this.fireEvent('metachange', this, this.reader.meta);
13715     },
13716     
13717     moveIndex : function(data, type)
13718     {
13719         var index = this.indexOf(data);
13720         
13721         var newIndex = index + type;
13722         
13723         this.remove(data);
13724         
13725         this.insert(newIndex, data);
13726         
13727     }
13728 });/*
13729  * Based on:
13730  * Ext JS Library 1.1.1
13731  * Copyright(c) 2006-2007, Ext JS, LLC.
13732  *
13733  * Originally Released Under LGPL - original licence link has changed is not relivant.
13734  *
13735  * Fork - LGPL
13736  * <script type="text/javascript">
13737  */
13738
13739 /**
13740  * @class Roo.data.SimpleStore
13741  * @extends Roo.data.Store
13742  * Small helper class to make creating Stores from Array data easier.
13743  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13744  * @cfg {Array} fields An array of field definition objects, or field name strings.
13745  * @cfg {Object} an existing reader (eg. copied from another store)
13746  * @cfg {Array} data The multi-dimensional array of data
13747  * @constructor
13748  * @param {Object} config
13749  */
13750 Roo.data.SimpleStore = function(config)
13751 {
13752     Roo.data.SimpleStore.superclass.constructor.call(this, {
13753         isLocal : true,
13754         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13755                 id: config.id
13756             },
13757             Roo.data.Record.create(config.fields)
13758         ),
13759         proxy : new Roo.data.MemoryProxy(config.data)
13760     });
13761     this.load();
13762 };
13763 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13764  * Based on:
13765  * Ext JS Library 1.1.1
13766  * Copyright(c) 2006-2007, Ext JS, LLC.
13767  *
13768  * Originally Released Under LGPL - original licence link has changed is not relivant.
13769  *
13770  * Fork - LGPL
13771  * <script type="text/javascript">
13772  */
13773
13774 /**
13775 /**
13776  * @extends Roo.data.Store
13777  * @class Roo.data.JsonStore
13778  * Small helper class to make creating Stores for JSON data easier. <br/>
13779 <pre><code>
13780 var store = new Roo.data.JsonStore({
13781     url: 'get-images.php',
13782     root: 'images',
13783     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13784 });
13785 </code></pre>
13786  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13787  * JsonReader and HttpProxy (unless inline data is provided).</b>
13788  * @cfg {Array} fields An array of field definition objects, or field name strings.
13789  * @constructor
13790  * @param {Object} config
13791  */
13792 Roo.data.JsonStore = function(c){
13793     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13794         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13795         reader: new Roo.data.JsonReader(c, c.fields)
13796     }));
13797 };
13798 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13799  * Based on:
13800  * Ext JS Library 1.1.1
13801  * Copyright(c) 2006-2007, Ext JS, LLC.
13802  *
13803  * Originally Released Under LGPL - original licence link has changed is not relivant.
13804  *
13805  * Fork - LGPL
13806  * <script type="text/javascript">
13807  */
13808
13809  
13810 Roo.data.Field = function(config){
13811     if(typeof config == "string"){
13812         config = {name: config};
13813     }
13814     Roo.apply(this, config);
13815     
13816     if(!this.type){
13817         this.type = "auto";
13818     }
13819     
13820     var st = Roo.data.SortTypes;
13821     // named sortTypes are supported, here we look them up
13822     if(typeof this.sortType == "string"){
13823         this.sortType = st[this.sortType];
13824     }
13825     
13826     // set default sortType for strings and dates
13827     if(!this.sortType){
13828         switch(this.type){
13829             case "string":
13830                 this.sortType = st.asUCString;
13831                 break;
13832             case "date":
13833                 this.sortType = st.asDate;
13834                 break;
13835             default:
13836                 this.sortType = st.none;
13837         }
13838     }
13839
13840     // define once
13841     var stripRe = /[\$,%]/g;
13842
13843     // prebuilt conversion function for this field, instead of
13844     // switching every time we're reading a value
13845     if(!this.convert){
13846         var cv, dateFormat = this.dateFormat;
13847         switch(this.type){
13848             case "":
13849             case "auto":
13850             case undefined:
13851                 cv = function(v){ return v; };
13852                 break;
13853             case "string":
13854                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13855                 break;
13856             case "int":
13857                 cv = function(v){
13858                     return v !== undefined && v !== null && v !== '' ?
13859                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13860                     };
13861                 break;
13862             case "float":
13863                 cv = function(v){
13864                     return v !== undefined && v !== null && v !== '' ?
13865                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13866                     };
13867                 break;
13868             case "bool":
13869             case "boolean":
13870                 cv = function(v){ return v === true || v === "true" || v == 1; };
13871                 break;
13872             case "date":
13873                 cv = function(v){
13874                     if(!v){
13875                         return '';
13876                     }
13877                     if(v instanceof Date){
13878                         return v;
13879                     }
13880                     if(dateFormat){
13881                         if(dateFormat == "timestamp"){
13882                             return new Date(v*1000);
13883                         }
13884                         return Date.parseDate(v, dateFormat);
13885                     }
13886                     var parsed = Date.parse(v);
13887                     return parsed ? new Date(parsed) : null;
13888                 };
13889              break;
13890             
13891         }
13892         this.convert = cv;
13893     }
13894 };
13895
13896 Roo.data.Field.prototype = {
13897     dateFormat: null,
13898     defaultValue: "",
13899     mapping: null,
13900     sortType : null,
13901     sortDir : "ASC"
13902 };/*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912  
13913 // Base class for reading structured data from a data source.  This class is intended to be
13914 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13915
13916 /**
13917  * @class Roo.data.DataReader
13918  * Base class for reading structured data from a data source.  This class is intended to be
13919  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13920  */
13921
13922 Roo.data.DataReader = function(meta, recordType){
13923     
13924     this.meta = meta;
13925     
13926     this.recordType = recordType instanceof Array ? 
13927         Roo.data.Record.create(recordType) : recordType;
13928 };
13929
13930 Roo.data.DataReader.prototype = {
13931     
13932     
13933     readerType : 'Data',
13934      /**
13935      * Create an empty record
13936      * @param {Object} data (optional) - overlay some values
13937      * @return {Roo.data.Record} record created.
13938      */
13939     newRow :  function(d) {
13940         var da =  {};
13941         this.recordType.prototype.fields.each(function(c) {
13942             switch( c.type) {
13943                 case 'int' : da[c.name] = 0; break;
13944                 case 'date' : da[c.name] = new Date(); break;
13945                 case 'float' : da[c.name] = 0.0; break;
13946                 case 'boolean' : da[c.name] = false; break;
13947                 default : da[c.name] = ""; break;
13948             }
13949             
13950         });
13951         return new this.recordType(Roo.apply(da, d));
13952     }
13953     
13954     
13955 };/*
13956  * Based on:
13957  * Ext JS Library 1.1.1
13958  * Copyright(c) 2006-2007, Ext JS, LLC.
13959  *
13960  * Originally Released Under LGPL - original licence link has changed is not relivant.
13961  *
13962  * Fork - LGPL
13963  * <script type="text/javascript">
13964  */
13965
13966 /**
13967  * @class Roo.data.DataProxy
13968  * @extends Roo.data.Observable
13969  * This class is an abstract base class for implementations which provide retrieval of
13970  * unformatted data objects.<br>
13971  * <p>
13972  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13973  * (of the appropriate type which knows how to parse the data object) to provide a block of
13974  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13975  * <p>
13976  * Custom implementations must implement the load method as described in
13977  * {@link Roo.data.HttpProxy#load}.
13978  */
13979 Roo.data.DataProxy = function(){
13980     this.addEvents({
13981         /**
13982          * @event beforeload
13983          * Fires before a network request is made to retrieve a data object.
13984          * @param {Object} This DataProxy object.
13985          * @param {Object} params The params parameter to the load function.
13986          */
13987         beforeload : true,
13988         /**
13989          * @event load
13990          * Fires before the load method's callback is called.
13991          * @param {Object} This DataProxy object.
13992          * @param {Object} o The data object.
13993          * @param {Object} arg The callback argument object passed to the load function.
13994          */
13995         load : true,
13996         /**
13997          * @event loadexception
13998          * Fires if an Exception occurs during data retrieval.
13999          * @param {Object} This DataProxy object.
14000          * @param {Object} o The data object.
14001          * @param {Object} arg The callback argument object passed to the load function.
14002          * @param {Object} e The Exception.
14003          */
14004         loadexception : true
14005     });
14006     Roo.data.DataProxy.superclass.constructor.call(this);
14007 };
14008
14009 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14010
14011     /**
14012      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14013      */
14014 /*
14015  * Based on:
14016  * Ext JS Library 1.1.1
14017  * Copyright(c) 2006-2007, Ext JS, LLC.
14018  *
14019  * Originally Released Under LGPL - original licence link has changed is not relivant.
14020  *
14021  * Fork - LGPL
14022  * <script type="text/javascript">
14023  */
14024 /**
14025  * @class Roo.data.MemoryProxy
14026  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14027  * to the Reader when its load method is called.
14028  * @constructor
14029  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14030  */
14031 Roo.data.MemoryProxy = function(data){
14032     if (data.data) {
14033         data = data.data;
14034     }
14035     Roo.data.MemoryProxy.superclass.constructor.call(this);
14036     this.data = data;
14037 };
14038
14039 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14040     
14041     /**
14042      * Load data from the requested source (in this case an in-memory
14043      * data object passed to the constructor), read the data object into
14044      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14045      * process that block using the passed callback.
14046      * @param {Object} params This parameter is not used by the MemoryProxy class.
14047      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14048      * object into a block of Roo.data.Records.
14049      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14050      * The function must be passed <ul>
14051      * <li>The Record block object</li>
14052      * <li>The "arg" argument from the load function</li>
14053      * <li>A boolean success indicator</li>
14054      * </ul>
14055      * @param {Object} scope The scope in which to call the callback
14056      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14057      */
14058     load : function(params, reader, callback, scope, arg){
14059         params = params || {};
14060         var result;
14061         try {
14062             result = reader.readRecords(params.data ? params.data :this.data);
14063         }catch(e){
14064             this.fireEvent("loadexception", this, arg, null, e);
14065             callback.call(scope, null, arg, false);
14066             return;
14067         }
14068         callback.call(scope, result, arg, true);
14069     },
14070     
14071     // private
14072     update : function(params, records){
14073         
14074     }
14075 });/*
14076  * Based on:
14077  * Ext JS Library 1.1.1
14078  * Copyright(c) 2006-2007, Ext JS, LLC.
14079  *
14080  * Originally Released Under LGPL - original licence link has changed is not relivant.
14081  *
14082  * Fork - LGPL
14083  * <script type="text/javascript">
14084  */
14085 /**
14086  * @class Roo.data.HttpProxy
14087  * @extends Roo.data.DataProxy
14088  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14089  * configured to reference a certain URL.<br><br>
14090  * <p>
14091  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14092  * from which the running page was served.<br><br>
14093  * <p>
14094  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14095  * <p>
14096  * Be aware that to enable the browser to parse an XML document, the server must set
14097  * the Content-Type header in the HTTP response to "text/xml".
14098  * @constructor
14099  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14100  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14101  * will be used to make the request.
14102  */
14103 Roo.data.HttpProxy = function(conn){
14104     Roo.data.HttpProxy.superclass.constructor.call(this);
14105     // is conn a conn config or a real conn?
14106     this.conn = conn;
14107     this.useAjax = !conn || !conn.events;
14108   
14109 };
14110
14111 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14112     // thse are take from connection...
14113     
14114     /**
14115      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14116      */
14117     /**
14118      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14119      * extra parameters to each request made by this object. (defaults to undefined)
14120      */
14121     /**
14122      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14123      *  to each request made by this object. (defaults to undefined)
14124      */
14125     /**
14126      * @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)
14127      */
14128     /**
14129      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14130      */
14131      /**
14132      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14133      * @type Boolean
14134      */
14135   
14136
14137     /**
14138      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14139      * @type Boolean
14140      */
14141     /**
14142      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14143      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14144      * a finer-grained basis than the DataProxy events.
14145      */
14146     getConnection : function(){
14147         return this.useAjax ? Roo.Ajax : this.conn;
14148     },
14149
14150     /**
14151      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14152      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14153      * process that block using the passed callback.
14154      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14155      * for the request to the remote server.
14156      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14157      * object into a block of Roo.data.Records.
14158      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14159      * The function must be passed <ul>
14160      * <li>The Record block object</li>
14161      * <li>The "arg" argument from the load function</li>
14162      * <li>A boolean success indicator</li>
14163      * </ul>
14164      * @param {Object} scope The scope in which to call the callback
14165      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14166      */
14167     load : function(params, reader, callback, scope, arg){
14168         if(this.fireEvent("beforeload", this, params) !== false){
14169             var  o = {
14170                 params : params || {},
14171                 request: {
14172                     callback : callback,
14173                     scope : scope,
14174                     arg : arg
14175                 },
14176                 reader: reader,
14177                 callback : this.loadResponse,
14178                 scope: this
14179             };
14180             if(this.useAjax){
14181                 Roo.applyIf(o, this.conn);
14182                 if(this.activeRequest){
14183                     Roo.Ajax.abort(this.activeRequest);
14184                 }
14185                 this.activeRequest = Roo.Ajax.request(o);
14186             }else{
14187                 this.conn.request(o);
14188             }
14189         }else{
14190             callback.call(scope||this, null, arg, false);
14191         }
14192     },
14193
14194     // private
14195     loadResponse : function(o, success, response){
14196         delete this.activeRequest;
14197         if(!success){
14198             this.fireEvent("loadexception", this, o, response);
14199             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14200             return;
14201         }
14202         var result;
14203         try {
14204             result = o.reader.read(response);
14205         }catch(e){
14206             this.fireEvent("loadexception", this, o, response, e);
14207             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14208             return;
14209         }
14210         
14211         this.fireEvent("load", this, o, o.request.arg);
14212         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14213     },
14214
14215     // private
14216     update : function(dataSet){
14217
14218     },
14219
14220     // private
14221     updateResponse : function(dataSet){
14222
14223     }
14224 });/*
14225  * Based on:
14226  * Ext JS Library 1.1.1
14227  * Copyright(c) 2006-2007, Ext JS, LLC.
14228  *
14229  * Originally Released Under LGPL - original licence link has changed is not relivant.
14230  *
14231  * Fork - LGPL
14232  * <script type="text/javascript">
14233  */
14234
14235 /**
14236  * @class Roo.data.ScriptTagProxy
14237  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14238  * other than the originating domain of the running page.<br><br>
14239  * <p>
14240  * <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
14241  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14242  * <p>
14243  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14244  * source code that is used as the source inside a &lt;script> tag.<br><br>
14245  * <p>
14246  * In order for the browser to process the returned data, the server must wrap the data object
14247  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14248  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14249  * depending on whether the callback name was passed:
14250  * <p>
14251  * <pre><code>
14252 boolean scriptTag = false;
14253 String cb = request.getParameter("callback");
14254 if (cb != null) {
14255     scriptTag = true;
14256     response.setContentType("text/javascript");
14257 } else {
14258     response.setContentType("application/x-json");
14259 }
14260 Writer out = response.getWriter();
14261 if (scriptTag) {
14262     out.write(cb + "(");
14263 }
14264 out.print(dataBlock.toJsonString());
14265 if (scriptTag) {
14266     out.write(");");
14267 }
14268 </pre></code>
14269  *
14270  * @constructor
14271  * @param {Object} config A configuration object.
14272  */
14273 Roo.data.ScriptTagProxy = function(config){
14274     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14275     Roo.apply(this, config);
14276     this.head = document.getElementsByTagName("head")[0];
14277 };
14278
14279 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14280
14281 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14282     /**
14283      * @cfg {String} url The URL from which to request the data object.
14284      */
14285     /**
14286      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14287      */
14288     timeout : 30000,
14289     /**
14290      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14291      * the server the name of the callback function set up by the load call to process the returned data object.
14292      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14293      * javascript output which calls this named function passing the data object as its only parameter.
14294      */
14295     callbackParam : "callback",
14296     /**
14297      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14298      * name to the request.
14299      */
14300     nocache : true,
14301
14302     /**
14303      * Load data from the configured URL, read the data object into
14304      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14305      * process that block using the passed callback.
14306      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14307      * for the request to the remote server.
14308      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14309      * object into a block of Roo.data.Records.
14310      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14311      * The function must be passed <ul>
14312      * <li>The Record block object</li>
14313      * <li>The "arg" argument from the load function</li>
14314      * <li>A boolean success indicator</li>
14315      * </ul>
14316      * @param {Object} scope The scope in which to call the callback
14317      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14318      */
14319     load : function(params, reader, callback, scope, arg){
14320         if(this.fireEvent("beforeload", this, params) !== false){
14321
14322             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14323
14324             var url = this.url;
14325             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14326             if(this.nocache){
14327                 url += "&_dc=" + (new Date().getTime());
14328             }
14329             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14330             var trans = {
14331                 id : transId,
14332                 cb : "stcCallback"+transId,
14333                 scriptId : "stcScript"+transId,
14334                 params : params,
14335                 arg : arg,
14336                 url : url,
14337                 callback : callback,
14338                 scope : scope,
14339                 reader : reader
14340             };
14341             var conn = this;
14342
14343             window[trans.cb] = function(o){
14344                 conn.handleResponse(o, trans);
14345             };
14346
14347             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14348
14349             if(this.autoAbort !== false){
14350                 this.abort();
14351             }
14352
14353             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14354
14355             var script = document.createElement("script");
14356             script.setAttribute("src", url);
14357             script.setAttribute("type", "text/javascript");
14358             script.setAttribute("id", trans.scriptId);
14359             this.head.appendChild(script);
14360
14361             this.trans = trans;
14362         }else{
14363             callback.call(scope||this, null, arg, false);
14364         }
14365     },
14366
14367     // private
14368     isLoading : function(){
14369         return this.trans ? true : false;
14370     },
14371
14372     /**
14373      * Abort the current server request.
14374      */
14375     abort : function(){
14376         if(this.isLoading()){
14377             this.destroyTrans(this.trans);
14378         }
14379     },
14380
14381     // private
14382     destroyTrans : function(trans, isLoaded){
14383         this.head.removeChild(document.getElementById(trans.scriptId));
14384         clearTimeout(trans.timeoutId);
14385         if(isLoaded){
14386             window[trans.cb] = undefined;
14387             try{
14388                 delete window[trans.cb];
14389             }catch(e){}
14390         }else{
14391             // if hasn't been loaded, wait for load to remove it to prevent script error
14392             window[trans.cb] = function(){
14393                 window[trans.cb] = undefined;
14394                 try{
14395                     delete window[trans.cb];
14396                 }catch(e){}
14397             };
14398         }
14399     },
14400
14401     // private
14402     handleResponse : function(o, trans){
14403         this.trans = false;
14404         this.destroyTrans(trans, true);
14405         var result;
14406         try {
14407             result = trans.reader.readRecords(o);
14408         }catch(e){
14409             this.fireEvent("loadexception", this, o, trans.arg, e);
14410             trans.callback.call(trans.scope||window, null, trans.arg, false);
14411             return;
14412         }
14413         this.fireEvent("load", this, o, trans.arg);
14414         trans.callback.call(trans.scope||window, result, trans.arg, true);
14415     },
14416
14417     // private
14418     handleFailure : function(trans){
14419         this.trans = false;
14420         this.destroyTrans(trans, false);
14421         this.fireEvent("loadexception", this, null, trans.arg);
14422         trans.callback.call(trans.scope||window, null, trans.arg, false);
14423     }
14424 });/*
14425  * Based on:
14426  * Ext JS Library 1.1.1
14427  * Copyright(c) 2006-2007, Ext JS, LLC.
14428  *
14429  * Originally Released Under LGPL - original licence link has changed is not relivant.
14430  *
14431  * Fork - LGPL
14432  * <script type="text/javascript">
14433  */
14434
14435 /**
14436  * @class Roo.data.JsonReader
14437  * @extends Roo.data.DataReader
14438  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14439  * based on mappings in a provided Roo.data.Record constructor.
14440  * 
14441  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14442  * in the reply previously. 
14443  * 
14444  * <p>
14445  * Example code:
14446  * <pre><code>
14447 var RecordDef = Roo.data.Record.create([
14448     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14449     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14450 ]);
14451 var myReader = new Roo.data.JsonReader({
14452     totalProperty: "results",    // The property which contains the total dataset size (optional)
14453     root: "rows",                // The property which contains an Array of row objects
14454     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14455 }, RecordDef);
14456 </code></pre>
14457  * <p>
14458  * This would consume a JSON file like this:
14459  * <pre><code>
14460 { 'results': 2, 'rows': [
14461     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14462     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14463 }
14464 </code></pre>
14465  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14466  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14467  * paged from the remote server.
14468  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14469  * @cfg {String} root name of the property which contains the Array of row objects.
14470  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14471  * @cfg {Array} fields Array of field definition objects
14472  * @constructor
14473  * Create a new JsonReader
14474  * @param {Object} meta Metadata configuration options
14475  * @param {Object} recordType Either an Array of field definition objects,
14476  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14477  */
14478 Roo.data.JsonReader = function(meta, recordType){
14479     
14480     meta = meta || {};
14481     // set some defaults:
14482     Roo.applyIf(meta, {
14483         totalProperty: 'total',
14484         successProperty : 'success',
14485         root : 'data',
14486         id : 'id'
14487     });
14488     
14489     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14490 };
14491 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14492     
14493     readerType : 'Json',
14494     
14495     /**
14496      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14497      * Used by Store query builder to append _requestMeta to params.
14498      * 
14499      */
14500     metaFromRemote : false,
14501     /**
14502      * This method is only used by a DataProxy which has retrieved data from a remote server.
14503      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14504      * @return {Object} data A data block which is used by an Roo.data.Store object as
14505      * a cache of Roo.data.Records.
14506      */
14507     read : function(response){
14508         var json = response.responseText;
14509        
14510         var o = /* eval:var:o */ eval("("+json+")");
14511         if(!o) {
14512             throw {message: "JsonReader.read: Json object not found"};
14513         }
14514         
14515         if(o.metaData){
14516             
14517             delete this.ef;
14518             this.metaFromRemote = true;
14519             this.meta = o.metaData;
14520             this.recordType = Roo.data.Record.create(o.metaData.fields);
14521             this.onMetaChange(this.meta, this.recordType, o);
14522         }
14523         return this.readRecords(o);
14524     },
14525
14526     // private function a store will implement
14527     onMetaChange : function(meta, recordType, o){
14528
14529     },
14530
14531     /**
14532          * @ignore
14533          */
14534     simpleAccess: function(obj, subsc) {
14535         return obj[subsc];
14536     },
14537
14538         /**
14539          * @ignore
14540          */
14541     getJsonAccessor: function(){
14542         var re = /[\[\.]/;
14543         return function(expr) {
14544             try {
14545                 return(re.test(expr))
14546                     ? new Function("obj", "return obj." + expr)
14547                     : function(obj){
14548                         return obj[expr];
14549                     };
14550             } catch(e){}
14551             return Roo.emptyFn;
14552         };
14553     }(),
14554
14555     /**
14556      * Create a data block containing Roo.data.Records from an XML document.
14557      * @param {Object} o An object which contains an Array of row objects in the property specified
14558      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14559      * which contains the total size of the dataset.
14560      * @return {Object} data A data block which is used by an Roo.data.Store object as
14561      * a cache of Roo.data.Records.
14562      */
14563     readRecords : function(o){
14564         /**
14565          * After any data loads, the raw JSON data is available for further custom processing.
14566          * @type Object
14567          */
14568         this.o = o;
14569         var s = this.meta, Record = this.recordType,
14570             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14571
14572 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14573         if (!this.ef) {
14574             if(s.totalProperty) {
14575                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14576                 }
14577                 if(s.successProperty) {
14578                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14579                 }
14580                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14581                 if (s.id) {
14582                         var g = this.getJsonAccessor(s.id);
14583                         this.getId = function(rec) {
14584                                 var r = g(rec);  
14585                                 return (r === undefined || r === "") ? null : r;
14586                         };
14587                 } else {
14588                         this.getId = function(){return null;};
14589                 }
14590             this.ef = [];
14591             for(var jj = 0; jj < fl; jj++){
14592                 f = fi[jj];
14593                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14594                 this.ef[jj] = this.getJsonAccessor(map);
14595             }
14596         }
14597
14598         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14599         if(s.totalProperty){
14600             var vt = parseInt(this.getTotal(o), 10);
14601             if(!isNaN(vt)){
14602                 totalRecords = vt;
14603             }
14604         }
14605         if(s.successProperty){
14606             var vs = this.getSuccess(o);
14607             if(vs === false || vs === 'false'){
14608                 success = false;
14609             }
14610         }
14611         var records = [];
14612         for(var i = 0; i < c; i++){
14613                 var n = root[i];
14614             var values = {};
14615             var id = this.getId(n);
14616             for(var j = 0; j < fl; j++){
14617                 f = fi[j];
14618             var v = this.ef[j](n);
14619             if (!f.convert) {
14620                 Roo.log('missing convert for ' + f.name);
14621                 Roo.log(f);
14622                 continue;
14623             }
14624             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14625             }
14626             var record = new Record(values, id);
14627             record.json = n;
14628             records[i] = record;
14629         }
14630         return {
14631             raw : o,
14632             success : success,
14633             records : records,
14634             totalRecords : totalRecords
14635         };
14636     },
14637     // used when loading children.. @see loadDataFromChildren
14638     toLoadData: function(rec)
14639     {
14640         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14641         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14642         return { data : data, total : data.length };
14643         
14644     }
14645 });/*
14646  * Based on:
14647  * Ext JS Library 1.1.1
14648  * Copyright(c) 2006-2007, Ext JS, LLC.
14649  *
14650  * Originally Released Under LGPL - original licence link has changed is not relivant.
14651  *
14652  * Fork - LGPL
14653  * <script type="text/javascript">
14654  */
14655
14656 /**
14657  * @class Roo.data.ArrayReader
14658  * @extends Roo.data.DataReader
14659  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14660  * Each element of that Array represents a row of data fields. The
14661  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14662  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14663  * <p>
14664  * Example code:.
14665  * <pre><code>
14666 var RecordDef = Roo.data.Record.create([
14667     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14668     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14669 ]);
14670 var myReader = new Roo.data.ArrayReader({
14671     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14672 }, RecordDef);
14673 </code></pre>
14674  * <p>
14675  * This would consume an Array like this:
14676  * <pre><code>
14677 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14678   </code></pre>
14679  
14680  * @constructor
14681  * Create a new JsonReader
14682  * @param {Object} meta Metadata configuration options.
14683  * @param {Object|Array} recordType Either an Array of field definition objects
14684  * 
14685  * @cfg {Array} fields Array of field definition objects
14686  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14687  * as specified to {@link Roo.data.Record#create},
14688  * or an {@link Roo.data.Record} object
14689  *
14690  * 
14691  * created using {@link Roo.data.Record#create}.
14692  */
14693 Roo.data.ArrayReader = function(meta, recordType)
14694 {    
14695     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14696 };
14697
14698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14699     
14700       /**
14701      * Create a data block containing Roo.data.Records from an XML document.
14702      * @param {Object} o An Array of row objects which represents the dataset.
14703      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o)
14707     {
14708         var sid = this.meta ? this.meta.id : null;
14709         var recordType = this.recordType, fields = recordType.prototype.fields;
14710         var records = [];
14711         var root = o;
14712         for(var i = 0; i < root.length; i++){
14713                 var n = root[i];
14714             var values = {};
14715             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14716             for(var j = 0, jlen = fields.length; j < jlen; j++){
14717                 var f = fields.items[j];
14718                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14719                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14720                 v = f.convert(v);
14721                 values[f.name] = v;
14722             }
14723             var record = new recordType(values, id);
14724             record.json = n;
14725             records[records.length] = record;
14726         }
14727         return {
14728             records : records,
14729             totalRecords : records.length
14730         };
14731     },
14732     // used when loading children.. @see loadDataFromChildren
14733     toLoadData: function(rec)
14734     {
14735         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14736         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14737         
14738     }
14739     
14740     
14741 });/*
14742  * - LGPL
14743  * * 
14744  */
14745
14746 /**
14747  * @class Roo.bootstrap.ComboBox
14748  * @extends Roo.bootstrap.TriggerField
14749  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14750  * @cfg {Boolean} append (true|false) default false
14751  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14752  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14753  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14754  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14755  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14756  * @cfg {Boolean} animate default true
14757  * @cfg {Boolean} emptyResultText only for touch device
14758  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14759  * @cfg {String} emptyTitle default ''
14760  * @cfg {Number} width fixed with? experimental
14761  * @constructor
14762  * Create a new ComboBox.
14763  * @param {Object} config Configuration options
14764  */
14765 Roo.bootstrap.ComboBox = function(config){
14766     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14767     this.addEvents({
14768         /**
14769          * @event expand
14770          * Fires when the dropdown list is expanded
14771         * @param {Roo.bootstrap.ComboBox} combo This combo box
14772         */
14773         'expand' : true,
14774         /**
14775          * @event collapse
14776          * Fires when the dropdown list is collapsed
14777         * @param {Roo.bootstrap.ComboBox} combo This combo box
14778         */
14779         'collapse' : true,
14780         /**
14781          * @event beforeselect
14782          * Fires before a list item is selected. Return false to cancel the selection.
14783         * @param {Roo.bootstrap.ComboBox} combo This combo box
14784         * @param {Roo.data.Record} record The data record returned from the underlying store
14785         * @param {Number} index The index of the selected item in the dropdown list
14786         */
14787         'beforeselect' : true,
14788         /**
14789          * @event select
14790          * Fires when a list item is selected
14791         * @param {Roo.bootstrap.ComboBox} combo This combo box
14792         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14793         * @param {Number} index The index of the selected item in the dropdown list
14794         */
14795         'select' : true,
14796         /**
14797          * @event beforequery
14798          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14799          * The event object passed has these properties:
14800         * @param {Roo.bootstrap.ComboBox} combo This combo box
14801         * @param {String} query The query
14802         * @param {Boolean} forceAll true to force "all" query
14803         * @param {Boolean} cancel true to cancel the query
14804         * @param {Object} e The query event object
14805         */
14806         'beforequery': true,
14807          /**
14808          * @event add
14809          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14810         * @param {Roo.bootstrap.ComboBox} combo This combo box
14811         */
14812         'add' : true,
14813         /**
14814          * @event edit
14815          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14816         * @param {Roo.bootstrap.ComboBox} combo This combo box
14817         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14818         */
14819         'edit' : true,
14820         /**
14821          * @event remove
14822          * Fires when the remove value from the combobox array
14823         * @param {Roo.bootstrap.ComboBox} combo This combo box
14824         */
14825         'remove' : true,
14826         /**
14827          * @event afterremove
14828          * Fires when the remove value from the combobox array
14829         * @param {Roo.bootstrap.ComboBox} combo This combo box
14830         */
14831         'afterremove' : true,
14832         /**
14833          * @event specialfilter
14834          * Fires when specialfilter
14835             * @param {Roo.bootstrap.ComboBox} combo This combo box
14836             */
14837         'specialfilter' : true,
14838         /**
14839          * @event tick
14840          * Fires when tick the element
14841             * @param {Roo.bootstrap.ComboBox} combo This combo box
14842             */
14843         'tick' : true,
14844         /**
14845          * @event touchviewdisplay
14846          * Fires when touch view require special display (default is using displayField)
14847             * @param {Roo.bootstrap.ComboBox} combo This combo box
14848             * @param {Object} cfg set html .
14849             */
14850         'touchviewdisplay' : true
14851         
14852     });
14853     
14854     this.item = [];
14855     this.tickItems = [];
14856     
14857     this.selectedIndex = -1;
14858     if(this.mode == 'local'){
14859         if(config.queryDelay === undefined){
14860             this.queryDelay = 10;
14861         }
14862         if(config.minChars === undefined){
14863             this.minChars = 0;
14864         }
14865     }
14866 };
14867
14868 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14869      
14870     /**
14871      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14872      * rendering into an Roo.Editor, defaults to false)
14873      */
14874     /**
14875      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14876      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14877      */
14878     /**
14879      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14880      */
14881     /**
14882      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14883      * the dropdown list (defaults to undefined, with no header element)
14884      */
14885
14886      /**
14887      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14888      */
14889      
14890      /**
14891      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14892      */
14893     listWidth: undefined,
14894     /**
14895      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14896      * mode = 'remote' or 'text' if mode = 'local')
14897      */
14898     displayField: undefined,
14899     
14900     /**
14901      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14902      * mode = 'remote' or 'value' if mode = 'local'). 
14903      * Note: use of a valueField requires the user make a selection
14904      * in order for a value to be mapped.
14905      */
14906     valueField: undefined,
14907     /**
14908      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14909      */
14910     modalTitle : '',
14911     
14912     /**
14913      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14914      * field's data value (defaults to the underlying DOM element's name)
14915      */
14916     hiddenName: undefined,
14917     /**
14918      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14919      */
14920     listClass: '',
14921     /**
14922      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14923      */
14924     selectedClass: 'active',
14925     
14926     /**
14927      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14928      */
14929     shadow:'sides',
14930     /**
14931      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14932      * anchor positions (defaults to 'tl-bl')
14933      */
14934     listAlign: 'tl-bl?',
14935     /**
14936      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14937      */
14938     maxHeight: 300,
14939     /**
14940      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14941      * query specified by the allQuery config option (defaults to 'query')
14942      */
14943     triggerAction: 'query',
14944     /**
14945      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14946      * (defaults to 4, does not apply if editable = false)
14947      */
14948     minChars : 4,
14949     /**
14950      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14951      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14952      */
14953     typeAhead: false,
14954     /**
14955      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14956      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14957      */
14958     queryDelay: 500,
14959     /**
14960      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14961      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14962      */
14963     pageSize: 0,
14964     /**
14965      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14966      * when editable = true (defaults to false)
14967      */
14968     selectOnFocus:false,
14969     /**
14970      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14971      */
14972     queryParam: 'query',
14973     /**
14974      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14975      * when mode = 'remote' (defaults to 'Loading...')
14976      */
14977     loadingText: 'Loading...',
14978     /**
14979      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14980      */
14981     resizable: false,
14982     /**
14983      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14984      */
14985     handleHeight : 8,
14986     /**
14987      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14988      * traditional select (defaults to true)
14989      */
14990     editable: true,
14991     /**
14992      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14993      */
14994     allQuery: '',
14995     /**
14996      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14997      */
14998     mode: 'remote',
14999     /**
15000      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15001      * listWidth has a higher value)
15002      */
15003     minListWidth : 70,
15004     /**
15005      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15006      * allow the user to set arbitrary text into the field (defaults to false)
15007      */
15008     forceSelection:false,
15009     /**
15010      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15011      * if typeAhead = true (defaults to 250)
15012      */
15013     typeAheadDelay : 250,
15014     /**
15015      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15016      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15017      */
15018     valueNotFoundText : undefined,
15019     /**
15020      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15021      */
15022     blockFocus : false,
15023     
15024     /**
15025      * @cfg {Boolean} disableClear Disable showing of clear button.
15026      */
15027     disableClear : false,
15028     /**
15029      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15030      */
15031     alwaysQuery : false,
15032     
15033     /**
15034      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15035      */
15036     multiple : false,
15037     
15038     /**
15039      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15040      */
15041     invalidClass : "has-warning",
15042     
15043     /**
15044      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15045      */
15046     validClass : "has-success",
15047     
15048     /**
15049      * @cfg {Boolean} specialFilter (true|false) special filter default false
15050      */
15051     specialFilter : false,
15052     
15053     /**
15054      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15055      */
15056     mobileTouchView : true,
15057     
15058     /**
15059      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15060      */
15061     useNativeIOS : false,
15062     
15063     /**
15064      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15065      */
15066     mobile_restrict_height : false,
15067     
15068     ios_options : false,
15069     
15070     //private
15071     addicon : false,
15072     editicon: false,
15073     
15074     page: 0,
15075     hasQuery: false,
15076     append: false,
15077     loadNext: false,
15078     autoFocus : true,
15079     tickable : false,
15080     btnPosition : 'right',
15081     triggerList : true,
15082     showToggleBtn : true,
15083     animate : true,
15084     emptyResultText: 'Empty',
15085     triggerText : 'Select',
15086     emptyTitle : '',
15087     width : false,
15088     
15089     // element that contains real text value.. (when hidden is used..)
15090     
15091     getAutoCreate : function()
15092     {   
15093         var cfg = false;
15094         //render
15095         /*
15096          * Render classic select for iso
15097          */
15098         
15099         if(Roo.isIOS && this.useNativeIOS){
15100             cfg = this.getAutoCreateNativeIOS();
15101             return cfg;
15102         }
15103         
15104         /*
15105          * Touch Devices
15106          */
15107         
15108         if(Roo.isTouch && this.mobileTouchView){
15109             cfg = this.getAutoCreateTouchView();
15110             return cfg;;
15111         }
15112         
15113         /*
15114          *  Normal ComboBox
15115          */
15116         if(!this.tickable){
15117             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15118             return cfg;
15119         }
15120         
15121         /*
15122          *  ComboBox with tickable selections
15123          */
15124              
15125         var align = this.labelAlign || this.parentLabelAlign();
15126         
15127         cfg = {
15128             cls : 'form-group roo-combobox-tickable' //input-group
15129         };
15130         
15131         var btn_text_select = '';
15132         var btn_text_done = '';
15133         var btn_text_cancel = '';
15134         
15135         if (this.btn_text_show) {
15136             btn_text_select = 'Select';
15137             btn_text_done = 'Done';
15138             btn_text_cancel = 'Cancel'; 
15139         }
15140         
15141         var buttons = {
15142             tag : 'div',
15143             cls : 'tickable-buttons',
15144             cn : [
15145                 {
15146                     tag : 'button',
15147                     type : 'button',
15148                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15149                     //html : this.triggerText
15150                     html: btn_text_select
15151                 },
15152                 {
15153                     tag : 'button',
15154                     type : 'button',
15155                     name : 'ok',
15156                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15157                     //html : 'Done'
15158                     html: btn_text_done
15159                 },
15160                 {
15161                     tag : 'button',
15162                     type : 'button',
15163                     name : 'cancel',
15164                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15165                     //html : 'Cancel'
15166                     html: btn_text_cancel
15167                 }
15168             ]
15169         };
15170         
15171         if(this.editable){
15172             buttons.cn.unshift({
15173                 tag: 'input',
15174                 cls: 'roo-select2-search-field-input'
15175             });
15176         }
15177         
15178         var _this = this;
15179         
15180         Roo.each(buttons.cn, function(c){
15181             if (_this.size) {
15182                 c.cls += ' btn-' + _this.size;
15183             }
15184
15185             if (_this.disabled) {
15186                 c.disabled = true;
15187             }
15188         });
15189         
15190         var box = {
15191             tag: 'div',
15192             style : 'display: contents',
15193             cn: [
15194                 {
15195                     tag: 'input',
15196                     type : 'hidden',
15197                     cls: 'form-hidden-field'
15198                 },
15199                 {
15200                     tag: 'ul',
15201                     cls: 'roo-select2-choices',
15202                     cn:[
15203                         {
15204                             tag: 'li',
15205                             cls: 'roo-select2-search-field',
15206                             cn: [
15207                                 buttons
15208                             ]
15209                         }
15210                     ]
15211                 }
15212             ]
15213         };
15214         
15215         var combobox = {
15216             cls: 'roo-select2-container input-group roo-select2-container-multi',
15217             cn: [
15218                 
15219                 box
15220 //                {
15221 //                    tag: 'ul',
15222 //                    cls: 'typeahead typeahead-long dropdown-menu',
15223 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15224 //                }
15225             ]
15226         };
15227         
15228         if(this.hasFeedback && !this.allowBlank){
15229             
15230             var feedback = {
15231                 tag: 'span',
15232                 cls: 'glyphicon form-control-feedback'
15233             };
15234
15235             combobox.cn.push(feedback);
15236         }
15237         
15238         
15239         
15240         var indicator = {
15241             tag : 'i',
15242             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15243             tooltip : 'This field is required'
15244         };
15245         if (Roo.bootstrap.version == 4) {
15246             indicator = {
15247                 tag : 'i',
15248                 style : 'display:none'
15249             };
15250         }
15251         if (align ==='left' && this.fieldLabel.length) {
15252             
15253             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15254             
15255             cfg.cn = [
15256                 indicator,
15257                 {
15258                     tag: 'label',
15259                     'for' :  id,
15260                     cls : 'control-label col-form-label',
15261                     html : this.fieldLabel
15262
15263                 },
15264                 {
15265                     cls : "", 
15266                     cn: [
15267                         combobox
15268                     ]
15269                 }
15270
15271             ];
15272             
15273             var labelCfg = cfg.cn[1];
15274             var contentCfg = cfg.cn[2];
15275             
15276
15277             if(this.indicatorpos == 'right'){
15278                 
15279                 cfg.cn = [
15280                     {
15281                         tag: 'label',
15282                         'for' :  id,
15283                         cls : 'control-label col-form-label',
15284                         cn : [
15285                             {
15286                                 tag : 'span',
15287                                 html : this.fieldLabel
15288                             },
15289                             indicator
15290                         ]
15291                     },
15292                     {
15293                         cls : "",
15294                         cn: [
15295                             combobox
15296                         ]
15297                     }
15298
15299                 ];
15300                 
15301                 
15302                 
15303                 labelCfg = cfg.cn[0];
15304                 contentCfg = cfg.cn[1];
15305             
15306             }
15307             
15308             if(this.labelWidth > 12){
15309                 labelCfg.style = "width: " + this.labelWidth + 'px';
15310             }
15311             if(this.width * 1 > 0){
15312                 contentCfg.style = "width: " + this.width + 'px';
15313             }
15314             if(this.labelWidth < 13 && this.labelmd == 0){
15315                 this.labelmd = this.labelWidth;
15316             }
15317             
15318             if(this.labellg > 0){
15319                 labelCfg.cls += ' col-lg-' + this.labellg;
15320                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15321             }
15322             
15323             if(this.labelmd > 0){
15324                 labelCfg.cls += ' col-md-' + this.labelmd;
15325                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15326             }
15327             
15328             if(this.labelsm > 0){
15329                 labelCfg.cls += ' col-sm-' + this.labelsm;
15330                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15331             }
15332             
15333             if(this.labelxs > 0){
15334                 labelCfg.cls += ' col-xs-' + this.labelxs;
15335                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15336             }
15337                 
15338                 
15339         } else if ( this.fieldLabel.length) {
15340 //                Roo.log(" label");
15341                  cfg.cn = [
15342                    indicator,
15343                     {
15344                         tag: 'label',
15345                         //cls : 'input-group-addon',
15346                         html : this.fieldLabel
15347                     },
15348                     combobox
15349                 ];
15350                 
15351                 if(this.indicatorpos == 'right'){
15352                     cfg.cn = [
15353                         {
15354                             tag: 'label',
15355                             //cls : 'input-group-addon',
15356                             html : this.fieldLabel
15357                         },
15358                         indicator,
15359                         combobox
15360                     ];
15361                     
15362                 }
15363
15364         } else {
15365             
15366 //                Roo.log(" no label && no align");
15367                 cfg = combobox
15368                      
15369                 
15370         }
15371          
15372         var settings=this;
15373         ['xs','sm','md','lg'].map(function(size){
15374             if (settings[size]) {
15375                 cfg.cls += ' col-' + size + '-' + settings[size];
15376             }
15377         });
15378         
15379         return cfg;
15380         
15381     },
15382     
15383     _initEventsCalled : false,
15384     
15385     // private
15386     initEvents: function()
15387     {   
15388         if (this._initEventsCalled) { // as we call render... prevent looping...
15389             return;
15390         }
15391         this._initEventsCalled = true;
15392         
15393         if (!this.store) {
15394             throw "can not find store for combo";
15395         }
15396         
15397         this.indicator = this.indicatorEl();
15398         
15399         this.store = Roo.factory(this.store, Roo.data);
15400         this.store.parent = this;
15401         
15402         // if we are building from html. then this element is so complex, that we can not really
15403         // use the rendered HTML.
15404         // so we have to trash and replace the previous code.
15405         if (Roo.XComponent.build_from_html) {
15406             // remove this element....
15407             var e = this.el.dom, k=0;
15408             while (e ) { e = e.previousSibling;  ++k;}
15409
15410             this.el.remove();
15411             
15412             this.el=false;
15413             this.rendered = false;
15414             
15415             this.render(this.parent().getChildContainer(true), k);
15416         }
15417         
15418         if(Roo.isIOS && this.useNativeIOS){
15419             this.initIOSView();
15420             return;
15421         }
15422         
15423         /*
15424          * Touch Devices
15425          */
15426         
15427         if(Roo.isTouch && this.mobileTouchView){
15428             this.initTouchView();
15429             return;
15430         }
15431         
15432         if(this.tickable){
15433             this.initTickableEvents();
15434             return;
15435         }
15436         
15437         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15438         
15439         if(this.hiddenName){
15440             
15441             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15442             
15443             this.hiddenField.dom.value =
15444                 this.hiddenValue !== undefined ? this.hiddenValue :
15445                 this.value !== undefined ? this.value : '';
15446
15447             // prevent input submission
15448             this.el.dom.removeAttribute('name');
15449             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15450              
15451              
15452         }
15453         //if(Roo.isGecko){
15454         //    this.el.dom.setAttribute('autocomplete', 'off');
15455         //}
15456         
15457         var cls = 'x-combo-list';
15458         
15459         //this.list = new Roo.Layer({
15460         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15461         //});
15462         
15463         var _this = this;
15464         
15465         (function(){
15466             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15467             _this.list.setWidth(lw);
15468         }).defer(100);
15469         
15470         this.list.on('mouseover', this.onViewOver, this);
15471         this.list.on('mousemove', this.onViewMove, this);
15472         this.list.on('scroll', this.onViewScroll, this);
15473         
15474         /*
15475         this.list.swallowEvent('mousewheel');
15476         this.assetHeight = 0;
15477
15478         if(this.title){
15479             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15480             this.assetHeight += this.header.getHeight();
15481         }
15482
15483         this.innerList = this.list.createChild({cls:cls+'-inner'});
15484         this.innerList.on('mouseover', this.onViewOver, this);
15485         this.innerList.on('mousemove', this.onViewMove, this);
15486         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15487         
15488         if(this.allowBlank && !this.pageSize && !this.disableClear){
15489             this.footer = this.list.createChild({cls:cls+'-ft'});
15490             this.pageTb = new Roo.Toolbar(this.footer);
15491            
15492         }
15493         if(this.pageSize){
15494             this.footer = this.list.createChild({cls:cls+'-ft'});
15495             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15496                     {pageSize: this.pageSize});
15497             
15498         }
15499         
15500         if (this.pageTb && this.allowBlank && !this.disableClear) {
15501             var _this = this;
15502             this.pageTb.add(new Roo.Toolbar.Fill(), {
15503                 cls: 'x-btn-icon x-btn-clear',
15504                 text: '&#160;',
15505                 handler: function()
15506                 {
15507                     _this.collapse();
15508                     _this.clearValue();
15509                     _this.onSelect(false, -1);
15510                 }
15511             });
15512         }
15513         if (this.footer) {
15514             this.assetHeight += this.footer.getHeight();
15515         }
15516         */
15517             
15518         if(!this.tpl){
15519             this.tpl = Roo.bootstrap.version == 4 ?
15520                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15521                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15522         }
15523
15524         this.view = new Roo.View(this.list, this.tpl, {
15525             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15526         });
15527         //this.view.wrapEl.setDisplayed(false);
15528         this.view.on('click', this.onViewClick, this);
15529         
15530         
15531         this.store.on('beforeload', this.onBeforeLoad, this);
15532         this.store.on('load', this.onLoad, this);
15533         this.store.on('loadexception', this.onLoadException, this);
15534         /*
15535         if(this.resizable){
15536             this.resizer = new Roo.Resizable(this.list,  {
15537                pinned:true, handles:'se'
15538             });
15539             this.resizer.on('resize', function(r, w, h){
15540                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15541                 this.listWidth = w;
15542                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15543                 this.restrictHeight();
15544             }, this);
15545             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15546         }
15547         */
15548         if(!this.editable){
15549             this.editable = true;
15550             this.setEditable(false);
15551         }
15552         
15553         /*
15554         
15555         if (typeof(this.events.add.listeners) != 'undefined') {
15556             
15557             this.addicon = this.wrap.createChild(
15558                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15559        
15560             this.addicon.on('click', function(e) {
15561                 this.fireEvent('add', this);
15562             }, this);
15563         }
15564         if (typeof(this.events.edit.listeners) != 'undefined') {
15565             
15566             this.editicon = this.wrap.createChild(
15567                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15568             if (this.addicon) {
15569                 this.editicon.setStyle('margin-left', '40px');
15570             }
15571             this.editicon.on('click', function(e) {
15572                 
15573                 // we fire even  if inothing is selected..
15574                 this.fireEvent('edit', this, this.lastData );
15575                 
15576             }, this);
15577         }
15578         */
15579         
15580         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15581             "up" : function(e){
15582                 this.inKeyMode = true;
15583                 this.selectPrev();
15584             },
15585
15586             "down" : function(e){
15587                 if(!this.isExpanded()){
15588                     this.onTriggerClick();
15589                 }else{
15590                     this.inKeyMode = true;
15591                     this.selectNext();
15592                 }
15593             },
15594
15595             "enter" : function(e){
15596 //                this.onViewClick();
15597                 //return true;
15598                 this.collapse();
15599                 
15600                 if(this.fireEvent("specialkey", this, e)){
15601                     this.onViewClick(false);
15602                 }
15603                 
15604                 return true;
15605             },
15606
15607             "esc" : function(e){
15608                 this.collapse();
15609             },
15610
15611             "tab" : function(e){
15612                 this.collapse();
15613                 
15614                 if(this.fireEvent("specialkey", this, e)){
15615                     this.onViewClick(false);
15616                 }
15617                 
15618                 return true;
15619             },
15620
15621             scope : this,
15622
15623             doRelay : function(foo, bar, hname){
15624                 if(hname == 'down' || this.scope.isExpanded()){
15625                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15626                 }
15627                 return true;
15628             },
15629
15630             forceKeyDown: true
15631         });
15632         
15633         
15634         this.queryDelay = Math.max(this.queryDelay || 10,
15635                 this.mode == 'local' ? 10 : 250);
15636         
15637         
15638         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15639         
15640         if(this.typeAhead){
15641             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15642         }
15643         if(this.editable !== false){
15644             this.inputEl().on("keyup", this.onKeyUp, this);
15645         }
15646         if(this.forceSelection){
15647             this.inputEl().on('blur', this.doForce, this);
15648         }
15649         
15650         if(this.multiple){
15651             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15652             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15653         }
15654     },
15655     
15656     initTickableEvents: function()
15657     {   
15658         this.createList();
15659         
15660         if(this.hiddenName){
15661             
15662             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15663             
15664             this.hiddenField.dom.value =
15665                 this.hiddenValue !== undefined ? this.hiddenValue :
15666                 this.value !== undefined ? this.value : '';
15667
15668             // prevent input submission
15669             this.el.dom.removeAttribute('name');
15670             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15671              
15672              
15673         }
15674         
15675 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15676         
15677         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15678         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15679         if(this.triggerList){
15680             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15681         }
15682          
15683         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15684         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15685         
15686         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15687         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15688         
15689         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15690         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15691         
15692         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15693         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15694         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15695         
15696         this.okBtn.hide();
15697         this.cancelBtn.hide();
15698         
15699         var _this = this;
15700         
15701         (function(){
15702             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15703             _this.list.setWidth(lw);
15704         }).defer(100);
15705         
15706         this.list.on('mouseover', this.onViewOver, this);
15707         this.list.on('mousemove', this.onViewMove, this);
15708         
15709         this.list.on('scroll', this.onViewScroll, this);
15710         
15711         if(!this.tpl){
15712             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15713                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15714         }
15715
15716         this.view = new Roo.View(this.list, this.tpl, {
15717             singleSelect:true,
15718             tickable:true,
15719             parent:this,
15720             store: this.store,
15721             selectedClass: this.selectedClass
15722         });
15723         
15724         //this.view.wrapEl.setDisplayed(false);
15725         this.view.on('click', this.onViewClick, this);
15726         
15727         
15728         
15729         this.store.on('beforeload', this.onBeforeLoad, this);
15730         this.store.on('load', this.onLoad, this);
15731         this.store.on('loadexception', this.onLoadException, this);
15732         
15733         if(this.editable){
15734             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15735                 "up" : function(e){
15736                     this.inKeyMode = true;
15737                     this.selectPrev();
15738                 },
15739
15740                 "down" : function(e){
15741                     this.inKeyMode = true;
15742                     this.selectNext();
15743                 },
15744
15745                 "enter" : function(e){
15746                     if(this.fireEvent("specialkey", this, e)){
15747                         this.onViewClick(false);
15748                     }
15749                     
15750                     return true;
15751                 },
15752
15753                 "esc" : function(e){
15754                     this.onTickableFooterButtonClick(e, false, false);
15755                 },
15756
15757                 "tab" : function(e){
15758                     this.fireEvent("specialkey", this, e);
15759                     
15760                     this.onTickableFooterButtonClick(e, false, false);
15761                     
15762                     return true;
15763                 },
15764
15765                 scope : this,
15766
15767                 doRelay : function(e, fn, key){
15768                     if(this.scope.isExpanded()){
15769                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15770                     }
15771                     return true;
15772                 },
15773
15774                 forceKeyDown: true
15775             });
15776         }
15777         
15778         this.queryDelay = Math.max(this.queryDelay || 10,
15779                 this.mode == 'local' ? 10 : 250);
15780         
15781         
15782         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15783         
15784         if(this.typeAhead){
15785             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15786         }
15787         
15788         if(this.editable !== false){
15789             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15790         }
15791         
15792         this.indicator = this.indicatorEl();
15793         
15794         if(this.indicator){
15795             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15796             this.indicator.hide();
15797         }
15798         
15799     },
15800
15801     onDestroy : function(){
15802         if(this.view){
15803             this.view.setStore(null);
15804             this.view.el.removeAllListeners();
15805             this.view.el.remove();
15806             this.view.purgeListeners();
15807         }
15808         if(this.list){
15809             this.list.dom.innerHTML  = '';
15810         }
15811         
15812         if(this.store){
15813             this.store.un('beforeload', this.onBeforeLoad, this);
15814             this.store.un('load', this.onLoad, this);
15815             this.store.un('loadexception', this.onLoadException, this);
15816         }
15817         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15818     },
15819
15820     // private
15821     fireKey : function(e){
15822         if(e.isNavKeyPress() && !this.list.isVisible()){
15823             this.fireEvent("specialkey", this, e);
15824         }
15825     },
15826
15827     // private
15828     onResize: function(w, h)
15829     {
15830         
15831         
15832 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15833 //        
15834 //        if(typeof w != 'number'){
15835 //            // we do not handle it!?!?
15836 //            return;
15837 //        }
15838 //        var tw = this.trigger.getWidth();
15839 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15840 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15841 //        var x = w - tw;
15842 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15843 //            
15844 //        //this.trigger.setStyle('left', x+'px');
15845 //        
15846 //        if(this.list && this.listWidth === undefined){
15847 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15848 //            this.list.setWidth(lw);
15849 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15850 //        }
15851         
15852     
15853         
15854     },
15855
15856     /**
15857      * Allow or prevent the user from directly editing the field text.  If false is passed,
15858      * the user will only be able to select from the items defined in the dropdown list.  This method
15859      * is the runtime equivalent of setting the 'editable' config option at config time.
15860      * @param {Boolean} value True to allow the user to directly edit the field text
15861      */
15862     setEditable : function(value){
15863         if(value == this.editable){
15864             return;
15865         }
15866         this.editable = value;
15867         if(!value){
15868             this.inputEl().dom.setAttribute('readOnly', true);
15869             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15870             this.inputEl().addClass('x-combo-noedit');
15871         }else{
15872             this.inputEl().dom.setAttribute('readOnly', false);
15873             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15874             this.inputEl().removeClass('x-combo-noedit');
15875         }
15876     },
15877
15878     // private
15879     
15880     onBeforeLoad : function(combo,opts){
15881         if(!this.hasFocus){
15882             return;
15883         }
15884          if (!opts.add) {
15885             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15886          }
15887         this.restrictHeight();
15888         this.selectedIndex = -1;
15889     },
15890
15891     // private
15892     onLoad : function(){
15893         
15894         this.hasQuery = false;
15895         
15896         if(!this.hasFocus){
15897             return;
15898         }
15899         
15900         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15901             this.loading.hide();
15902         }
15903         
15904         if(this.store.getCount() > 0){
15905             
15906             this.expand();
15907             this.restrictHeight();
15908             if(this.lastQuery == this.allQuery){
15909                 if(this.editable && !this.tickable){
15910                     this.inputEl().dom.select();
15911                 }
15912                 
15913                 if(
15914                     !this.selectByValue(this.value, true) &&
15915                     this.autoFocus && 
15916                     (
15917                         !this.store.lastOptions ||
15918                         typeof(this.store.lastOptions.add) == 'undefined' || 
15919                         this.store.lastOptions.add != true
15920                     )
15921                 ){
15922                     this.select(0, true);
15923                 }
15924             }else{
15925                 if(this.autoFocus){
15926                     this.selectNext();
15927                 }
15928                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15929                     this.taTask.delay(this.typeAheadDelay);
15930                 }
15931             }
15932         }else{
15933             this.onEmptyResults();
15934         }
15935         
15936         //this.el.focus();
15937     },
15938     // private
15939     onLoadException : function()
15940     {
15941         this.hasQuery = false;
15942         
15943         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15944             this.loading.hide();
15945         }
15946         
15947         if(this.tickable && this.editable){
15948             return;
15949         }
15950         
15951         this.collapse();
15952         // only causes errors at present
15953         //Roo.log(this.store.reader.jsonData);
15954         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15955             // fixme
15956             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15957         //}
15958         
15959         
15960     },
15961     // private
15962     onTypeAhead : function(){
15963         if(this.store.getCount() > 0){
15964             var r = this.store.getAt(0);
15965             var newValue = r.data[this.displayField];
15966             var len = newValue.length;
15967             var selStart = this.getRawValue().length;
15968             
15969             if(selStart != len){
15970                 this.setRawValue(newValue);
15971                 this.selectText(selStart, newValue.length);
15972             }
15973         }
15974     },
15975
15976     // private
15977     onSelect : function(record, index){
15978         
15979         if(this.fireEvent('beforeselect', this, record, index) !== false){
15980         
15981             this.setFromData(index > -1 ? record.data : false);
15982             
15983             this.collapse();
15984             this.fireEvent('select', this, record, index);
15985         }
15986     },
15987
15988     /**
15989      * Returns the currently selected field value or empty string if no value is set.
15990      * @return {String} value The selected value
15991      */
15992     getValue : function()
15993     {
15994         if(Roo.isIOS && this.useNativeIOS){
15995             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15996         }
15997         
15998         if(this.multiple){
15999             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16000         }
16001         
16002         if(this.valueField){
16003             return typeof this.value != 'undefined' ? this.value : '';
16004         }else{
16005             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16006         }
16007     },
16008     
16009     getRawValue : function()
16010     {
16011         if(Roo.isIOS && this.useNativeIOS){
16012             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16013         }
16014         
16015         var v = this.inputEl().getValue();
16016         
16017         return v;
16018     },
16019
16020     /**
16021      * Clears any text/value currently set in the field
16022      */
16023     clearValue : function(){
16024         
16025         if(this.hiddenField){
16026             this.hiddenField.dom.value = '';
16027         }
16028         this.value = '';
16029         this.setRawValue('');
16030         this.lastSelectionText = '';
16031         this.lastData = false;
16032         
16033         var close = this.closeTriggerEl();
16034         
16035         if(close){
16036             close.hide();
16037         }
16038         
16039         this.validate();
16040         
16041     },
16042
16043     /**
16044      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16045      * will be displayed in the field.  If the value does not match the data value of an existing item,
16046      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16047      * Otherwise the field will be blank (although the value will still be set).
16048      * @param {String} value The value to match
16049      */
16050     setValue : function(v)
16051     {
16052         if(Roo.isIOS && this.useNativeIOS){
16053             this.setIOSValue(v);
16054             return;
16055         }
16056         
16057         if(this.multiple){
16058             this.syncValue();
16059             return;
16060         }
16061         
16062         var text = v;
16063         if(this.valueField){
16064             var r = this.findRecord(this.valueField, v);
16065             if(r){
16066                 text = r.data[this.displayField];
16067             }else if(this.valueNotFoundText !== undefined){
16068                 text = this.valueNotFoundText;
16069             }
16070         }
16071         this.lastSelectionText = text;
16072         if(this.hiddenField){
16073             this.hiddenField.dom.value = v;
16074         }
16075         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16076         this.value = v;
16077         
16078         var close = this.closeTriggerEl();
16079         
16080         if(close){
16081             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16082         }
16083         
16084         this.validate();
16085     },
16086     /**
16087      * @property {Object} the last set data for the element
16088      */
16089     
16090     lastData : false,
16091     /**
16092      * Sets the value of the field based on a object which is related to the record format for the store.
16093      * @param {Object} value the value to set as. or false on reset?
16094      */
16095     setFromData : function(o){
16096         
16097         if(this.multiple){
16098             this.addItem(o);
16099             return;
16100         }
16101             
16102         var dv = ''; // display value
16103         var vv = ''; // value value..
16104         this.lastData = o;
16105         if (this.displayField) {
16106             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16107         } else {
16108             // this is an error condition!!!
16109             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16110         }
16111         
16112         if(this.valueField){
16113             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16114         }
16115         
16116         var close = this.closeTriggerEl();
16117         
16118         if(close){
16119             if(dv.length || vv * 1 > 0){
16120                 close.show() ;
16121                 this.blockFocus=true;
16122             } else {
16123                 close.hide();
16124             }             
16125         }
16126         
16127         if(this.hiddenField){
16128             this.hiddenField.dom.value = vv;
16129             
16130             this.lastSelectionText = dv;
16131             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16132             this.value = vv;
16133             return;
16134         }
16135         // no hidden field.. - we store the value in 'value', but still display
16136         // display field!!!!
16137         this.lastSelectionText = dv;
16138         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16139         this.value = vv;
16140         
16141         
16142         
16143     },
16144     // private
16145     reset : function(){
16146         // overridden so that last data is reset..
16147         
16148         if(this.multiple){
16149             this.clearItem();
16150             return;
16151         }
16152         
16153         this.setValue(this.originalValue);
16154         //this.clearInvalid();
16155         this.lastData = false;
16156         if (this.view) {
16157             this.view.clearSelections();
16158         }
16159         
16160         this.validate();
16161     },
16162     // private
16163     findRecord : function(prop, value){
16164         var record;
16165         if(this.store.getCount() > 0){
16166             this.store.each(function(r){
16167                 if(r.data[prop] == value){
16168                     record = r;
16169                     return false;
16170                 }
16171                 return true;
16172             });
16173         }
16174         return record;
16175     },
16176     
16177     getName: function()
16178     {
16179         // returns hidden if it's set..
16180         if (!this.rendered) {return ''};
16181         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16182         
16183     },
16184     // private
16185     onViewMove : function(e, t){
16186         this.inKeyMode = false;
16187     },
16188
16189     // private
16190     onViewOver : function(e, t){
16191         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16192             return;
16193         }
16194         var item = this.view.findItemFromChild(t);
16195         
16196         if(item){
16197             var index = this.view.indexOf(item);
16198             this.select(index, false);
16199         }
16200     },
16201
16202     // private
16203     onViewClick : function(view, doFocus, el, e)
16204     {
16205         var index = this.view.getSelectedIndexes()[0];
16206         
16207         var r = this.store.getAt(index);
16208         
16209         if(this.tickable){
16210             
16211             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16212                 return;
16213             }
16214             
16215             var rm = false;
16216             var _this = this;
16217             
16218             Roo.each(this.tickItems, function(v,k){
16219                 
16220                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16221                     Roo.log(v);
16222                     _this.tickItems.splice(k, 1);
16223                     
16224                     if(typeof(e) == 'undefined' && view == false){
16225                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16226                     }
16227                     
16228                     rm = true;
16229                     return;
16230                 }
16231             });
16232             
16233             if(rm){
16234                 return;
16235             }
16236             
16237             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16238                 this.tickItems.push(r.data);
16239             }
16240             
16241             if(typeof(e) == 'undefined' && view == false){
16242                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16243             }
16244                     
16245             return;
16246         }
16247         
16248         if(r){
16249             this.onSelect(r, index);
16250         }
16251         if(doFocus !== false && !this.blockFocus){
16252             this.inputEl().focus();
16253         }
16254     },
16255
16256     // private
16257     restrictHeight : function(){
16258         //this.innerList.dom.style.height = '';
16259         //var inner = this.innerList.dom;
16260         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16261         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16262         //this.list.beginUpdate();
16263         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16264         this.list.alignTo(this.inputEl(), this.listAlign);
16265         this.list.alignTo(this.inputEl(), this.listAlign);
16266         //this.list.endUpdate();
16267     },
16268
16269     // private
16270     onEmptyResults : function(){
16271         
16272         if(this.tickable && this.editable){
16273             this.hasFocus = false;
16274             this.restrictHeight();
16275             return;
16276         }
16277         
16278         this.collapse();
16279     },
16280
16281     /**
16282      * Returns true if the dropdown list is expanded, else false.
16283      */
16284     isExpanded : function(){
16285         return this.list.isVisible();
16286     },
16287
16288     /**
16289      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16290      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16291      * @param {String} value The data value of the item to select
16292      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16293      * selected item if it is not currently in view (defaults to true)
16294      * @return {Boolean} True if the value matched an item in the list, else false
16295      */
16296     selectByValue : function(v, scrollIntoView){
16297         if(v !== undefined && v !== null){
16298             var r = this.findRecord(this.valueField || this.displayField, v);
16299             if(r){
16300                 this.select(this.store.indexOf(r), scrollIntoView);
16301                 return true;
16302             }
16303         }
16304         return false;
16305     },
16306
16307     /**
16308      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16309      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16310      * @param {Number} index The zero-based index of the list item to select
16311      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16312      * selected item if it is not currently in view (defaults to true)
16313      */
16314     select : function(index, scrollIntoView){
16315         this.selectedIndex = index;
16316         this.view.select(index);
16317         if(scrollIntoView !== false){
16318             var el = this.view.getNode(index);
16319             /*
16320              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16321              */
16322             if(el){
16323                 this.list.scrollChildIntoView(el, false);
16324             }
16325         }
16326     },
16327
16328     // private
16329     selectNext : function(){
16330         var ct = this.store.getCount();
16331         if(ct > 0){
16332             if(this.selectedIndex == -1){
16333                 this.select(0);
16334             }else if(this.selectedIndex < ct-1){
16335                 this.select(this.selectedIndex+1);
16336             }
16337         }
16338     },
16339
16340     // private
16341     selectPrev : function(){
16342         var ct = this.store.getCount();
16343         if(ct > 0){
16344             if(this.selectedIndex == -1){
16345                 this.select(0);
16346             }else if(this.selectedIndex != 0){
16347                 this.select(this.selectedIndex-1);
16348             }
16349         }
16350     },
16351
16352     // private
16353     onKeyUp : function(e){
16354         if(this.editable !== false && !e.isSpecialKey()){
16355             this.lastKey = e.getKey();
16356             this.dqTask.delay(this.queryDelay);
16357         }
16358     },
16359
16360     // private
16361     validateBlur : function(){
16362         return !this.list || !this.list.isVisible();   
16363     },
16364
16365     // private
16366     initQuery : function(){
16367         
16368         var v = this.getRawValue();
16369         
16370         if(this.tickable && this.editable){
16371             v = this.tickableInputEl().getValue();
16372         }
16373         
16374         this.doQuery(v);
16375     },
16376
16377     // private
16378     doForce : function(){
16379         if(this.inputEl().dom.value.length > 0){
16380             this.inputEl().dom.value =
16381                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16382              
16383         }
16384     },
16385
16386     /**
16387      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16388      * query allowing the query action to be canceled if needed.
16389      * @param {String} query The SQL query to execute
16390      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16391      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16392      * saved in the current store (defaults to false)
16393      */
16394     doQuery : function(q, forceAll){
16395         
16396         if(q === undefined || q === null){
16397             q = '';
16398         }
16399         var qe = {
16400             query: q,
16401             forceAll: forceAll,
16402             combo: this,
16403             cancel:false
16404         };
16405         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16406             return false;
16407         }
16408         q = qe.query;
16409         
16410         forceAll = qe.forceAll;
16411         if(forceAll === true || (q.length >= this.minChars)){
16412             
16413             this.hasQuery = true;
16414             
16415             if(this.lastQuery != q || this.alwaysQuery){
16416                 this.lastQuery = q;
16417                 if(this.mode == 'local'){
16418                     this.selectedIndex = -1;
16419                     if(forceAll){
16420                         this.store.clearFilter();
16421                     }else{
16422                         
16423                         if(this.specialFilter){
16424                             this.fireEvent('specialfilter', this);
16425                             this.onLoad();
16426                             return;
16427                         }
16428                         
16429                         this.store.filter(this.displayField, q);
16430                     }
16431                     
16432                     this.store.fireEvent("datachanged", this.store);
16433                     
16434                     this.onLoad();
16435                     
16436                     
16437                 }else{
16438                     
16439                     this.store.baseParams[this.queryParam] = q;
16440                     
16441                     var options = {params : this.getParams(q)};
16442                     
16443                     if(this.loadNext){
16444                         options.add = true;
16445                         options.params.start = this.page * this.pageSize;
16446                     }
16447                     
16448                     this.store.load(options);
16449                     
16450                     /*
16451                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16452                      *  we should expand the list on onLoad
16453                      *  so command out it
16454                      */
16455 //                    this.expand();
16456                 }
16457             }else{
16458                 this.selectedIndex = -1;
16459                 this.onLoad();   
16460             }
16461         }
16462         
16463         this.loadNext = false;
16464     },
16465     
16466     // private
16467     getParams : function(q){
16468         var p = {};
16469         //p[this.queryParam] = q;
16470         
16471         if(this.pageSize){
16472             p.start = 0;
16473             p.limit = this.pageSize;
16474         }
16475         return p;
16476     },
16477
16478     /**
16479      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16480      */
16481     collapse : function(){
16482         if(!this.isExpanded()){
16483             return;
16484         }
16485         
16486         this.list.hide();
16487         
16488         this.hasFocus = false;
16489         
16490         if(this.tickable){
16491             this.okBtn.hide();
16492             this.cancelBtn.hide();
16493             this.trigger.show();
16494             
16495             if(this.editable){
16496                 this.tickableInputEl().dom.value = '';
16497                 this.tickableInputEl().blur();
16498             }
16499             
16500         }
16501         
16502         Roo.get(document).un('mousedown', this.collapseIf, this);
16503         Roo.get(document).un('mousewheel', this.collapseIf, this);
16504         if (!this.editable) {
16505             Roo.get(document).un('keydown', this.listKeyPress, this);
16506         }
16507         this.fireEvent('collapse', this);
16508         
16509         this.validate();
16510     },
16511
16512     // private
16513     collapseIf : function(e){
16514         var in_combo  = e.within(this.el);
16515         var in_list =  e.within(this.list);
16516         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16517         
16518         if (in_combo || in_list || is_list) {
16519             //e.stopPropagation();
16520             return;
16521         }
16522         
16523         if(this.tickable){
16524             this.onTickableFooterButtonClick(e, false, false);
16525         }
16526
16527         this.collapse();
16528         
16529     },
16530
16531     /**
16532      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16533      */
16534     expand : function(){
16535        
16536         if(this.isExpanded() || !this.hasFocus){
16537             return;
16538         }
16539         
16540         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16541         this.list.setWidth(lw);
16542         
16543         Roo.log('expand');
16544         
16545         this.list.show();
16546         
16547         this.restrictHeight();
16548         
16549         if(this.tickable){
16550             
16551             this.tickItems = Roo.apply([], this.item);
16552             
16553             this.okBtn.show();
16554             this.cancelBtn.show();
16555             this.trigger.hide();
16556             
16557             if(this.editable){
16558                 this.tickableInputEl().focus();
16559             }
16560             
16561         }
16562         
16563         Roo.get(document).on('mousedown', this.collapseIf, this);
16564         Roo.get(document).on('mousewheel', this.collapseIf, this);
16565         if (!this.editable) {
16566             Roo.get(document).on('keydown', this.listKeyPress, this);
16567         }
16568         
16569         this.fireEvent('expand', this);
16570     },
16571
16572     // private
16573     // Implements the default empty TriggerField.onTriggerClick function
16574     onTriggerClick : function(e)
16575     {
16576         Roo.log('trigger click');
16577         
16578         if(this.disabled || !this.triggerList){
16579             return;
16580         }
16581         
16582         this.page = 0;
16583         this.loadNext = false;
16584         
16585         if(this.isExpanded()){
16586             this.collapse();
16587             if (!this.blockFocus) {
16588                 this.inputEl().focus();
16589             }
16590             
16591         }else {
16592             this.hasFocus = true;
16593             if(this.triggerAction == 'all') {
16594                 this.doQuery(this.allQuery, true);
16595             } else {
16596                 this.doQuery(this.getRawValue());
16597             }
16598             if (!this.blockFocus) {
16599                 this.inputEl().focus();
16600             }
16601         }
16602     },
16603     
16604     onTickableTriggerClick : function(e)
16605     {
16606         if(this.disabled){
16607             return;
16608         }
16609         
16610         this.page = 0;
16611         this.loadNext = false;
16612         this.hasFocus = true;
16613         
16614         if(this.triggerAction == 'all') {
16615             this.doQuery(this.allQuery, true);
16616         } else {
16617             this.doQuery(this.getRawValue());
16618         }
16619     },
16620     
16621     onSearchFieldClick : function(e)
16622     {
16623         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16624             this.onTickableFooterButtonClick(e, false, false);
16625             return;
16626         }
16627         
16628         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16629             return;
16630         }
16631         
16632         this.page = 0;
16633         this.loadNext = false;
16634         this.hasFocus = true;
16635         
16636         if(this.triggerAction == 'all') {
16637             this.doQuery(this.allQuery, true);
16638         } else {
16639             this.doQuery(this.getRawValue());
16640         }
16641     },
16642     
16643     listKeyPress : function(e)
16644     {
16645         //Roo.log('listkeypress');
16646         // scroll to first matching element based on key pres..
16647         if (e.isSpecialKey()) {
16648             return false;
16649         }
16650         var k = String.fromCharCode(e.getKey()).toUpperCase();
16651         //Roo.log(k);
16652         var match  = false;
16653         var csel = this.view.getSelectedNodes();
16654         var cselitem = false;
16655         if (csel.length) {
16656             var ix = this.view.indexOf(csel[0]);
16657             cselitem  = this.store.getAt(ix);
16658             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16659                 cselitem = false;
16660             }
16661             
16662         }
16663         
16664         this.store.each(function(v) { 
16665             if (cselitem) {
16666                 // start at existing selection.
16667                 if (cselitem.id == v.id) {
16668                     cselitem = false;
16669                 }
16670                 return true;
16671             }
16672                 
16673             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16674                 match = this.store.indexOf(v);
16675                 return false;
16676             }
16677             return true;
16678         }, this);
16679         
16680         if (match === false) {
16681             return true; // no more action?
16682         }
16683         // scroll to?
16684         this.view.select(match);
16685         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16686         sn.scrollIntoView(sn.dom.parentNode, false);
16687     },
16688     
16689     onViewScroll : function(e, t){
16690         
16691         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){
16692             return;
16693         }
16694         
16695         this.hasQuery = true;
16696         
16697         this.loading = this.list.select('.loading', true).first();
16698         
16699         if(this.loading === null){
16700             this.list.createChild({
16701                 tag: 'div',
16702                 cls: 'loading roo-select2-more-results roo-select2-active',
16703                 html: 'Loading more results...'
16704             });
16705             
16706             this.loading = this.list.select('.loading', true).first();
16707             
16708             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16709             
16710             this.loading.hide();
16711         }
16712         
16713         this.loading.show();
16714         
16715         var _combo = this;
16716         
16717         this.page++;
16718         this.loadNext = true;
16719         
16720         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16721         
16722         return;
16723     },
16724     
16725     addItem : function(o)
16726     {   
16727         var dv = ''; // display value
16728         
16729         if (this.displayField) {
16730             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16731         } else {
16732             // this is an error condition!!!
16733             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16734         }
16735         
16736         if(!dv.length){
16737             return;
16738         }
16739         
16740         var choice = this.choices.createChild({
16741             tag: 'li',
16742             cls: 'roo-select2-search-choice',
16743             cn: [
16744                 {
16745                     tag: 'div',
16746                     html: dv
16747                 },
16748                 {
16749                     tag: 'a',
16750                     href: '#',
16751                     cls: 'roo-select2-search-choice-close fa fa-times',
16752                     tabindex: '-1'
16753                 }
16754             ]
16755             
16756         }, this.searchField);
16757         
16758         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16759         
16760         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16761         
16762         this.item.push(o);
16763         
16764         this.lastData = o;
16765         
16766         this.syncValue();
16767         
16768         this.inputEl().dom.value = '';
16769         
16770         this.validate();
16771     },
16772     
16773     onRemoveItem : function(e, _self, o)
16774     {
16775         e.preventDefault();
16776         
16777         this.lastItem = Roo.apply([], this.item);
16778         
16779         var index = this.item.indexOf(o.data) * 1;
16780         
16781         if( index < 0){
16782             Roo.log('not this item?!');
16783             return;
16784         }
16785         
16786         this.item.splice(index, 1);
16787         o.item.remove();
16788         
16789         this.syncValue();
16790         
16791         this.fireEvent('remove', this, e);
16792         
16793         this.validate();
16794         
16795     },
16796     
16797     syncValue : function()
16798     {
16799         if(!this.item.length){
16800             this.clearValue();
16801             return;
16802         }
16803             
16804         var value = [];
16805         var _this = this;
16806         Roo.each(this.item, function(i){
16807             if(_this.valueField){
16808                 value.push(i[_this.valueField]);
16809                 return;
16810             }
16811
16812             value.push(i);
16813         });
16814
16815         this.value = value.join(',');
16816
16817         if(this.hiddenField){
16818             this.hiddenField.dom.value = this.value;
16819         }
16820         
16821         this.store.fireEvent("datachanged", this.store);
16822         
16823         this.validate();
16824     },
16825     
16826     clearItem : function()
16827     {
16828         if(!this.multiple){
16829             return;
16830         }
16831         
16832         this.item = [];
16833         
16834         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16835            c.remove();
16836         });
16837         
16838         this.syncValue();
16839         
16840         this.validate();
16841         
16842         if(this.tickable && !Roo.isTouch){
16843             this.view.refresh();
16844         }
16845     },
16846     
16847     inputEl: function ()
16848     {
16849         if(Roo.isIOS && this.useNativeIOS){
16850             return this.el.select('select.roo-ios-select', true).first();
16851         }
16852         
16853         if(Roo.isTouch && this.mobileTouchView){
16854             return this.el.select('input.form-control',true).first();
16855         }
16856         
16857         if(this.tickable){
16858             return this.searchField;
16859         }
16860         
16861         return this.el.select('input.form-control',true).first();
16862     },
16863     
16864     onTickableFooterButtonClick : function(e, btn, el)
16865     {
16866         e.preventDefault();
16867         
16868         this.lastItem = Roo.apply([], this.item);
16869         
16870         if(btn && btn.name == 'cancel'){
16871             this.tickItems = Roo.apply([], this.item);
16872             this.collapse();
16873             return;
16874         }
16875         
16876         this.clearItem();
16877         
16878         var _this = this;
16879         
16880         Roo.each(this.tickItems, function(o){
16881             _this.addItem(o);
16882         });
16883         
16884         this.collapse();
16885         
16886     },
16887     
16888     validate : function()
16889     {
16890         if(this.getVisibilityEl().hasClass('hidden')){
16891             return true;
16892         }
16893         
16894         var v = this.getRawValue();
16895         
16896         if(this.multiple){
16897             v = this.getValue();
16898         }
16899         
16900         if(this.disabled || this.allowBlank || v.length){
16901             this.markValid();
16902             return true;
16903         }
16904         
16905         this.markInvalid();
16906         return false;
16907     },
16908     
16909     tickableInputEl : function()
16910     {
16911         if(!this.tickable || !this.editable){
16912             return this.inputEl();
16913         }
16914         
16915         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16916     },
16917     
16918     
16919     getAutoCreateTouchView : function()
16920     {
16921         var id = Roo.id();
16922         
16923         var cfg = {
16924             cls: 'form-group' //input-group
16925         };
16926         
16927         var input =  {
16928             tag: 'input',
16929             id : id,
16930             type : this.inputType,
16931             cls : 'form-control x-combo-noedit',
16932             autocomplete: 'new-password',
16933             placeholder : this.placeholder || '',
16934             readonly : true
16935         };
16936         
16937         if (this.name) {
16938             input.name = this.name;
16939         }
16940         
16941         if (this.size) {
16942             input.cls += ' input-' + this.size;
16943         }
16944         
16945         if (this.disabled) {
16946             input.disabled = true;
16947         }
16948         
16949         var inputblock = {
16950             cls : 'roo-combobox-wrap',
16951             cn : [
16952                 input
16953             ]
16954         };
16955         
16956         if(this.before){
16957             inputblock.cls += ' input-group';
16958             
16959             inputblock.cn.unshift({
16960                 tag :'span',
16961                 cls : 'input-group-addon input-group-prepend input-group-text',
16962                 html : this.before
16963             });
16964         }
16965         
16966         if(this.removable && !this.multiple){
16967             inputblock.cls += ' roo-removable';
16968             
16969             inputblock.cn.push({
16970                 tag: 'button',
16971                 html : 'x',
16972                 cls : 'roo-combo-removable-btn close'
16973             });
16974         }
16975
16976         if(this.hasFeedback && !this.allowBlank){
16977             
16978             inputblock.cls += ' has-feedback';
16979             
16980             inputblock.cn.push({
16981                 tag: 'span',
16982                 cls: 'glyphicon form-control-feedback'
16983             });
16984             
16985         }
16986         
16987         if (this.after) {
16988             
16989             inputblock.cls += (this.before) ? '' : ' input-group';
16990             
16991             inputblock.cn.push({
16992                 tag :'span',
16993                 cls : 'input-group-addon input-group-append input-group-text',
16994                 html : this.after
16995             });
16996         }
16997
16998         
16999         var ibwrap = inputblock;
17000         
17001         if(this.multiple){
17002             ibwrap = {
17003                 tag: 'ul',
17004                 cls: 'roo-select2-choices',
17005                 cn:[
17006                     {
17007                         tag: 'li',
17008                         cls: 'roo-select2-search-field',
17009                         cn: [
17010
17011                             inputblock
17012                         ]
17013                     }
17014                 ]
17015             };
17016         
17017             
17018         }
17019         
17020         var combobox = {
17021             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17022             cn: [
17023                 {
17024                     tag: 'input',
17025                     type : 'hidden',
17026                     cls: 'form-hidden-field'
17027                 },
17028                 ibwrap
17029             ]
17030         };
17031         
17032         if(!this.multiple && this.showToggleBtn){
17033             
17034             var caret = {
17035                 cls: 'caret'
17036             };
17037             
17038             if (this.caret != false) {
17039                 caret = {
17040                      tag: 'i',
17041                      cls: 'fa fa-' + this.caret
17042                 };
17043                 
17044             }
17045             
17046             combobox.cn.push({
17047                 tag :'span',
17048                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17049                 cn : [
17050                     Roo.bootstrap.version == 3 ? caret : '',
17051                     {
17052                         tag: 'span',
17053                         cls: 'combobox-clear',
17054                         cn  : [
17055                             {
17056                                 tag : 'i',
17057                                 cls: 'icon-remove'
17058                             }
17059                         ]
17060                     }
17061                 ]
17062
17063             })
17064         }
17065         
17066         if(this.multiple){
17067             combobox.cls += ' roo-select2-container-multi';
17068         }
17069         
17070         var align = this.labelAlign || this.parentLabelAlign();
17071         
17072         if (align ==='left' && this.fieldLabel.length) {
17073
17074             cfg.cn = [
17075                 {
17076                    tag : 'i',
17077                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17078                    tooltip : 'This field is required'
17079                 },
17080                 {
17081                     tag: 'label',
17082                     cls : 'control-label col-form-label',
17083                     html : this.fieldLabel
17084
17085                 },
17086                 {
17087                     cls : 'roo-combobox-wrap ', 
17088                     cn: [
17089                         combobox
17090                     ]
17091                 }
17092             ];
17093             
17094             var labelCfg = cfg.cn[1];
17095             var contentCfg = cfg.cn[2];
17096             
17097
17098             if(this.indicatorpos == 'right'){
17099                 cfg.cn = [
17100                     {
17101                         tag: 'label',
17102                         'for' :  id,
17103                         cls : 'control-label col-form-label',
17104                         cn : [
17105                             {
17106                                 tag : 'span',
17107                                 html : this.fieldLabel
17108                             },
17109                             {
17110                                 tag : 'i',
17111                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17112                                 tooltip : 'This field is required'
17113                             }
17114                         ]
17115                     },
17116                     {
17117                         cls : "roo-combobox-wrap ",
17118                         cn: [
17119                             combobox
17120                         ]
17121                     }
17122
17123                 ];
17124                 
17125                 labelCfg = cfg.cn[0];
17126                 contentCfg = cfg.cn[1];
17127             }
17128             
17129            
17130             
17131             if(this.labelWidth > 12){
17132                 labelCfg.style = "width: " + this.labelWidth + 'px';
17133             }
17134            
17135             if(this.labelWidth < 13 && this.labelmd == 0){
17136                 this.labelmd = this.labelWidth;
17137             }
17138             
17139             if(this.labellg > 0){
17140                 labelCfg.cls += ' col-lg-' + this.labellg;
17141                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17142             }
17143             
17144             if(this.labelmd > 0){
17145                 labelCfg.cls += ' col-md-' + this.labelmd;
17146                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17147             }
17148             
17149             if(this.labelsm > 0){
17150                 labelCfg.cls += ' col-sm-' + this.labelsm;
17151                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17152             }
17153             
17154             if(this.labelxs > 0){
17155                 labelCfg.cls += ' col-xs-' + this.labelxs;
17156                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17157             }
17158                 
17159                 
17160         } else if ( this.fieldLabel.length) {
17161             cfg.cn = [
17162                 {
17163                    tag : 'i',
17164                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17165                    tooltip : 'This field is required'
17166                 },
17167                 {
17168                     tag: 'label',
17169                     cls : 'control-label',
17170                     html : this.fieldLabel
17171
17172                 },
17173                 {
17174                     cls : '', 
17175                     cn: [
17176                         combobox
17177                     ]
17178                 }
17179             ];
17180             
17181             if(this.indicatorpos == 'right'){
17182                 cfg.cn = [
17183                     {
17184                         tag: 'label',
17185                         cls : 'control-label',
17186                         html : this.fieldLabel,
17187                         cn : [
17188                             {
17189                                tag : 'i',
17190                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17191                                tooltip : 'This field is required'
17192                             }
17193                         ]
17194                     },
17195                     {
17196                         cls : '', 
17197                         cn: [
17198                             combobox
17199                         ]
17200                     }
17201                 ];
17202             }
17203         } else {
17204             cfg.cn = combobox;    
17205         }
17206         
17207         
17208         var settings = this;
17209         
17210         ['xs','sm','md','lg'].map(function(size){
17211             if (settings[size]) {
17212                 cfg.cls += ' col-' + size + '-' + settings[size];
17213             }
17214         });
17215         
17216         return cfg;
17217     },
17218     
17219     initTouchView : function()
17220     {
17221         this.renderTouchView();
17222         
17223         this.touchViewEl.on('scroll', function(){
17224             this.el.dom.scrollTop = 0;
17225         }, this);
17226         
17227         this.originalValue = this.getValue();
17228         
17229         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17230         
17231         this.inputEl().on("click", this.showTouchView, this);
17232         if (this.triggerEl) {
17233             this.triggerEl.on("click", this.showTouchView, this);
17234         }
17235         
17236         
17237         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17238         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17239         
17240         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17241         
17242         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17243         this.store.on('load', this.onTouchViewLoad, this);
17244         this.store.on('loadexception', this.onTouchViewLoadException, this);
17245         
17246         if(this.hiddenName){
17247             
17248             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17249             
17250             this.hiddenField.dom.value =
17251                 this.hiddenValue !== undefined ? this.hiddenValue :
17252                 this.value !== undefined ? this.value : '';
17253         
17254             this.el.dom.removeAttribute('name');
17255             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17256         }
17257         
17258         if(this.multiple){
17259             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17260             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17261         }
17262         
17263         if(this.removable && !this.multiple){
17264             var close = this.closeTriggerEl();
17265             if(close){
17266                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17267                 close.on('click', this.removeBtnClick, this, close);
17268             }
17269         }
17270         /*
17271          * fix the bug in Safari iOS8
17272          */
17273         this.inputEl().on("focus", function(e){
17274             document.activeElement.blur();
17275         }, this);
17276         
17277         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17278         
17279         return;
17280         
17281         
17282     },
17283     
17284     renderTouchView : function()
17285     {
17286         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17287         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17288         
17289         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17290         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291         
17292         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17293         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17294         this.touchViewBodyEl.setStyle('overflow', 'auto');
17295         
17296         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17297         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17298         
17299         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17300         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17301         
17302     },
17303     
17304     showTouchView : function()
17305     {
17306         if(this.disabled){
17307             return;
17308         }
17309         
17310         this.touchViewHeaderEl.hide();
17311
17312         if(this.modalTitle.length){
17313             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17314             this.touchViewHeaderEl.show();
17315         }
17316
17317         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17318         this.touchViewEl.show();
17319
17320         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17321         
17322         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17323         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17324
17325         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17326
17327         if(this.modalTitle.length){
17328             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17329         }
17330         
17331         this.touchViewBodyEl.setHeight(bodyHeight);
17332
17333         if(this.animate){
17334             var _this = this;
17335             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17336         }else{
17337             this.touchViewEl.addClass(['in','show']);
17338         }
17339         
17340         if(this._touchViewMask){
17341             Roo.get(document.body).addClass("x-body-masked");
17342             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17343             this._touchViewMask.setStyle('z-index', 10000);
17344             this._touchViewMask.addClass('show');
17345         }
17346         
17347         this.doTouchViewQuery();
17348         
17349     },
17350     
17351     hideTouchView : function()
17352     {
17353         this.touchViewEl.removeClass(['in','show']);
17354
17355         if(this.animate){
17356             var _this = this;
17357             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17358         }else{
17359             this.touchViewEl.setStyle('display', 'none');
17360         }
17361         
17362         if(this._touchViewMask){
17363             this._touchViewMask.removeClass('show');
17364             Roo.get(document.body).removeClass("x-body-masked");
17365         }
17366     },
17367     
17368     setTouchViewValue : function()
17369     {
17370         if(this.multiple){
17371             this.clearItem();
17372         
17373             var _this = this;
17374
17375             Roo.each(this.tickItems, function(o){
17376                 this.addItem(o);
17377             }, this);
17378         }
17379         
17380         this.hideTouchView();
17381     },
17382     
17383     doTouchViewQuery : function()
17384     {
17385         var qe = {
17386             query: '',
17387             forceAll: true,
17388             combo: this,
17389             cancel:false
17390         };
17391         
17392         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17393             return false;
17394         }
17395         
17396         if(!this.alwaysQuery || this.mode == 'local'){
17397             this.onTouchViewLoad();
17398             return;
17399         }
17400         
17401         this.store.load();
17402     },
17403     
17404     onTouchViewBeforeLoad : function(combo,opts)
17405     {
17406         return;
17407     },
17408
17409     // private
17410     onTouchViewLoad : function()
17411     {
17412         if(this.store.getCount() < 1){
17413             this.onTouchViewEmptyResults();
17414             return;
17415         }
17416         
17417         this.clearTouchView();
17418         
17419         var rawValue = this.getRawValue();
17420         
17421         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17422         
17423         this.tickItems = [];
17424         
17425         this.store.data.each(function(d, rowIndex){
17426             var row = this.touchViewListGroup.createChild(template);
17427             
17428             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17429                 row.addClass(d.data.cls);
17430             }
17431             
17432             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17433                 var cfg = {
17434                     data : d.data,
17435                     html : d.data[this.displayField]
17436                 };
17437                 
17438                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17439                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17440                 }
17441             }
17442             row.removeClass('selected');
17443             if(!this.multiple && this.valueField &&
17444                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17445             {
17446                 // radio buttons..
17447                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17448                 row.addClass('selected');
17449             }
17450             
17451             if(this.multiple && this.valueField &&
17452                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17453             {
17454                 
17455                 // checkboxes...
17456                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17457                 this.tickItems.push(d.data);
17458             }
17459             
17460             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17461             
17462         }, this);
17463         
17464         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17465         
17466         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17467
17468         if(this.modalTitle.length){
17469             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17470         }
17471
17472         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17473         
17474         if(this.mobile_restrict_height && listHeight < bodyHeight){
17475             this.touchViewBodyEl.setHeight(listHeight);
17476         }
17477         
17478         var _this = this;
17479         
17480         if(firstChecked && listHeight > bodyHeight){
17481             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17482         }
17483         
17484     },
17485     
17486     onTouchViewLoadException : function()
17487     {
17488         this.hideTouchView();
17489     },
17490     
17491     onTouchViewEmptyResults : function()
17492     {
17493         this.clearTouchView();
17494         
17495         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17496         
17497         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17498         
17499     },
17500     
17501     clearTouchView : function()
17502     {
17503         this.touchViewListGroup.dom.innerHTML = '';
17504     },
17505     
17506     onTouchViewClick : function(e, el, o)
17507     {
17508         e.preventDefault();
17509         
17510         var row = o.row;
17511         var rowIndex = o.rowIndex;
17512         
17513         var r = this.store.getAt(rowIndex);
17514         
17515         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17516             
17517             if(!this.multiple){
17518                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17519                     c.dom.removeAttribute('checked');
17520                 }, this);
17521
17522                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17523
17524                 this.setFromData(r.data);
17525
17526                 var close = this.closeTriggerEl();
17527
17528                 if(close){
17529                     close.show();
17530                 }
17531
17532                 this.hideTouchView();
17533
17534                 this.fireEvent('select', this, r, rowIndex);
17535
17536                 return;
17537             }
17538
17539             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17540                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17541                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17542                 return;
17543             }
17544
17545             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17546             this.addItem(r.data);
17547             this.tickItems.push(r.data);
17548         }
17549     },
17550     
17551     getAutoCreateNativeIOS : function()
17552     {
17553         var cfg = {
17554             cls: 'form-group' //input-group,
17555         };
17556         
17557         var combobox =  {
17558             tag: 'select',
17559             cls : 'roo-ios-select'
17560         };
17561         
17562         if (this.name) {
17563             combobox.name = this.name;
17564         }
17565         
17566         if (this.disabled) {
17567             combobox.disabled = true;
17568         }
17569         
17570         var settings = this;
17571         
17572         ['xs','sm','md','lg'].map(function(size){
17573             if (settings[size]) {
17574                 cfg.cls += ' col-' + size + '-' + settings[size];
17575             }
17576         });
17577         
17578         cfg.cn = combobox;
17579         
17580         return cfg;
17581         
17582     },
17583     
17584     initIOSView : function()
17585     {
17586         this.store.on('load', this.onIOSViewLoad, this);
17587         
17588         return;
17589     },
17590     
17591     onIOSViewLoad : function()
17592     {
17593         if(this.store.getCount() < 1){
17594             return;
17595         }
17596         
17597         this.clearIOSView();
17598         
17599         if(this.allowBlank) {
17600             
17601             var default_text = '-- SELECT --';
17602             
17603             if(this.placeholder.length){
17604                 default_text = this.placeholder;
17605             }
17606             
17607             if(this.emptyTitle.length){
17608                 default_text += ' - ' + this.emptyTitle + ' -';
17609             }
17610             
17611             var opt = this.inputEl().createChild({
17612                 tag: 'option',
17613                 value : 0,
17614                 html : default_text
17615             });
17616             
17617             var o = {};
17618             o[this.valueField] = 0;
17619             o[this.displayField] = default_text;
17620             
17621             this.ios_options.push({
17622                 data : o,
17623                 el : opt
17624             });
17625             
17626         }
17627         
17628         this.store.data.each(function(d, rowIndex){
17629             
17630             var html = '';
17631             
17632             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17633                 html = d.data[this.displayField];
17634             }
17635             
17636             var value = '';
17637             
17638             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17639                 value = d.data[this.valueField];
17640             }
17641             
17642             var option = {
17643                 tag: 'option',
17644                 value : value,
17645                 html : html
17646             };
17647             
17648             if(this.value == d.data[this.valueField]){
17649                 option['selected'] = true;
17650             }
17651             
17652             var opt = this.inputEl().createChild(option);
17653             
17654             this.ios_options.push({
17655                 data : d.data,
17656                 el : opt
17657             });
17658             
17659         }, this);
17660         
17661         this.inputEl().on('change', function(){
17662            this.fireEvent('select', this);
17663         }, this);
17664         
17665     },
17666     
17667     clearIOSView: function()
17668     {
17669         this.inputEl().dom.innerHTML = '';
17670         
17671         this.ios_options = [];
17672     },
17673     
17674     setIOSValue: function(v)
17675     {
17676         this.value = v;
17677         
17678         if(!this.ios_options){
17679             return;
17680         }
17681         
17682         Roo.each(this.ios_options, function(opts){
17683            
17684            opts.el.dom.removeAttribute('selected');
17685            
17686            if(opts.data[this.valueField] != v){
17687                return;
17688            }
17689            
17690            opts.el.dom.setAttribute('selected', true);
17691            
17692         }, this);
17693     }
17694
17695     /** 
17696     * @cfg {Boolean} grow 
17697     * @hide 
17698     */
17699     /** 
17700     * @cfg {Number} growMin 
17701     * @hide 
17702     */
17703     /** 
17704     * @cfg {Number} growMax 
17705     * @hide 
17706     */
17707     /**
17708      * @hide
17709      * @method autoSize
17710      */
17711 });
17712
17713 Roo.apply(Roo.bootstrap.ComboBox,  {
17714     
17715     header : {
17716         tag: 'div',
17717         cls: 'modal-header',
17718         cn: [
17719             {
17720                 tag: 'h4',
17721                 cls: 'modal-title'
17722             }
17723         ]
17724     },
17725     
17726     body : {
17727         tag: 'div',
17728         cls: 'modal-body',
17729         cn: [
17730             {
17731                 tag: 'ul',
17732                 cls: 'list-group'
17733             }
17734         ]
17735     },
17736     
17737     listItemRadio : {
17738         tag: 'li',
17739         cls: 'list-group-item',
17740         cn: [
17741             {
17742                 tag: 'span',
17743                 cls: 'roo-combobox-list-group-item-value'
17744             },
17745             {
17746                 tag: 'div',
17747                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17748                 cn: [
17749                     {
17750                         tag: 'input',
17751                         type: 'radio'
17752                     },
17753                     {
17754                         tag: 'label'
17755                     }
17756                 ]
17757             }
17758         ]
17759     },
17760     
17761     listItemCheckbox : {
17762         tag: 'li',
17763         cls: 'list-group-item',
17764         cn: [
17765             {
17766                 tag: 'span',
17767                 cls: 'roo-combobox-list-group-item-value'
17768             },
17769             {
17770                 tag: 'div',
17771                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17772                 cn: [
17773                     {
17774                         tag: 'input',
17775                         type: 'checkbox'
17776                     },
17777                     {
17778                         tag: 'label'
17779                     }
17780                 ]
17781             }
17782         ]
17783     },
17784     
17785     emptyResult : {
17786         tag: 'div',
17787         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17788     },
17789     
17790     footer : {
17791         tag: 'div',
17792         cls: 'modal-footer',
17793         cn: [
17794             {
17795                 tag: 'div',
17796                 cls: 'row',
17797                 cn: [
17798                     {
17799                         tag: 'div',
17800                         cls: 'col-xs-6 text-left',
17801                         cn: {
17802                             tag: 'button',
17803                             cls: 'btn btn-danger roo-touch-view-cancel',
17804                             html: 'Cancel'
17805                         }
17806                     },
17807                     {
17808                         tag: 'div',
17809                         cls: 'col-xs-6 text-right',
17810                         cn: {
17811                             tag: 'button',
17812                             cls: 'btn btn-success roo-touch-view-ok',
17813                             html: 'OK'
17814                         }
17815                     }
17816                 ]
17817             }
17818         ]
17819         
17820     }
17821 });
17822
17823 Roo.apply(Roo.bootstrap.ComboBox,  {
17824     
17825     touchViewTemplate : {
17826         tag: 'div',
17827         cls: 'modal fade roo-combobox-touch-view',
17828         cn: [
17829             {
17830                 tag: 'div',
17831                 cls: 'modal-dialog',
17832                 style : 'position:fixed', // we have to fix position....
17833                 cn: [
17834                     {
17835                         tag: 'div',
17836                         cls: 'modal-content',
17837                         cn: [
17838                             Roo.bootstrap.ComboBox.header,
17839                             Roo.bootstrap.ComboBox.body,
17840                             Roo.bootstrap.ComboBox.footer
17841                         ]
17842                     }
17843                 ]
17844             }
17845         ]
17846     }
17847 });/*
17848  * Based on:
17849  * Ext JS Library 1.1.1
17850  * Copyright(c) 2006-2007, Ext JS, LLC.
17851  *
17852  * Originally Released Under LGPL - original licence link has changed is not relivant.
17853  *
17854  * Fork - LGPL
17855  * <script type="text/javascript">
17856  */
17857
17858 /**
17859  * @class Roo.View
17860  * @extends Roo.util.Observable
17861  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17862  * This class also supports single and multi selection modes. <br>
17863  * Create a data model bound view:
17864  <pre><code>
17865  var store = new Roo.data.Store(...);
17866
17867  var view = new Roo.View({
17868     el : "my-element",
17869     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17870  
17871     singleSelect: true,
17872     selectedClass: "ydataview-selected",
17873     store: store
17874  });
17875
17876  // listen for node click?
17877  view.on("click", function(vw, index, node, e){
17878  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17879  });
17880
17881  // load XML data
17882  dataModel.load("foobar.xml");
17883  </code></pre>
17884  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17885  * <br><br>
17886  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17887  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17888  * 
17889  * Note: old style constructor is still suported (container, template, config)
17890  * 
17891  * @constructor
17892  * Create a new View
17893  * @param {Object} config The config object
17894  * 
17895  */
17896 Roo.View = function(config, depreciated_tpl, depreciated_config){
17897     
17898     this.parent = false;
17899     
17900     if (typeof(depreciated_tpl) == 'undefined') {
17901         // new way.. - universal constructor.
17902         Roo.apply(this, config);
17903         this.el  = Roo.get(this.el);
17904     } else {
17905         // old format..
17906         this.el  = Roo.get(config);
17907         this.tpl = depreciated_tpl;
17908         Roo.apply(this, depreciated_config);
17909     }
17910     this.wrapEl  = this.el.wrap().wrap();
17911     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17912     
17913     
17914     if(typeof(this.tpl) == "string"){
17915         this.tpl = new Roo.Template(this.tpl);
17916     } else {
17917         // support xtype ctors..
17918         this.tpl = new Roo.factory(this.tpl, Roo);
17919     }
17920     
17921     
17922     this.tpl.compile();
17923     
17924     /** @private */
17925     this.addEvents({
17926         /**
17927          * @event beforeclick
17928          * Fires before a click is processed. Returns false to cancel the default action.
17929          * @param {Roo.View} this
17930          * @param {Number} index The index of the target node
17931          * @param {HTMLElement} node The target node
17932          * @param {Roo.EventObject} e The raw event object
17933          */
17934             "beforeclick" : true,
17935         /**
17936          * @event click
17937          * Fires when a template node is clicked.
17938          * @param {Roo.View} this
17939          * @param {Number} index The index of the target node
17940          * @param {HTMLElement} node The target node
17941          * @param {Roo.EventObject} e The raw event object
17942          */
17943             "click" : true,
17944         /**
17945          * @event dblclick
17946          * Fires when a template node is double clicked.
17947          * @param {Roo.View} this
17948          * @param {Number} index The index of the target node
17949          * @param {HTMLElement} node The target node
17950          * @param {Roo.EventObject} e The raw event object
17951          */
17952             "dblclick" : true,
17953         /**
17954          * @event contextmenu
17955          * Fires when a template node is right clicked.
17956          * @param {Roo.View} this
17957          * @param {Number} index The index of the target node
17958          * @param {HTMLElement} node The target node
17959          * @param {Roo.EventObject} e The raw event object
17960          */
17961             "contextmenu" : true,
17962         /**
17963          * @event selectionchange
17964          * Fires when the selected nodes change.
17965          * @param {Roo.View} this
17966          * @param {Array} selections Array of the selected nodes
17967          */
17968             "selectionchange" : true,
17969     
17970         /**
17971          * @event beforeselect
17972          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17973          * @param {Roo.View} this
17974          * @param {HTMLElement} node The node to be selected
17975          * @param {Array} selections Array of currently selected nodes
17976          */
17977             "beforeselect" : true,
17978         /**
17979          * @event preparedata
17980          * Fires on every row to render, to allow you to change the data.
17981          * @param {Roo.View} this
17982          * @param {Object} data to be rendered (change this)
17983          */
17984           "preparedata" : true
17985           
17986           
17987         });
17988
17989
17990
17991     this.el.on({
17992         "click": this.onClick,
17993         "dblclick": this.onDblClick,
17994         "contextmenu": this.onContextMenu,
17995         scope:this
17996     });
17997
17998     this.selections = [];
17999     this.nodes = [];
18000     this.cmp = new Roo.CompositeElementLite([]);
18001     if(this.store){
18002         this.store = Roo.factory(this.store, Roo.data);
18003         this.setStore(this.store, true);
18004     }
18005     
18006     if ( this.footer && this.footer.xtype) {
18007            
18008          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18009         
18010         this.footer.dataSource = this.store;
18011         this.footer.container = fctr;
18012         this.footer = Roo.factory(this.footer, Roo);
18013         fctr.insertFirst(this.el);
18014         
18015         // this is a bit insane - as the paging toolbar seems to detach the el..
18016 //        dom.parentNode.parentNode.parentNode
18017          // they get detached?
18018     }
18019     
18020     
18021     Roo.View.superclass.constructor.call(this);
18022     
18023     
18024 };
18025
18026 Roo.extend(Roo.View, Roo.util.Observable, {
18027     
18028      /**
18029      * @cfg {Roo.data.Store} store Data store to load data from.
18030      */
18031     store : false,
18032     
18033     /**
18034      * @cfg {String|Roo.Element} el The container element.
18035      */
18036     el : '',
18037     
18038     /**
18039      * @cfg {String|Roo.Template} tpl The template used by this View 
18040      */
18041     tpl : false,
18042     /**
18043      * @cfg {String} dataName the named area of the template to use as the data area
18044      *                          Works with domtemplates roo-name="name"
18045      */
18046     dataName: false,
18047     /**
18048      * @cfg {String} selectedClass The css class to add to selected nodes
18049      */
18050     selectedClass : "x-view-selected",
18051      /**
18052      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18053      */
18054     emptyText : "",
18055     
18056     /**
18057      * @cfg {String} text to display on mask (default Loading)
18058      */
18059     mask : false,
18060     /**
18061      * @cfg {Boolean} multiSelect Allow multiple selection
18062      */
18063     multiSelect : false,
18064     /**
18065      * @cfg {Boolean} singleSelect Allow single selection
18066      */
18067     singleSelect:  false,
18068     
18069     /**
18070      * @cfg {Boolean} toggleSelect - selecting 
18071      */
18072     toggleSelect : false,
18073     
18074     /**
18075      * @cfg {Boolean} tickable - selecting 
18076      */
18077     tickable : false,
18078     
18079     /**
18080      * Returns the element this view is bound to.
18081      * @return {Roo.Element}
18082      */
18083     getEl : function(){
18084         return this.wrapEl;
18085     },
18086     
18087     
18088
18089     /**
18090      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18091      */
18092     refresh : function(){
18093         //Roo.log('refresh');
18094         var t = this.tpl;
18095         
18096         // if we are using something like 'domtemplate', then
18097         // the what gets used is:
18098         // t.applySubtemplate(NAME, data, wrapping data..)
18099         // the outer template then get' applied with
18100         //     the store 'extra data'
18101         // and the body get's added to the
18102         //      roo-name="data" node?
18103         //      <span class='roo-tpl-{name}'></span> ?????
18104         
18105         
18106         
18107         this.clearSelections();
18108         this.el.update("");
18109         var html = [];
18110         var records = this.store.getRange();
18111         if(records.length < 1) {
18112             
18113             // is this valid??  = should it render a template??
18114             
18115             this.el.update(this.emptyText);
18116             return;
18117         }
18118         var el = this.el;
18119         if (this.dataName) {
18120             this.el.update(t.apply(this.store.meta)); //????
18121             el = this.el.child('.roo-tpl-' + this.dataName);
18122         }
18123         
18124         for(var i = 0, len = records.length; i < len; i++){
18125             var data = this.prepareData(records[i].data, i, records[i]);
18126             this.fireEvent("preparedata", this, data, i, records[i]);
18127             
18128             var d = Roo.apply({}, data);
18129             
18130             if(this.tickable){
18131                 Roo.apply(d, {'roo-id' : Roo.id()});
18132                 
18133                 var _this = this;
18134             
18135                 Roo.each(this.parent.item, function(item){
18136                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18137                         return;
18138                     }
18139                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18140                 });
18141             }
18142             
18143             html[html.length] = Roo.util.Format.trim(
18144                 this.dataName ?
18145                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18146                     t.apply(d)
18147             );
18148         }
18149         
18150         
18151         
18152         el.update(html.join(""));
18153         this.nodes = el.dom.childNodes;
18154         this.updateIndexes(0);
18155     },
18156     
18157
18158     /**
18159      * Function to override to reformat the data that is sent to
18160      * the template for each node.
18161      * DEPRICATED - use the preparedata event handler.
18162      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18163      * a JSON object for an UpdateManager bound view).
18164      */
18165     prepareData : function(data, index, record)
18166     {
18167         this.fireEvent("preparedata", this, data, index, record);
18168         return data;
18169     },
18170
18171     onUpdate : function(ds, record){
18172         // Roo.log('on update');   
18173         this.clearSelections();
18174         var index = this.store.indexOf(record);
18175         var n = this.nodes[index];
18176         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18177         n.parentNode.removeChild(n);
18178         this.updateIndexes(index, index);
18179     },
18180
18181     
18182     
18183 // --------- FIXME     
18184     onAdd : function(ds, records, index)
18185     {
18186         //Roo.log(['on Add', ds, records, index] );        
18187         this.clearSelections();
18188         if(this.nodes.length == 0){
18189             this.refresh();
18190             return;
18191         }
18192         var n = this.nodes[index];
18193         for(var i = 0, len = records.length; i < len; i++){
18194             var d = this.prepareData(records[i].data, i, records[i]);
18195             if(n){
18196                 this.tpl.insertBefore(n, d);
18197             }else{
18198                 
18199                 this.tpl.append(this.el, d);
18200             }
18201         }
18202         this.updateIndexes(index);
18203     },
18204
18205     onRemove : function(ds, record, index){
18206        // Roo.log('onRemove');
18207         this.clearSelections();
18208         var el = this.dataName  ?
18209             this.el.child('.roo-tpl-' + this.dataName) :
18210             this.el; 
18211         
18212         el.dom.removeChild(this.nodes[index]);
18213         this.updateIndexes(index);
18214     },
18215
18216     /**
18217      * Refresh an individual node.
18218      * @param {Number} index
18219      */
18220     refreshNode : function(index){
18221         this.onUpdate(this.store, this.store.getAt(index));
18222     },
18223
18224     updateIndexes : function(startIndex, endIndex){
18225         var ns = this.nodes;
18226         startIndex = startIndex || 0;
18227         endIndex = endIndex || ns.length - 1;
18228         for(var i = startIndex; i <= endIndex; i++){
18229             ns[i].nodeIndex = i;
18230         }
18231     },
18232
18233     /**
18234      * Changes the data store this view uses and refresh the view.
18235      * @param {Store} store
18236      */
18237     setStore : function(store, initial){
18238         if(!initial && this.store){
18239             this.store.un("datachanged", this.refresh);
18240             this.store.un("add", this.onAdd);
18241             this.store.un("remove", this.onRemove);
18242             this.store.un("update", this.onUpdate);
18243             this.store.un("clear", this.refresh);
18244             this.store.un("beforeload", this.onBeforeLoad);
18245             this.store.un("load", this.onLoad);
18246             this.store.un("loadexception", this.onLoad);
18247         }
18248         if(store){
18249           
18250             store.on("datachanged", this.refresh, this);
18251             store.on("add", this.onAdd, this);
18252             store.on("remove", this.onRemove, this);
18253             store.on("update", this.onUpdate, this);
18254             store.on("clear", this.refresh, this);
18255             store.on("beforeload", this.onBeforeLoad, this);
18256             store.on("load", this.onLoad, this);
18257             store.on("loadexception", this.onLoad, this);
18258         }
18259         
18260         if(store){
18261             this.refresh();
18262         }
18263     },
18264     /**
18265      * onbeforeLoad - masks the loading area.
18266      *
18267      */
18268     onBeforeLoad : function(store,opts)
18269     {
18270          //Roo.log('onBeforeLoad');   
18271         if (!opts.add) {
18272             this.el.update("");
18273         }
18274         this.el.mask(this.mask ? this.mask : "Loading" ); 
18275     },
18276     onLoad : function ()
18277     {
18278         this.el.unmask();
18279     },
18280     
18281
18282     /**
18283      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18284      * @param {HTMLElement} node
18285      * @return {HTMLElement} The template node
18286      */
18287     findItemFromChild : function(node){
18288         var el = this.dataName  ?
18289             this.el.child('.roo-tpl-' + this.dataName,true) :
18290             this.el.dom; 
18291         
18292         if(!node || node.parentNode == el){
18293                     return node;
18294             }
18295             var p = node.parentNode;
18296             while(p && p != el){
18297             if(p.parentNode == el){
18298                 return p;
18299             }
18300             p = p.parentNode;
18301         }
18302             return null;
18303     },
18304
18305     /** @ignore */
18306     onClick : function(e){
18307         var item = this.findItemFromChild(e.getTarget());
18308         if(item){
18309             var index = this.indexOf(item);
18310             if(this.onItemClick(item, index, e) !== false){
18311                 this.fireEvent("click", this, index, item, e);
18312             }
18313         }else{
18314             this.clearSelections();
18315         }
18316     },
18317
18318     /** @ignore */
18319     onContextMenu : function(e){
18320         var item = this.findItemFromChild(e.getTarget());
18321         if(item){
18322             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18323         }
18324     },
18325
18326     /** @ignore */
18327     onDblClick : function(e){
18328         var item = this.findItemFromChild(e.getTarget());
18329         if(item){
18330             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18331         }
18332     },
18333
18334     onItemClick : function(item, index, e)
18335     {
18336         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18337             return false;
18338         }
18339         if (this.toggleSelect) {
18340             var m = this.isSelected(item) ? 'unselect' : 'select';
18341             //Roo.log(m);
18342             var _t = this;
18343             _t[m](item, true, false);
18344             return true;
18345         }
18346         if(this.multiSelect || this.singleSelect){
18347             if(this.multiSelect && e.shiftKey && this.lastSelection){
18348                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18349             }else{
18350                 this.select(item, this.multiSelect && e.ctrlKey);
18351                 this.lastSelection = item;
18352             }
18353             
18354             if(!this.tickable){
18355                 e.preventDefault();
18356             }
18357             
18358         }
18359         return true;
18360     },
18361
18362     /**
18363      * Get the number of selected nodes.
18364      * @return {Number}
18365      */
18366     getSelectionCount : function(){
18367         return this.selections.length;
18368     },
18369
18370     /**
18371      * Get the currently selected nodes.
18372      * @return {Array} An array of HTMLElements
18373      */
18374     getSelectedNodes : function(){
18375         return this.selections;
18376     },
18377
18378     /**
18379      * Get the indexes of the selected nodes.
18380      * @return {Array}
18381      */
18382     getSelectedIndexes : function(){
18383         var indexes = [], s = this.selections;
18384         for(var i = 0, len = s.length; i < len; i++){
18385             indexes.push(s[i].nodeIndex);
18386         }
18387         return indexes;
18388     },
18389
18390     /**
18391      * Clear all selections
18392      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18393      */
18394     clearSelections : function(suppressEvent){
18395         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18396             this.cmp.elements = this.selections;
18397             this.cmp.removeClass(this.selectedClass);
18398             this.selections = [];
18399             if(!suppressEvent){
18400                 this.fireEvent("selectionchange", this, this.selections);
18401             }
18402         }
18403     },
18404
18405     /**
18406      * Returns true if the passed node is selected
18407      * @param {HTMLElement/Number} node The node or node index
18408      * @return {Boolean}
18409      */
18410     isSelected : function(node){
18411         var s = this.selections;
18412         if(s.length < 1){
18413             return false;
18414         }
18415         node = this.getNode(node);
18416         return s.indexOf(node) !== -1;
18417     },
18418
18419     /**
18420      * Selects nodes.
18421      * @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
18422      * @param {Boolean} keepExisting (optional) true to keep existing selections
18423      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18424      */
18425     select : function(nodeInfo, keepExisting, suppressEvent){
18426         if(nodeInfo instanceof Array){
18427             if(!keepExisting){
18428                 this.clearSelections(true);
18429             }
18430             for(var i = 0, len = nodeInfo.length; i < len; i++){
18431                 this.select(nodeInfo[i], true, true);
18432             }
18433             return;
18434         } 
18435         var node = this.getNode(nodeInfo);
18436         if(!node || this.isSelected(node)){
18437             return; // already selected.
18438         }
18439         if(!keepExisting){
18440             this.clearSelections(true);
18441         }
18442         
18443         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18444             Roo.fly(node).addClass(this.selectedClass);
18445             this.selections.push(node);
18446             if(!suppressEvent){
18447                 this.fireEvent("selectionchange", this, this.selections);
18448             }
18449         }
18450         
18451         
18452     },
18453       /**
18454      * Unselects nodes.
18455      * @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
18456      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18457      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18458      */
18459     unselect : function(nodeInfo, keepExisting, suppressEvent)
18460     {
18461         if(nodeInfo instanceof Array){
18462             Roo.each(this.selections, function(s) {
18463                 this.unselect(s, nodeInfo);
18464             }, this);
18465             return;
18466         }
18467         var node = this.getNode(nodeInfo);
18468         if(!node || !this.isSelected(node)){
18469             //Roo.log("not selected");
18470             return; // not selected.
18471         }
18472         // fireevent???
18473         var ns = [];
18474         Roo.each(this.selections, function(s) {
18475             if (s == node ) {
18476                 Roo.fly(node).removeClass(this.selectedClass);
18477
18478                 return;
18479             }
18480             ns.push(s);
18481         },this);
18482         
18483         this.selections= ns;
18484         this.fireEvent("selectionchange", this, this.selections);
18485     },
18486
18487     /**
18488      * Gets a template node.
18489      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18490      * @return {HTMLElement} The node or null if it wasn't found
18491      */
18492     getNode : function(nodeInfo){
18493         if(typeof nodeInfo == "string"){
18494             return document.getElementById(nodeInfo);
18495         }else if(typeof nodeInfo == "number"){
18496             return this.nodes[nodeInfo];
18497         }
18498         return nodeInfo;
18499     },
18500
18501     /**
18502      * Gets a range template nodes.
18503      * @param {Number} startIndex
18504      * @param {Number} endIndex
18505      * @return {Array} An array of nodes
18506      */
18507     getNodes : function(start, end){
18508         var ns = this.nodes;
18509         start = start || 0;
18510         end = typeof end == "undefined" ? ns.length - 1 : end;
18511         var nodes = [];
18512         if(start <= end){
18513             for(var i = start; i <= end; i++){
18514                 nodes.push(ns[i]);
18515             }
18516         } else{
18517             for(var i = start; i >= end; i--){
18518                 nodes.push(ns[i]);
18519             }
18520         }
18521         return nodes;
18522     },
18523
18524     /**
18525      * Finds the index of the passed node
18526      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18527      * @return {Number} The index of the node or -1
18528      */
18529     indexOf : function(node){
18530         node = this.getNode(node);
18531         if(typeof node.nodeIndex == "number"){
18532             return node.nodeIndex;
18533         }
18534         var ns = this.nodes;
18535         for(var i = 0, len = ns.length; i < len; i++){
18536             if(ns[i] == node){
18537                 return i;
18538             }
18539         }
18540         return -1;
18541     }
18542 });
18543 /*
18544  * - LGPL
18545  *
18546  * based on jquery fullcalendar
18547  * 
18548  */
18549
18550 Roo.bootstrap = Roo.bootstrap || {};
18551 /**
18552  * @class Roo.bootstrap.Calendar
18553  * @extends Roo.bootstrap.Component
18554  * Bootstrap Calendar class
18555  * @cfg {Boolean} loadMask (true|false) default false
18556  * @cfg {Object} header generate the user specific header of the calendar, default false
18557
18558  * @constructor
18559  * Create a new Container
18560  * @param {Object} config The config object
18561  */
18562
18563
18564
18565 Roo.bootstrap.Calendar = function(config){
18566     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18567      this.addEvents({
18568         /**
18569              * @event select
18570              * Fires when a date is selected
18571              * @param {DatePicker} this
18572              * @param {Date} date The selected date
18573              */
18574         'select': true,
18575         /**
18576              * @event monthchange
18577              * Fires when the displayed month changes 
18578              * @param {DatePicker} this
18579              * @param {Date} date The selected month
18580              */
18581         'monthchange': true,
18582         /**
18583              * @event evententer
18584              * Fires when mouse over an event
18585              * @param {Calendar} this
18586              * @param {event} Event
18587              */
18588         'evententer': true,
18589         /**
18590              * @event eventleave
18591              * Fires when the mouse leaves an
18592              * @param {Calendar} this
18593              * @param {event}
18594              */
18595         'eventleave': true,
18596         /**
18597              * @event eventclick
18598              * Fires when the mouse click an
18599              * @param {Calendar} this
18600              * @param {event}
18601              */
18602         'eventclick': true
18603         
18604     });
18605
18606 };
18607
18608 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18609     
18610      /**
18611      * @cfg {Number} startDay
18612      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18613      */
18614     startDay : 0,
18615     
18616     loadMask : false,
18617     
18618     header : false,
18619       
18620     getAutoCreate : function(){
18621         
18622         
18623         var fc_button = function(name, corner, style, content ) {
18624             return Roo.apply({},{
18625                 tag : 'span',
18626                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18627                          (corner.length ?
18628                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18629                             ''
18630                         ),
18631                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18632                 unselectable: 'on'
18633             });
18634         };
18635         
18636         var header = {};
18637         
18638         if(!this.header){
18639             header = {
18640                 tag : 'table',
18641                 cls : 'fc-header',
18642                 style : 'width:100%',
18643                 cn : [
18644                     {
18645                         tag: 'tr',
18646                         cn : [
18647                             {
18648                                 tag : 'td',
18649                                 cls : 'fc-header-left',
18650                                 cn : [
18651                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18652                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18653                                     { tag: 'span', cls: 'fc-header-space' },
18654                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18655
18656
18657                                 ]
18658                             },
18659
18660                             {
18661                                 tag : 'td',
18662                                 cls : 'fc-header-center',
18663                                 cn : [
18664                                     {
18665                                         tag: 'span',
18666                                         cls: 'fc-header-title',
18667                                         cn : {
18668                                             tag: 'H2',
18669                                             html : 'month / year'
18670                                         }
18671                                     }
18672
18673                                 ]
18674                             },
18675                             {
18676                                 tag : 'td',
18677                                 cls : 'fc-header-right',
18678                                 cn : [
18679                               /*      fc_button('month', 'left', '', 'month' ),
18680                                     fc_button('week', '', '', 'week' ),
18681                                     fc_button('day', 'right', '', 'day' )
18682                                 */    
18683
18684                                 ]
18685                             }
18686
18687                         ]
18688                     }
18689                 ]
18690             };
18691         }
18692         
18693         header = this.header;
18694         
18695        
18696         var cal_heads = function() {
18697             var ret = [];
18698             // fixme - handle this.
18699             
18700             for (var i =0; i < Date.dayNames.length; i++) {
18701                 var d = Date.dayNames[i];
18702                 ret.push({
18703                     tag: 'th',
18704                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18705                     html : d.substring(0,3)
18706                 });
18707                 
18708             }
18709             ret[0].cls += ' fc-first';
18710             ret[6].cls += ' fc-last';
18711             return ret;
18712         };
18713         var cal_cell = function(n) {
18714             return  {
18715                 tag: 'td',
18716                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18717                 cn : [
18718                     {
18719                         cn : [
18720                             {
18721                                 cls: 'fc-day-number',
18722                                 html: 'D'
18723                             },
18724                             {
18725                                 cls: 'fc-day-content',
18726                              
18727                                 cn : [
18728                                      {
18729                                         style: 'position: relative;' // height: 17px;
18730                                     }
18731                                 ]
18732                             }
18733                             
18734                             
18735                         ]
18736                     }
18737                 ]
18738                 
18739             }
18740         };
18741         var cal_rows = function() {
18742             
18743             var ret = [];
18744             for (var r = 0; r < 6; r++) {
18745                 var row= {
18746                     tag : 'tr',
18747                     cls : 'fc-week',
18748                     cn : []
18749                 };
18750                 
18751                 for (var i =0; i < Date.dayNames.length; i++) {
18752                     var d = Date.dayNames[i];
18753                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18754
18755                 }
18756                 row.cn[0].cls+=' fc-first';
18757                 row.cn[0].cn[0].style = 'min-height:90px';
18758                 row.cn[6].cls+=' fc-last';
18759                 ret.push(row);
18760                 
18761             }
18762             ret[0].cls += ' fc-first';
18763             ret[4].cls += ' fc-prev-last';
18764             ret[5].cls += ' fc-last';
18765             return ret;
18766             
18767         };
18768         
18769         var cal_table = {
18770             tag: 'table',
18771             cls: 'fc-border-separate',
18772             style : 'width:100%',
18773             cellspacing  : 0,
18774             cn : [
18775                 { 
18776                     tag: 'thead',
18777                     cn : [
18778                         { 
18779                             tag: 'tr',
18780                             cls : 'fc-first fc-last',
18781                             cn : cal_heads()
18782                         }
18783                     ]
18784                 },
18785                 { 
18786                     tag: 'tbody',
18787                     cn : cal_rows()
18788                 }
18789                   
18790             ]
18791         };
18792          
18793          var cfg = {
18794             cls : 'fc fc-ltr',
18795             cn : [
18796                 header,
18797                 {
18798                     cls : 'fc-content',
18799                     style : "position: relative;",
18800                     cn : [
18801                         {
18802                             cls : 'fc-view fc-view-month fc-grid',
18803                             style : 'position: relative',
18804                             unselectable : 'on',
18805                             cn : [
18806                                 {
18807                                     cls : 'fc-event-container',
18808                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18809                                 },
18810                                 cal_table
18811                             ]
18812                         }
18813                     ]
18814     
18815                 }
18816            ] 
18817             
18818         };
18819         
18820          
18821         
18822         return cfg;
18823     },
18824     
18825     
18826     initEvents : function()
18827     {
18828         if(!this.store){
18829             throw "can not find store for calendar";
18830         }
18831         
18832         var mark = {
18833             tag: "div",
18834             cls:"x-dlg-mask",
18835             style: "text-align:center",
18836             cn: [
18837                 {
18838                     tag: "div",
18839                     style: "background-color:white;width:50%;margin:250 auto",
18840                     cn: [
18841                         {
18842                             tag: "img",
18843                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18844                         },
18845                         {
18846                             tag: "span",
18847                             html: "Loading"
18848                         }
18849                         
18850                     ]
18851                 }
18852             ]
18853         };
18854         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18855         
18856         var size = this.el.select('.fc-content', true).first().getSize();
18857         this.maskEl.setSize(size.width, size.height);
18858         this.maskEl.enableDisplayMode("block");
18859         if(!this.loadMask){
18860             this.maskEl.hide();
18861         }
18862         
18863         this.store = Roo.factory(this.store, Roo.data);
18864         this.store.on('load', this.onLoad, this);
18865         this.store.on('beforeload', this.onBeforeLoad, this);
18866         
18867         this.resize();
18868         
18869         this.cells = this.el.select('.fc-day',true);
18870         //Roo.log(this.cells);
18871         this.textNodes = this.el.query('.fc-day-number');
18872         this.cells.addClassOnOver('fc-state-hover');
18873         
18874         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18875         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18876         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18877         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18878         
18879         this.on('monthchange', this.onMonthChange, this);
18880         
18881         this.update(new Date().clearTime());
18882     },
18883     
18884     resize : function() {
18885         var sz  = this.el.getSize();
18886         
18887         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18888         this.el.select('.fc-day-content div',true).setHeight(34);
18889     },
18890     
18891     
18892     // private
18893     showPrevMonth : function(e){
18894         this.update(this.activeDate.add("mo", -1));
18895     },
18896     showToday : function(e){
18897         this.update(new Date().clearTime());
18898     },
18899     // private
18900     showNextMonth : function(e){
18901         this.update(this.activeDate.add("mo", 1));
18902     },
18903
18904     // private
18905     showPrevYear : function(){
18906         this.update(this.activeDate.add("y", -1));
18907     },
18908
18909     // private
18910     showNextYear : function(){
18911         this.update(this.activeDate.add("y", 1));
18912     },
18913
18914     
18915    // private
18916     update : function(date)
18917     {
18918         var vd = this.activeDate;
18919         this.activeDate = date;
18920 //        if(vd && this.el){
18921 //            var t = date.getTime();
18922 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18923 //                Roo.log('using add remove');
18924 //                
18925 //                this.fireEvent('monthchange', this, date);
18926 //                
18927 //                this.cells.removeClass("fc-state-highlight");
18928 //                this.cells.each(function(c){
18929 //                   if(c.dateValue == t){
18930 //                       c.addClass("fc-state-highlight");
18931 //                       setTimeout(function(){
18932 //                            try{c.dom.firstChild.focus();}catch(e){}
18933 //                       }, 50);
18934 //                       return false;
18935 //                   }
18936 //                   return true;
18937 //                });
18938 //                return;
18939 //            }
18940 //        }
18941         
18942         var days = date.getDaysInMonth();
18943         
18944         var firstOfMonth = date.getFirstDateOfMonth();
18945         var startingPos = firstOfMonth.getDay()-this.startDay;
18946         
18947         if(startingPos < this.startDay){
18948             startingPos += 7;
18949         }
18950         
18951         var pm = date.add(Date.MONTH, -1);
18952         var prevStart = pm.getDaysInMonth()-startingPos;
18953 //        
18954         this.cells = this.el.select('.fc-day',true);
18955         this.textNodes = this.el.query('.fc-day-number');
18956         this.cells.addClassOnOver('fc-state-hover');
18957         
18958         var cells = this.cells.elements;
18959         var textEls = this.textNodes;
18960         
18961         Roo.each(cells, function(cell){
18962             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18963         });
18964         
18965         days += startingPos;
18966
18967         // convert everything to numbers so it's fast
18968         var day = 86400000;
18969         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18970         //Roo.log(d);
18971         //Roo.log(pm);
18972         //Roo.log(prevStart);
18973         
18974         var today = new Date().clearTime().getTime();
18975         var sel = date.clearTime().getTime();
18976         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18977         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18978         var ddMatch = this.disabledDatesRE;
18979         var ddText = this.disabledDatesText;
18980         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18981         var ddaysText = this.disabledDaysText;
18982         var format = this.format;
18983         
18984         var setCellClass = function(cal, cell){
18985             cell.row = 0;
18986             cell.events = [];
18987             cell.more = [];
18988             //Roo.log('set Cell Class');
18989             cell.title = "";
18990             var t = d.getTime();
18991             
18992             //Roo.log(d);
18993             
18994             cell.dateValue = t;
18995             if(t == today){
18996                 cell.className += " fc-today";
18997                 cell.className += " fc-state-highlight";
18998                 cell.title = cal.todayText;
18999             }
19000             if(t == sel){
19001                 // disable highlight in other month..
19002                 //cell.className += " fc-state-highlight";
19003                 
19004             }
19005             // disabling
19006             if(t < min) {
19007                 cell.className = " fc-state-disabled";
19008                 cell.title = cal.minText;
19009                 return;
19010             }
19011             if(t > max) {
19012                 cell.className = " fc-state-disabled";
19013                 cell.title = cal.maxText;
19014                 return;
19015             }
19016             if(ddays){
19017                 if(ddays.indexOf(d.getDay()) != -1){
19018                     cell.title = ddaysText;
19019                     cell.className = " fc-state-disabled";
19020                 }
19021             }
19022             if(ddMatch && format){
19023                 var fvalue = d.dateFormat(format);
19024                 if(ddMatch.test(fvalue)){
19025                     cell.title = ddText.replace("%0", fvalue);
19026                     cell.className = " fc-state-disabled";
19027                 }
19028             }
19029             
19030             if (!cell.initialClassName) {
19031                 cell.initialClassName = cell.dom.className;
19032             }
19033             
19034             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19035         };
19036
19037         var i = 0;
19038         
19039         for(; i < startingPos; i++) {
19040             textEls[i].innerHTML = (++prevStart);
19041             d.setDate(d.getDate()+1);
19042             
19043             cells[i].className = "fc-past fc-other-month";
19044             setCellClass(this, cells[i]);
19045         }
19046         
19047         var intDay = 0;
19048         
19049         for(; i < days; i++){
19050             intDay = i - startingPos + 1;
19051             textEls[i].innerHTML = (intDay);
19052             d.setDate(d.getDate()+1);
19053             
19054             cells[i].className = ''; // "x-date-active";
19055             setCellClass(this, cells[i]);
19056         }
19057         var extraDays = 0;
19058         
19059         for(; i < 42; i++) {
19060             textEls[i].innerHTML = (++extraDays);
19061             d.setDate(d.getDate()+1);
19062             
19063             cells[i].className = "fc-future fc-other-month";
19064             setCellClass(this, cells[i]);
19065         }
19066         
19067         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19068         
19069         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19070         
19071         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19072         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19073         
19074         if(totalRows != 6){
19075             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19076             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19077         }
19078         
19079         this.fireEvent('monthchange', this, date);
19080         
19081         
19082         /*
19083         if(!this.internalRender){
19084             var main = this.el.dom.firstChild;
19085             var w = main.offsetWidth;
19086             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19087             Roo.fly(main).setWidth(w);
19088             this.internalRender = true;
19089             // opera does not respect the auto grow header center column
19090             // then, after it gets a width opera refuses to recalculate
19091             // without a second pass
19092             if(Roo.isOpera && !this.secondPass){
19093                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19094                 this.secondPass = true;
19095                 this.update.defer(10, this, [date]);
19096             }
19097         }
19098         */
19099         
19100     },
19101     
19102     findCell : function(dt) {
19103         dt = dt.clearTime().getTime();
19104         var ret = false;
19105         this.cells.each(function(c){
19106             //Roo.log("check " +c.dateValue + '?=' + dt);
19107             if(c.dateValue == dt){
19108                 ret = c;
19109                 return false;
19110             }
19111             return true;
19112         });
19113         
19114         return ret;
19115     },
19116     
19117     findCells : function(ev) {
19118         var s = ev.start.clone().clearTime().getTime();
19119        // Roo.log(s);
19120         var e= ev.end.clone().clearTime().getTime();
19121        // Roo.log(e);
19122         var ret = [];
19123         this.cells.each(function(c){
19124              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19125             
19126             if(c.dateValue > e){
19127                 return ;
19128             }
19129             if(c.dateValue < s){
19130                 return ;
19131             }
19132             ret.push(c);
19133         });
19134         
19135         return ret;    
19136     },
19137     
19138 //    findBestRow: function(cells)
19139 //    {
19140 //        var ret = 0;
19141 //        
19142 //        for (var i =0 ; i < cells.length;i++) {
19143 //            ret  = Math.max(cells[i].rows || 0,ret);
19144 //        }
19145 //        return ret;
19146 //        
19147 //    },
19148     
19149     
19150     addItem : function(ev)
19151     {
19152         // look for vertical location slot in
19153         var cells = this.findCells(ev);
19154         
19155 //        ev.row = this.findBestRow(cells);
19156         
19157         // work out the location.
19158         
19159         var crow = false;
19160         var rows = [];
19161         for(var i =0; i < cells.length; i++) {
19162             
19163             cells[i].row = cells[0].row;
19164             
19165             if(i == 0){
19166                 cells[i].row = cells[i].row + 1;
19167             }
19168             
19169             if (!crow) {
19170                 crow = {
19171                     start : cells[i],
19172                     end :  cells[i]
19173                 };
19174                 continue;
19175             }
19176             if (crow.start.getY() == cells[i].getY()) {
19177                 // on same row.
19178                 crow.end = cells[i];
19179                 continue;
19180             }
19181             // different row.
19182             rows.push(crow);
19183             crow = {
19184                 start: cells[i],
19185                 end : cells[i]
19186             };
19187             
19188         }
19189         
19190         rows.push(crow);
19191         ev.els = [];
19192         ev.rows = rows;
19193         ev.cells = cells;
19194         
19195         cells[0].events.push(ev);
19196         
19197         this.calevents.push(ev);
19198     },
19199     
19200     clearEvents: function() {
19201         
19202         if(!this.calevents){
19203             return;
19204         }
19205         
19206         Roo.each(this.cells.elements, function(c){
19207             c.row = 0;
19208             c.events = [];
19209             c.more = [];
19210         });
19211         
19212         Roo.each(this.calevents, function(e) {
19213             Roo.each(e.els, function(el) {
19214                 el.un('mouseenter' ,this.onEventEnter, this);
19215                 el.un('mouseleave' ,this.onEventLeave, this);
19216                 el.remove();
19217             },this);
19218         },this);
19219         
19220         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19221             e.remove();
19222         });
19223         
19224     },
19225     
19226     renderEvents: function()
19227     {   
19228         var _this = this;
19229         
19230         this.cells.each(function(c) {
19231             
19232             if(c.row < 5){
19233                 return;
19234             }
19235             
19236             var ev = c.events;
19237             
19238             var r = 4;
19239             if(c.row != c.events.length){
19240                 r = 4 - (4 - (c.row - c.events.length));
19241             }
19242             
19243             c.events = ev.slice(0, r);
19244             c.more = ev.slice(r);
19245             
19246             if(c.more.length && c.more.length == 1){
19247                 c.events.push(c.more.pop());
19248             }
19249             
19250             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19251             
19252         });
19253             
19254         this.cells.each(function(c) {
19255             
19256             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19257             
19258             
19259             for (var e = 0; e < c.events.length; e++){
19260                 var ev = c.events[e];
19261                 var rows = ev.rows;
19262                 
19263                 for(var i = 0; i < rows.length; i++) {
19264                 
19265                     // how many rows should it span..
19266
19267                     var  cfg = {
19268                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19269                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19270
19271                         unselectable : "on",
19272                         cn : [
19273                             {
19274                                 cls: 'fc-event-inner',
19275                                 cn : [
19276     //                                {
19277     //                                  tag:'span',
19278     //                                  cls: 'fc-event-time',
19279     //                                  html : cells.length > 1 ? '' : ev.time
19280     //                                },
19281                                     {
19282                                       tag:'span',
19283                                       cls: 'fc-event-title',
19284                                       html : String.format('{0}', ev.title)
19285                                     }
19286
19287
19288                                 ]
19289                             },
19290                             {
19291                                 cls: 'ui-resizable-handle ui-resizable-e',
19292                                 html : '&nbsp;&nbsp;&nbsp'
19293                             }
19294
19295                         ]
19296                     };
19297
19298                     if (i == 0) {
19299                         cfg.cls += ' fc-event-start';
19300                     }
19301                     if ((i+1) == rows.length) {
19302                         cfg.cls += ' fc-event-end';
19303                     }
19304
19305                     var ctr = _this.el.select('.fc-event-container',true).first();
19306                     var cg = ctr.createChild(cfg);
19307
19308                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19309                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19310
19311                     var r = (c.more.length) ? 1 : 0;
19312                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19313                     cg.setWidth(ebox.right - sbox.x -2);
19314
19315                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19316                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19317                     cg.on('click', _this.onEventClick, _this, ev);
19318
19319                     ev.els.push(cg);
19320                     
19321                 }
19322                 
19323             }
19324             
19325             
19326             if(c.more.length){
19327                 var  cfg = {
19328                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19329                     style : 'position: absolute',
19330                     unselectable : "on",
19331                     cn : [
19332                         {
19333                             cls: 'fc-event-inner',
19334                             cn : [
19335                                 {
19336                                   tag:'span',
19337                                   cls: 'fc-event-title',
19338                                   html : 'More'
19339                                 }
19340
19341
19342                             ]
19343                         },
19344                         {
19345                             cls: 'ui-resizable-handle ui-resizable-e',
19346                             html : '&nbsp;&nbsp;&nbsp'
19347                         }
19348
19349                     ]
19350                 };
19351
19352                 var ctr = _this.el.select('.fc-event-container',true).first();
19353                 var cg = ctr.createChild(cfg);
19354
19355                 var sbox = c.select('.fc-day-content',true).first().getBox();
19356                 var ebox = c.select('.fc-day-content',true).first().getBox();
19357                 //Roo.log(cg);
19358                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19359                 cg.setWidth(ebox.right - sbox.x -2);
19360
19361                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19362                 
19363             }
19364             
19365         });
19366         
19367         
19368         
19369     },
19370     
19371     onEventEnter: function (e, el,event,d) {
19372         this.fireEvent('evententer', this, el, event);
19373     },
19374     
19375     onEventLeave: function (e, el,event,d) {
19376         this.fireEvent('eventleave', this, el, event);
19377     },
19378     
19379     onEventClick: function (e, el,event,d) {
19380         this.fireEvent('eventclick', this, el, event);
19381     },
19382     
19383     onMonthChange: function () {
19384         this.store.load();
19385     },
19386     
19387     onMoreEventClick: function(e, el, more)
19388     {
19389         var _this = this;
19390         
19391         this.calpopover.placement = 'right';
19392         this.calpopover.setTitle('More');
19393         
19394         this.calpopover.setContent('');
19395         
19396         var ctr = this.calpopover.el.select('.popover-content', true).first();
19397         
19398         Roo.each(more, function(m){
19399             var cfg = {
19400                 cls : 'fc-event-hori fc-event-draggable',
19401                 html : m.title
19402             };
19403             var cg = ctr.createChild(cfg);
19404             
19405             cg.on('click', _this.onEventClick, _this, m);
19406         });
19407         
19408         this.calpopover.show(el);
19409         
19410         
19411     },
19412     
19413     onLoad: function () 
19414     {   
19415         this.calevents = [];
19416         var cal = this;
19417         
19418         if(this.store.getCount() > 0){
19419             this.store.data.each(function(d){
19420                cal.addItem({
19421                     id : d.data.id,
19422                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19423                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19424                     time : d.data.start_time,
19425                     title : d.data.title,
19426                     description : d.data.description,
19427                     venue : d.data.venue
19428                 });
19429             });
19430         }
19431         
19432         this.renderEvents();
19433         
19434         if(this.calevents.length && this.loadMask){
19435             this.maskEl.hide();
19436         }
19437     },
19438     
19439     onBeforeLoad: function()
19440     {
19441         this.clearEvents();
19442         if(this.loadMask){
19443             this.maskEl.show();
19444         }
19445     }
19446 });
19447
19448  
19449  /*
19450  * - LGPL
19451  *
19452  * element
19453  * 
19454  */
19455
19456 /**
19457  * @class Roo.bootstrap.Popover
19458  * @extends Roo.bootstrap.Component
19459  * Bootstrap Popover class
19460  * @cfg {String} html contents of the popover   (or false to use children..)
19461  * @cfg {String} title of popover (or false to hide)
19462  * @cfg {String} placement how it is placed
19463  * @cfg {String} trigger click || hover (or false to trigger manually)
19464  * @cfg {String} over what (parent or false to trigger manually.)
19465  * @cfg {Number} delay - delay before showing
19466  
19467  * @constructor
19468  * Create a new Popover
19469  * @param {Object} config The config object
19470  */
19471
19472 Roo.bootstrap.Popover = function(config){
19473     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19474     
19475     this.addEvents({
19476         // raw events
19477          /**
19478          * @event show
19479          * After the popover show
19480          * 
19481          * @param {Roo.bootstrap.Popover} this
19482          */
19483         "show" : true,
19484         /**
19485          * @event hide
19486          * After the popover hide
19487          * 
19488          * @param {Roo.bootstrap.Popover} this
19489          */
19490         "hide" : true
19491     });
19492 };
19493
19494 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19495     
19496     title: 'Fill in a title',
19497     html: false,
19498     
19499     placement : 'right',
19500     trigger : 'hover', // hover
19501     
19502     delay : 0,
19503     
19504     over: 'parent',
19505     
19506     can_build_overlaid : false,
19507     
19508     getChildContainer : function()
19509     {
19510         return this.el.select('.popover-content',true).first();
19511     },
19512     
19513     getAutoCreate : function(){
19514          
19515         var cfg = {
19516            cls : 'popover roo-dynamic',
19517            style: 'display:block',
19518            cn : [
19519                 {
19520                     cls : 'arrow'
19521                 },
19522                 {
19523                     cls : 'popover-inner',
19524                     cn : [
19525                         {
19526                             tag: 'h3',
19527                             cls: 'popover-title popover-header',
19528                             html : this.title
19529                         },
19530                         {
19531                             cls : 'popover-content popover-body',
19532                             html : this.html
19533                         }
19534                     ]
19535                     
19536                 }
19537            ]
19538         };
19539         
19540         return cfg;
19541     },
19542     setTitle: function(str)
19543     {
19544         this.title = str;
19545         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19546     },
19547     setContent: function(str)
19548     {
19549         this.html = str;
19550         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19551     },
19552     // as it get's added to the bottom of the page.
19553     onRender : function(ct, position)
19554     {
19555         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19556         if(!this.el){
19557             var cfg = Roo.apply({},  this.getAutoCreate());
19558             cfg.id = Roo.id();
19559             
19560             if (this.cls) {
19561                 cfg.cls += ' ' + this.cls;
19562             }
19563             if (this.style) {
19564                 cfg.style = this.style;
19565             }
19566             //Roo.log("adding to ");
19567             this.el = Roo.get(document.body).createChild(cfg, position);
19568 //            Roo.log(this.el);
19569         }
19570         this.initEvents();
19571     },
19572     
19573     initEvents : function()
19574     {
19575         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19576         this.el.enableDisplayMode('block');
19577         this.el.hide();
19578         if (this.over === false) {
19579             return; 
19580         }
19581         if (this.triggers === false) {
19582             return;
19583         }
19584         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19585         var triggers = this.trigger ? this.trigger.split(' ') : [];
19586         Roo.each(triggers, function(trigger) {
19587         
19588             if (trigger == 'click') {
19589                 on_el.on('click', this.toggle, this);
19590             } else if (trigger != 'manual') {
19591                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19592                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19593       
19594                 on_el.on(eventIn  ,this.enter, this);
19595                 on_el.on(eventOut, this.leave, this);
19596             }
19597         }, this);
19598         
19599     },
19600     
19601     
19602     // private
19603     timeout : null,
19604     hoverState : null,
19605     
19606     toggle : function () {
19607         this.hoverState == 'in' ? this.leave() : this.enter();
19608     },
19609     
19610     enter : function () {
19611         
19612         clearTimeout(this.timeout);
19613     
19614         this.hoverState = 'in';
19615     
19616         if (!this.delay || !this.delay.show) {
19617             this.show();
19618             return;
19619         }
19620         var _t = this;
19621         this.timeout = setTimeout(function () {
19622             if (_t.hoverState == 'in') {
19623                 _t.show();
19624             }
19625         }, this.delay.show)
19626     },
19627     
19628     leave : function() {
19629         clearTimeout(this.timeout);
19630     
19631         this.hoverState = 'out';
19632     
19633         if (!this.delay || !this.delay.hide) {
19634             this.hide();
19635             return;
19636         }
19637         var _t = this;
19638         this.timeout = setTimeout(function () {
19639             if (_t.hoverState == 'out') {
19640                 _t.hide();
19641             }
19642         }, this.delay.hide)
19643     },
19644     
19645     show : function (on_el)
19646     {
19647         if (!on_el) {
19648             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19649         }
19650         
19651         // set content.
19652         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19653         if (this.html !== false) {
19654             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19655         }
19656         this.el.removeClass([
19657             'fade','top','bottom', 'left', 'right','in',
19658             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19659         ]);
19660         if (!this.title.length) {
19661             this.el.select('.popover-title',true).hide();
19662         }
19663         
19664         var placement = typeof this.placement == 'function' ?
19665             this.placement.call(this, this.el, on_el) :
19666             this.placement;
19667             
19668         var autoToken = /\s?auto?\s?/i;
19669         var autoPlace = autoToken.test(placement);
19670         if (autoPlace) {
19671             placement = placement.replace(autoToken, '') || 'top';
19672         }
19673         
19674         //this.el.detach()
19675         //this.el.setXY([0,0]);
19676         this.el.show();
19677         this.el.dom.style.display='block';
19678         this.el.addClass(placement);
19679         
19680         //this.el.appendTo(on_el);
19681         
19682         var p = this.getPosition();
19683         var box = this.el.getBox();
19684         
19685         if (autoPlace) {
19686             // fixme..
19687         }
19688         var align = Roo.bootstrap.Popover.alignment[placement];
19689         
19690 //        Roo.log(align);
19691         this.el.alignTo(on_el, align[0],align[1]);
19692         //var arrow = this.el.select('.arrow',true).first();
19693         //arrow.set(align[2], 
19694         
19695         this.el.addClass('in');
19696         
19697         
19698         if (this.el.hasClass('fade')) {
19699             // fade it?
19700         }
19701         
19702         this.hoverState = 'in';
19703         
19704         this.fireEvent('show', this);
19705         
19706     },
19707     hide : function()
19708     {
19709         this.el.setXY([0,0]);
19710         this.el.removeClass('in');
19711         this.el.hide();
19712         this.hoverState = null;
19713         
19714         this.fireEvent('hide', this);
19715     }
19716     
19717 });
19718
19719 Roo.bootstrap.Popover.alignment = {
19720     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19721     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19722     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19723     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19724 };
19725
19726  /*
19727  * - LGPL
19728  *
19729  * Progress
19730  * 
19731  */
19732
19733 /**
19734  * @class Roo.bootstrap.Progress
19735  * @extends Roo.bootstrap.Component
19736  * Bootstrap Progress class
19737  * @cfg {Boolean} striped striped of the progress bar
19738  * @cfg {Boolean} active animated of the progress bar
19739  * 
19740  * 
19741  * @constructor
19742  * Create a new Progress
19743  * @param {Object} config The config object
19744  */
19745
19746 Roo.bootstrap.Progress = function(config){
19747     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19748 };
19749
19750 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19751     
19752     striped : false,
19753     active: false,
19754     
19755     getAutoCreate : function(){
19756         var cfg = {
19757             tag: 'div',
19758             cls: 'progress'
19759         };
19760         
19761         
19762         if(this.striped){
19763             cfg.cls += ' progress-striped';
19764         }
19765       
19766         if(this.active){
19767             cfg.cls += ' active';
19768         }
19769         
19770         
19771         return cfg;
19772     }
19773    
19774 });
19775
19776  
19777
19778  /*
19779  * - LGPL
19780  *
19781  * ProgressBar
19782  * 
19783  */
19784
19785 /**
19786  * @class Roo.bootstrap.ProgressBar
19787  * @extends Roo.bootstrap.Component
19788  * Bootstrap ProgressBar class
19789  * @cfg {Number} aria_valuenow aria-value now
19790  * @cfg {Number} aria_valuemin aria-value min
19791  * @cfg {Number} aria_valuemax aria-value max
19792  * @cfg {String} label label for the progress bar
19793  * @cfg {String} panel (success | info | warning | danger )
19794  * @cfg {String} role role of the progress bar
19795  * @cfg {String} sr_only text
19796  * 
19797  * 
19798  * @constructor
19799  * Create a new ProgressBar
19800  * @param {Object} config The config object
19801  */
19802
19803 Roo.bootstrap.ProgressBar = function(config){
19804     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19805 };
19806
19807 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19808     
19809     aria_valuenow : 0,
19810     aria_valuemin : 0,
19811     aria_valuemax : 100,
19812     label : false,
19813     panel : false,
19814     role : false,
19815     sr_only: false,
19816     
19817     getAutoCreate : function()
19818     {
19819         
19820         var cfg = {
19821             tag: 'div',
19822             cls: 'progress-bar',
19823             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19824         };
19825         
19826         if(this.sr_only){
19827             cfg.cn = {
19828                 tag: 'span',
19829                 cls: 'sr-only',
19830                 html: this.sr_only
19831             }
19832         }
19833         
19834         if(this.role){
19835             cfg.role = this.role;
19836         }
19837         
19838         if(this.aria_valuenow){
19839             cfg['aria-valuenow'] = this.aria_valuenow;
19840         }
19841         
19842         if(this.aria_valuemin){
19843             cfg['aria-valuemin'] = this.aria_valuemin;
19844         }
19845         
19846         if(this.aria_valuemax){
19847             cfg['aria-valuemax'] = this.aria_valuemax;
19848         }
19849         
19850         if(this.label && !this.sr_only){
19851             cfg.html = this.label;
19852         }
19853         
19854         if(this.panel){
19855             cfg.cls += ' progress-bar-' + this.panel;
19856         }
19857         
19858         return cfg;
19859     },
19860     
19861     update : function(aria_valuenow)
19862     {
19863         this.aria_valuenow = aria_valuenow;
19864         
19865         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19866     }
19867    
19868 });
19869
19870  
19871
19872  /*
19873  * - LGPL
19874  *
19875  * column
19876  * 
19877  */
19878
19879 /**
19880  * @class Roo.bootstrap.TabGroup
19881  * @extends Roo.bootstrap.Column
19882  * Bootstrap Column class
19883  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19884  * @cfg {Boolean} carousel true to make the group behave like a carousel
19885  * @cfg {Boolean} bullets show bullets for the panels
19886  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19887  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19888  * @cfg {Boolean} showarrow (true|false) show arrow default true
19889  * 
19890  * @constructor
19891  * Create a new TabGroup
19892  * @param {Object} config The config object
19893  */
19894
19895 Roo.bootstrap.TabGroup = function(config){
19896     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19897     if (!this.navId) {
19898         this.navId = Roo.id();
19899     }
19900     this.tabs = [];
19901     Roo.bootstrap.TabGroup.register(this);
19902     
19903 };
19904
19905 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19906     
19907     carousel : false,
19908     transition : false,
19909     bullets : 0,
19910     timer : 0,
19911     autoslide : false,
19912     slideFn : false,
19913     slideOnTouch : false,
19914     showarrow : true,
19915     
19916     getAutoCreate : function()
19917     {
19918         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19919         
19920         cfg.cls += ' tab-content';
19921         
19922         if (this.carousel) {
19923             cfg.cls += ' carousel slide';
19924             
19925             cfg.cn = [{
19926                cls : 'carousel-inner',
19927                cn : []
19928             }];
19929         
19930             if(this.bullets  && !Roo.isTouch){
19931                 
19932                 var bullets = {
19933                     cls : 'carousel-bullets',
19934                     cn : []
19935                 };
19936                
19937                 if(this.bullets_cls){
19938                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19939                 }
19940                 
19941                 bullets.cn.push({
19942                     cls : 'clear'
19943                 });
19944                 
19945                 cfg.cn[0].cn.push(bullets);
19946             }
19947             
19948             if(this.showarrow){
19949                 cfg.cn[0].cn.push({
19950                     tag : 'div',
19951                     class : 'carousel-arrow',
19952                     cn : [
19953                         {
19954                             tag : 'div',
19955                             class : 'carousel-prev',
19956                             cn : [
19957                                 {
19958                                     tag : 'i',
19959                                     class : 'fa fa-chevron-left'
19960                                 }
19961                             ]
19962                         },
19963                         {
19964                             tag : 'div',
19965                             class : 'carousel-next',
19966                             cn : [
19967                                 {
19968                                     tag : 'i',
19969                                     class : 'fa fa-chevron-right'
19970                                 }
19971                             ]
19972                         }
19973                     ]
19974                 });
19975             }
19976             
19977         }
19978         
19979         return cfg;
19980     },
19981     
19982     initEvents:  function()
19983     {
19984 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19985 //            this.el.on("touchstart", this.onTouchStart, this);
19986 //        }
19987         
19988         if(this.autoslide){
19989             var _this = this;
19990             
19991             this.slideFn = window.setInterval(function() {
19992                 _this.showPanelNext();
19993             }, this.timer);
19994         }
19995         
19996         if(this.showarrow){
19997             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19998             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19999         }
20000         
20001         
20002     },
20003     
20004 //    onTouchStart : function(e, el, o)
20005 //    {
20006 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20007 //            return;
20008 //        }
20009 //        
20010 //        this.showPanelNext();
20011 //    },
20012     
20013     
20014     getChildContainer : function()
20015     {
20016         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20017     },
20018     
20019     /**
20020     * register a Navigation item
20021     * @param {Roo.bootstrap.NavItem} the navitem to add
20022     */
20023     register : function(item)
20024     {
20025         this.tabs.push( item);
20026         item.navId = this.navId; // not really needed..
20027         this.addBullet();
20028     
20029     },
20030     
20031     getActivePanel : function()
20032     {
20033         var r = false;
20034         Roo.each(this.tabs, function(t) {
20035             if (t.active) {
20036                 r = t;
20037                 return false;
20038             }
20039             return null;
20040         });
20041         return r;
20042         
20043     },
20044     getPanelByName : function(n)
20045     {
20046         var r = false;
20047         Roo.each(this.tabs, function(t) {
20048             if (t.tabId == n) {
20049                 r = t;
20050                 return false;
20051             }
20052             return null;
20053         });
20054         return r;
20055     },
20056     indexOfPanel : function(p)
20057     {
20058         var r = false;
20059         Roo.each(this.tabs, function(t,i) {
20060             if (t.tabId == p.tabId) {
20061                 r = i;
20062                 return false;
20063             }
20064             return null;
20065         });
20066         return r;
20067     },
20068     /**
20069      * show a specific panel
20070      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20071      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20072      */
20073     showPanel : function (pan)
20074     {
20075         if(this.transition || typeof(pan) == 'undefined'){
20076             Roo.log("waiting for the transitionend");
20077             return false;
20078         }
20079         
20080         if (typeof(pan) == 'number') {
20081             pan = this.tabs[pan];
20082         }
20083         
20084         if (typeof(pan) == 'string') {
20085             pan = this.getPanelByName(pan);
20086         }
20087         
20088         var cur = this.getActivePanel();
20089         
20090         if(!pan || !cur){
20091             Roo.log('pan or acitve pan is undefined');
20092             return false;
20093         }
20094         
20095         if (pan.tabId == this.getActivePanel().tabId) {
20096             return true;
20097         }
20098         
20099         if (false === cur.fireEvent('beforedeactivate')) {
20100             return false;
20101         }
20102         
20103         if(this.bullets > 0 && !Roo.isTouch){
20104             this.setActiveBullet(this.indexOfPanel(pan));
20105         }
20106         
20107         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20108             
20109             //class="carousel-item carousel-item-next carousel-item-left"
20110             
20111             this.transition = true;
20112             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20113             var lr = dir == 'next' ? 'left' : 'right';
20114             pan.el.addClass(dir); // or prev
20115             pan.el.addClass('carousel-item-' + dir); // or prev
20116             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20117             cur.el.addClass(lr); // or right
20118             pan.el.addClass(lr);
20119             cur.el.addClass('carousel-item-' +lr); // or right
20120             pan.el.addClass('carousel-item-' +lr);
20121             
20122             
20123             var _this = this;
20124             cur.el.on('transitionend', function() {
20125                 Roo.log("trans end?");
20126                 
20127                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20128                 pan.setActive(true);
20129                 
20130                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20131                 cur.setActive(false);
20132                 
20133                 _this.transition = false;
20134                 
20135             }, this, { single:  true } );
20136             
20137             return true;
20138         }
20139         
20140         cur.setActive(false);
20141         pan.setActive(true);
20142         
20143         return true;
20144         
20145     },
20146     showPanelNext : function()
20147     {
20148         var i = this.indexOfPanel(this.getActivePanel());
20149         
20150         if (i >= this.tabs.length - 1 && !this.autoslide) {
20151             return;
20152         }
20153         
20154         if (i >= this.tabs.length - 1 && this.autoslide) {
20155             i = -1;
20156         }
20157         
20158         this.showPanel(this.tabs[i+1]);
20159     },
20160     
20161     showPanelPrev : function()
20162     {
20163         var i = this.indexOfPanel(this.getActivePanel());
20164         
20165         if (i  < 1 && !this.autoslide) {
20166             return;
20167         }
20168         
20169         if (i < 1 && this.autoslide) {
20170             i = this.tabs.length;
20171         }
20172         
20173         this.showPanel(this.tabs[i-1]);
20174     },
20175     
20176     
20177     addBullet: function()
20178     {
20179         if(!this.bullets || Roo.isTouch){
20180             return;
20181         }
20182         var ctr = this.el.select('.carousel-bullets',true).first();
20183         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20184         var bullet = ctr.createChild({
20185             cls : 'bullet bullet-' + i
20186         },ctr.dom.lastChild);
20187         
20188         
20189         var _this = this;
20190         
20191         bullet.on('click', (function(e, el, o, ii, t){
20192
20193             e.preventDefault();
20194
20195             this.showPanel(ii);
20196
20197             if(this.autoslide && this.slideFn){
20198                 clearInterval(this.slideFn);
20199                 this.slideFn = window.setInterval(function() {
20200                     _this.showPanelNext();
20201                 }, this.timer);
20202             }
20203
20204         }).createDelegate(this, [i, bullet], true));
20205                 
20206         
20207     },
20208      
20209     setActiveBullet : function(i)
20210     {
20211         if(Roo.isTouch){
20212             return;
20213         }
20214         
20215         Roo.each(this.el.select('.bullet', true).elements, function(el){
20216             el.removeClass('selected');
20217         });
20218
20219         var bullet = this.el.select('.bullet-' + i, true).first();
20220         
20221         if(!bullet){
20222             return;
20223         }
20224         
20225         bullet.addClass('selected');
20226     }
20227     
20228     
20229   
20230 });
20231
20232  
20233
20234  
20235  
20236 Roo.apply(Roo.bootstrap.TabGroup, {
20237     
20238     groups: {},
20239      /**
20240     * register a Navigation Group
20241     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20242     */
20243     register : function(navgrp)
20244     {
20245         this.groups[navgrp.navId] = navgrp;
20246         
20247     },
20248     /**
20249     * fetch a Navigation Group based on the navigation ID
20250     * if one does not exist , it will get created.
20251     * @param {string} the navgroup to add
20252     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20253     */
20254     get: function(navId) {
20255         if (typeof(this.groups[navId]) == 'undefined') {
20256             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20257         }
20258         return this.groups[navId] ;
20259     }
20260     
20261     
20262     
20263 });
20264
20265  /*
20266  * - LGPL
20267  *
20268  * TabPanel
20269  * 
20270  */
20271
20272 /**
20273  * @class Roo.bootstrap.TabPanel
20274  * @extends Roo.bootstrap.Component
20275  * Bootstrap TabPanel class
20276  * @cfg {Boolean} active panel active
20277  * @cfg {String} html panel content
20278  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20279  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20280  * @cfg {String} href click to link..
20281  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20282  * 
20283  * 
20284  * @constructor
20285  * Create a new TabPanel
20286  * @param {Object} config The config object
20287  */
20288
20289 Roo.bootstrap.TabPanel = function(config){
20290     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20291     this.addEvents({
20292         /**
20293              * @event changed
20294              * Fires when the active status changes
20295              * @param {Roo.bootstrap.TabPanel} this
20296              * @param {Boolean} state the new state
20297             
20298          */
20299         'changed': true,
20300         /**
20301              * @event beforedeactivate
20302              * Fires before a tab is de-activated - can be used to do validation on a form.
20303              * @param {Roo.bootstrap.TabPanel} this
20304              * @return {Boolean} false if there is an error
20305             
20306          */
20307         'beforedeactivate': true
20308      });
20309     
20310     this.tabId = this.tabId || Roo.id();
20311   
20312 };
20313
20314 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20315     
20316     active: false,
20317     html: false,
20318     tabId: false,
20319     navId : false,
20320     href : '',
20321     touchSlide : false,
20322     getAutoCreate : function(){
20323         
20324         
20325         var cfg = {
20326             tag: 'div',
20327             // item is needed for carousel - not sure if it has any effect otherwise
20328             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20329             html: this.html || ''
20330         };
20331         
20332         if(this.active){
20333             cfg.cls += ' active';
20334         }
20335         
20336         if(this.tabId){
20337             cfg.tabId = this.tabId;
20338         }
20339         
20340         
20341         
20342         return cfg;
20343     },
20344     
20345     initEvents:  function()
20346     {
20347         var p = this.parent();
20348         
20349         this.navId = this.navId || p.navId;
20350         
20351         if (typeof(this.navId) != 'undefined') {
20352             // not really needed.. but just in case.. parent should be a NavGroup.
20353             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20354             
20355             tg.register(this);
20356             
20357             var i = tg.tabs.length - 1;
20358             
20359             if(this.active && tg.bullets > 0 && i < tg.bullets){
20360                 tg.setActiveBullet(i);
20361             }
20362         }
20363         
20364         this.el.on('click', this.onClick, this);
20365         
20366         if(Roo.isTouch && this.touchSlide){
20367             this.el.on("touchstart", this.onTouchStart, this);
20368             this.el.on("touchmove", this.onTouchMove, this);
20369             this.el.on("touchend", this.onTouchEnd, this);
20370         }
20371         
20372     },
20373     
20374     onRender : function(ct, position)
20375     {
20376         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20377     },
20378     
20379     setActive : function(state)
20380     {
20381         Roo.log("panel - set active " + this.tabId + "=" + state);
20382         
20383         this.active = state;
20384         if (!state) {
20385             this.el.removeClass('active');
20386             
20387         } else  if (!this.el.hasClass('active')) {
20388             this.el.addClass('active');
20389         }
20390         
20391         this.fireEvent('changed', this, state);
20392     },
20393     
20394     onClick : function(e)
20395     {
20396         e.preventDefault();
20397         
20398         if(!this.href.length){
20399             return;
20400         }
20401         
20402         window.location.href = this.href;
20403     },
20404     
20405     startX : 0,
20406     startY : 0,
20407     endX : 0,
20408     endY : 0,
20409     swiping : false,
20410     
20411     onTouchStart : function(e)
20412     {
20413         this.swiping = false;
20414         
20415         this.startX = e.browserEvent.touches[0].clientX;
20416         this.startY = e.browserEvent.touches[0].clientY;
20417     },
20418     
20419     onTouchMove : function(e)
20420     {
20421         this.swiping = true;
20422         
20423         this.endX = e.browserEvent.touches[0].clientX;
20424         this.endY = e.browserEvent.touches[0].clientY;
20425     },
20426     
20427     onTouchEnd : function(e)
20428     {
20429         if(!this.swiping){
20430             this.onClick(e);
20431             return;
20432         }
20433         
20434         var tabGroup = this.parent();
20435         
20436         if(this.endX > this.startX){ // swiping right
20437             tabGroup.showPanelPrev();
20438             return;
20439         }
20440         
20441         if(this.startX > this.endX){ // swiping left
20442             tabGroup.showPanelNext();
20443             return;
20444         }
20445     }
20446     
20447     
20448 });
20449  
20450
20451  
20452
20453  /*
20454  * - LGPL
20455  *
20456  * DateField
20457  * 
20458  */
20459
20460 /**
20461  * @class Roo.bootstrap.DateField
20462  * @extends Roo.bootstrap.Input
20463  * Bootstrap DateField class
20464  * @cfg {Number} weekStart default 0
20465  * @cfg {String} viewMode default empty, (months|years)
20466  * @cfg {String} minViewMode default empty, (months|years)
20467  * @cfg {Number} startDate default -Infinity
20468  * @cfg {Number} endDate default Infinity
20469  * @cfg {Boolean} todayHighlight default false
20470  * @cfg {Boolean} todayBtn default false
20471  * @cfg {Boolean} calendarWeeks default false
20472  * @cfg {Object} daysOfWeekDisabled default empty
20473  * @cfg {Boolean} singleMode default false (true | false)
20474  * 
20475  * @cfg {Boolean} keyboardNavigation default true
20476  * @cfg {String} language default en
20477  * 
20478  * @constructor
20479  * Create a new DateField
20480  * @param {Object} config The config object
20481  */
20482
20483 Roo.bootstrap.DateField = function(config){
20484     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20485      this.addEvents({
20486             /**
20487              * @event show
20488              * Fires when this field show.
20489              * @param {Roo.bootstrap.DateField} this
20490              * @param {Mixed} date The date value
20491              */
20492             show : true,
20493             /**
20494              * @event show
20495              * Fires when this field hide.
20496              * @param {Roo.bootstrap.DateField} this
20497              * @param {Mixed} date The date value
20498              */
20499             hide : true,
20500             /**
20501              * @event select
20502              * Fires when select a date.
20503              * @param {Roo.bootstrap.DateField} this
20504              * @param {Mixed} date The date value
20505              */
20506             select : true,
20507             /**
20508              * @event beforeselect
20509              * Fires when before select a date.
20510              * @param {Roo.bootstrap.DateField} this
20511              * @param {Mixed} date The date value
20512              */
20513             beforeselect : true
20514         });
20515 };
20516
20517 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20518     
20519     /**
20520      * @cfg {String} format
20521      * The default date format string which can be overriden for localization support.  The format must be
20522      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20523      */
20524     format : "m/d/y",
20525     /**
20526      * @cfg {String} altFormats
20527      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20528      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20529      */
20530     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20531     
20532     weekStart : 0,
20533     
20534     viewMode : '',
20535     
20536     minViewMode : '',
20537     
20538     todayHighlight : false,
20539     
20540     todayBtn: false,
20541     
20542     language: 'en',
20543     
20544     keyboardNavigation: true,
20545     
20546     calendarWeeks: false,
20547     
20548     startDate: -Infinity,
20549     
20550     endDate: Infinity,
20551     
20552     daysOfWeekDisabled: [],
20553     
20554     _events: [],
20555     
20556     singleMode : false,
20557     
20558     UTCDate: function()
20559     {
20560         return new Date(Date.UTC.apply(Date, arguments));
20561     },
20562     
20563     UTCToday: function()
20564     {
20565         var today = new Date();
20566         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20567     },
20568     
20569     getDate: function() {
20570             var d = this.getUTCDate();
20571             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20572     },
20573     
20574     getUTCDate: function() {
20575             return this.date;
20576     },
20577     
20578     setDate: function(d) {
20579             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20580     },
20581     
20582     setUTCDate: function(d) {
20583             this.date = d;
20584             this.setValue(this.formatDate(this.date));
20585     },
20586         
20587     onRender: function(ct, position)
20588     {
20589         
20590         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20591         
20592         this.language = this.language || 'en';
20593         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20594         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20595         
20596         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20597         this.format = this.format || 'm/d/y';
20598         this.isInline = false;
20599         this.isInput = true;
20600         this.component = this.el.select('.add-on', true).first() || false;
20601         this.component = (this.component && this.component.length === 0) ? false : this.component;
20602         this.hasInput = this.component && this.inputEl().length;
20603         
20604         if (typeof(this.minViewMode === 'string')) {
20605             switch (this.minViewMode) {
20606                 case 'months':
20607                     this.minViewMode = 1;
20608                     break;
20609                 case 'years':
20610                     this.minViewMode = 2;
20611                     break;
20612                 default:
20613                     this.minViewMode = 0;
20614                     break;
20615             }
20616         }
20617         
20618         if (typeof(this.viewMode === 'string')) {
20619             switch (this.viewMode) {
20620                 case 'months':
20621                     this.viewMode = 1;
20622                     break;
20623                 case 'years':
20624                     this.viewMode = 2;
20625                     break;
20626                 default:
20627                     this.viewMode = 0;
20628                     break;
20629             }
20630         }
20631                 
20632         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20633         
20634 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20635         
20636         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20637         
20638         this.picker().on('mousedown', this.onMousedown, this);
20639         this.picker().on('click', this.onClick, this);
20640         
20641         this.picker().addClass('datepicker-dropdown');
20642         
20643         this.startViewMode = this.viewMode;
20644         
20645         if(this.singleMode){
20646             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20647                 v.setVisibilityMode(Roo.Element.DISPLAY);
20648                 v.hide();
20649             });
20650             
20651             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20652                 v.setStyle('width', '189px');
20653             });
20654         }
20655         
20656         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20657             if(!this.calendarWeeks){
20658                 v.remove();
20659                 return;
20660             }
20661             
20662             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20663             v.attr('colspan', function(i, val){
20664                 return parseInt(val) + 1;
20665             });
20666         });
20667                         
20668         
20669         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20670         
20671         this.setStartDate(this.startDate);
20672         this.setEndDate(this.endDate);
20673         
20674         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20675         
20676         this.fillDow();
20677         this.fillMonths();
20678         this.update();
20679         this.showMode();
20680         
20681         if(this.isInline) {
20682             this.showPopup();
20683         }
20684     },
20685     
20686     picker : function()
20687     {
20688         return this.pickerEl;
20689 //        return this.el.select('.datepicker', true).first();
20690     },
20691     
20692     fillDow: function()
20693     {
20694         var dowCnt = this.weekStart;
20695         
20696         var dow = {
20697             tag: 'tr',
20698             cn: [
20699                 
20700             ]
20701         };
20702         
20703         if(this.calendarWeeks){
20704             dow.cn.push({
20705                 tag: 'th',
20706                 cls: 'cw',
20707                 html: '&nbsp;'
20708             })
20709         }
20710         
20711         while (dowCnt < this.weekStart + 7) {
20712             dow.cn.push({
20713                 tag: 'th',
20714                 cls: 'dow',
20715                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20716             });
20717         }
20718         
20719         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20720     },
20721     
20722     fillMonths: function()
20723     {    
20724         var i = 0;
20725         var months = this.picker().select('>.datepicker-months td', true).first();
20726         
20727         months.dom.innerHTML = '';
20728         
20729         while (i < 12) {
20730             var month = {
20731                 tag: 'span',
20732                 cls: 'month',
20733                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20734             };
20735             
20736             months.createChild(month);
20737         }
20738         
20739     },
20740     
20741     update: function()
20742     {
20743         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;
20744         
20745         if (this.date < this.startDate) {
20746             this.viewDate = new Date(this.startDate);
20747         } else if (this.date > this.endDate) {
20748             this.viewDate = new Date(this.endDate);
20749         } else {
20750             this.viewDate = new Date(this.date);
20751         }
20752         
20753         this.fill();
20754     },
20755     
20756     fill: function() 
20757     {
20758         var d = new Date(this.viewDate),
20759                 year = d.getUTCFullYear(),
20760                 month = d.getUTCMonth(),
20761                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20762                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20763                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20764                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20765                 currentDate = this.date && this.date.valueOf(),
20766                 today = this.UTCToday();
20767         
20768         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20769         
20770 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20771         
20772 //        this.picker.select('>tfoot th.today').
20773 //                                              .text(dates[this.language].today)
20774 //                                              .toggle(this.todayBtn !== false);
20775     
20776         this.updateNavArrows();
20777         this.fillMonths();
20778                                                 
20779         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20780         
20781         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20782          
20783         prevMonth.setUTCDate(day);
20784         
20785         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20786         
20787         var nextMonth = new Date(prevMonth);
20788         
20789         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20790         
20791         nextMonth = nextMonth.valueOf();
20792         
20793         var fillMonths = false;
20794         
20795         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20796         
20797         while(prevMonth.valueOf() <= nextMonth) {
20798             var clsName = '';
20799             
20800             if (prevMonth.getUTCDay() === this.weekStart) {
20801                 if(fillMonths){
20802                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20803                 }
20804                     
20805                 fillMonths = {
20806                     tag: 'tr',
20807                     cn: []
20808                 };
20809                 
20810                 if(this.calendarWeeks){
20811                     // ISO 8601: First week contains first thursday.
20812                     // ISO also states week starts on Monday, but we can be more abstract here.
20813                     var
20814                     // Start of current week: based on weekstart/current date
20815                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20816                     // Thursday of this week
20817                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20818                     // First Thursday of year, year from thursday
20819                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20820                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20821                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20822                     
20823                     fillMonths.cn.push({
20824                         tag: 'td',
20825                         cls: 'cw',
20826                         html: calWeek
20827                     });
20828                 }
20829             }
20830             
20831             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20832                 clsName += ' old';
20833             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20834                 clsName += ' new';
20835             }
20836             if (this.todayHighlight &&
20837                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20838                 prevMonth.getUTCMonth() == today.getMonth() &&
20839                 prevMonth.getUTCDate() == today.getDate()) {
20840                 clsName += ' today';
20841             }
20842             
20843             if (currentDate && prevMonth.valueOf() === currentDate) {
20844                 clsName += ' active';
20845             }
20846             
20847             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20848                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20849                     clsName += ' disabled';
20850             }
20851             
20852             fillMonths.cn.push({
20853                 tag: 'td',
20854                 cls: 'day ' + clsName,
20855                 html: prevMonth.getDate()
20856             });
20857             
20858             prevMonth.setDate(prevMonth.getDate()+1);
20859         }
20860           
20861         var currentYear = this.date && this.date.getUTCFullYear();
20862         var currentMonth = this.date && this.date.getUTCMonth();
20863         
20864         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20865         
20866         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20867             v.removeClass('active');
20868             
20869             if(currentYear === year && k === currentMonth){
20870                 v.addClass('active');
20871             }
20872             
20873             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20874                 v.addClass('disabled');
20875             }
20876             
20877         });
20878         
20879         
20880         year = parseInt(year/10, 10) * 10;
20881         
20882         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20883         
20884         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20885         
20886         year -= 1;
20887         for (var i = -1; i < 11; i++) {
20888             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20889                 tag: 'span',
20890                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20891                 html: year
20892             });
20893             
20894             year += 1;
20895         }
20896     },
20897     
20898     showMode: function(dir) 
20899     {
20900         if (dir) {
20901             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20902         }
20903         
20904         Roo.each(this.picker().select('>div',true).elements, function(v){
20905             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20906             v.hide();
20907         });
20908         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20909     },
20910     
20911     place: function()
20912     {
20913         if(this.isInline) {
20914             return;
20915         }
20916         
20917         this.picker().removeClass(['bottom', 'top']);
20918         
20919         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20920             /*
20921              * place to the top of element!
20922              *
20923              */
20924             
20925             this.picker().addClass('top');
20926             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20927             
20928             return;
20929         }
20930         
20931         this.picker().addClass('bottom');
20932         
20933         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20934     },
20935     
20936     parseDate : function(value)
20937     {
20938         if(!value || value instanceof Date){
20939             return value;
20940         }
20941         var v = Date.parseDate(value, this.format);
20942         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20943             v = Date.parseDate(value, 'Y-m-d');
20944         }
20945         if(!v && this.altFormats){
20946             if(!this.altFormatsArray){
20947                 this.altFormatsArray = this.altFormats.split("|");
20948             }
20949             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20950                 v = Date.parseDate(value, this.altFormatsArray[i]);
20951             }
20952         }
20953         return v;
20954     },
20955     
20956     formatDate : function(date, fmt)
20957     {   
20958         return (!date || !(date instanceof Date)) ?
20959         date : date.dateFormat(fmt || this.format);
20960     },
20961     
20962     onFocus : function()
20963     {
20964         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20965         this.showPopup();
20966     },
20967     
20968     onBlur : function()
20969     {
20970         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20971         
20972         var d = this.inputEl().getValue();
20973         
20974         this.setValue(d);
20975                 
20976         this.hidePopup();
20977     },
20978     
20979     showPopup : function()
20980     {
20981         this.picker().show();
20982         this.update();
20983         this.place();
20984         
20985         this.fireEvent('showpopup', this, this.date);
20986     },
20987     
20988     hidePopup : function()
20989     {
20990         if(this.isInline) {
20991             return;
20992         }
20993         this.picker().hide();
20994         this.viewMode = this.startViewMode;
20995         this.showMode();
20996         
20997         this.fireEvent('hidepopup', this, this.date);
20998         
20999     },
21000     
21001     onMousedown: function(e)
21002     {
21003         e.stopPropagation();
21004         e.preventDefault();
21005     },
21006     
21007     keyup: function(e)
21008     {
21009         Roo.bootstrap.DateField.superclass.keyup.call(this);
21010         this.update();
21011     },
21012
21013     setValue: function(v)
21014     {
21015         if(this.fireEvent('beforeselect', this, v) !== false){
21016             var d = new Date(this.parseDate(v) ).clearTime();
21017         
21018             if(isNaN(d.getTime())){
21019                 this.date = this.viewDate = '';
21020                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21021                 return;
21022             }
21023
21024             v = this.formatDate(d);
21025
21026             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21027
21028             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21029
21030             this.update();
21031
21032             this.fireEvent('select', this, this.date);
21033         }
21034     },
21035     
21036     getValue: function()
21037     {
21038         return this.formatDate(this.date);
21039     },
21040     
21041     fireKey: function(e)
21042     {
21043         if (!this.picker().isVisible()){
21044             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21045                 this.showPopup();
21046             }
21047             return;
21048         }
21049         
21050         var dateChanged = false,
21051         dir, day, month,
21052         newDate, newViewDate;
21053         
21054         switch(e.keyCode){
21055             case 27: // escape
21056                 this.hidePopup();
21057                 e.preventDefault();
21058                 break;
21059             case 37: // left
21060             case 39: // right
21061                 if (!this.keyboardNavigation) {
21062                     break;
21063                 }
21064                 dir = e.keyCode == 37 ? -1 : 1;
21065                 
21066                 if (e.ctrlKey){
21067                     newDate = this.moveYear(this.date, dir);
21068                     newViewDate = this.moveYear(this.viewDate, dir);
21069                 } else if (e.shiftKey){
21070                     newDate = this.moveMonth(this.date, dir);
21071                     newViewDate = this.moveMonth(this.viewDate, dir);
21072                 } else {
21073                     newDate = new Date(this.date);
21074                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21075                     newViewDate = new Date(this.viewDate);
21076                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21077                 }
21078                 if (this.dateWithinRange(newDate)){
21079                     this.date = newDate;
21080                     this.viewDate = newViewDate;
21081                     this.setValue(this.formatDate(this.date));
21082 //                    this.update();
21083                     e.preventDefault();
21084                     dateChanged = true;
21085                 }
21086                 break;
21087             case 38: // up
21088             case 40: // down
21089                 if (!this.keyboardNavigation) {
21090                     break;
21091                 }
21092                 dir = e.keyCode == 38 ? -1 : 1;
21093                 if (e.ctrlKey){
21094                     newDate = this.moveYear(this.date, dir);
21095                     newViewDate = this.moveYear(this.viewDate, dir);
21096                 } else if (e.shiftKey){
21097                     newDate = this.moveMonth(this.date, dir);
21098                     newViewDate = this.moveMonth(this.viewDate, dir);
21099                 } else {
21100                     newDate = new Date(this.date);
21101                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21102                     newViewDate = new Date(this.viewDate);
21103                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21104                 }
21105                 if (this.dateWithinRange(newDate)){
21106                     this.date = newDate;
21107                     this.viewDate = newViewDate;
21108                     this.setValue(this.formatDate(this.date));
21109 //                    this.update();
21110                     e.preventDefault();
21111                     dateChanged = true;
21112                 }
21113                 break;
21114             case 13: // enter
21115                 this.setValue(this.formatDate(this.date));
21116                 this.hidePopup();
21117                 e.preventDefault();
21118                 break;
21119             case 9: // tab
21120                 this.setValue(this.formatDate(this.date));
21121                 this.hidePopup();
21122                 break;
21123             case 16: // shift
21124             case 17: // ctrl
21125             case 18: // alt
21126                 break;
21127             default :
21128                 this.hidePopup();
21129                 
21130         }
21131     },
21132     
21133     
21134     onClick: function(e) 
21135     {
21136         e.stopPropagation();
21137         e.preventDefault();
21138         
21139         var target = e.getTarget();
21140         
21141         if(target.nodeName.toLowerCase() === 'i'){
21142             target = Roo.get(target).dom.parentNode;
21143         }
21144         
21145         var nodeName = target.nodeName;
21146         var className = target.className;
21147         var html = target.innerHTML;
21148         //Roo.log(nodeName);
21149         
21150         switch(nodeName.toLowerCase()) {
21151             case 'th':
21152                 switch(className) {
21153                     case 'switch':
21154                         this.showMode(1);
21155                         break;
21156                     case 'prev':
21157                     case 'next':
21158                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21159                         switch(this.viewMode){
21160                                 case 0:
21161                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21162                                         break;
21163                                 case 1:
21164                                 case 2:
21165                                         this.viewDate = this.moveYear(this.viewDate, dir);
21166                                         break;
21167                         }
21168                         this.fill();
21169                         break;
21170                     case 'today':
21171                         var date = new Date();
21172                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21173 //                        this.fill()
21174                         this.setValue(this.formatDate(this.date));
21175                         
21176                         this.hidePopup();
21177                         break;
21178                 }
21179                 break;
21180             case 'span':
21181                 if (className.indexOf('disabled') < 0) {
21182                     this.viewDate.setUTCDate(1);
21183                     if (className.indexOf('month') > -1) {
21184                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21185                     } else {
21186                         var year = parseInt(html, 10) || 0;
21187                         this.viewDate.setUTCFullYear(year);
21188                         
21189                     }
21190                     
21191                     if(this.singleMode){
21192                         this.setValue(this.formatDate(this.viewDate));
21193                         this.hidePopup();
21194                         return;
21195                     }
21196                     
21197                     this.showMode(-1);
21198                     this.fill();
21199                 }
21200                 break;
21201                 
21202             case 'td':
21203                 //Roo.log(className);
21204                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21205                     var day = parseInt(html, 10) || 1;
21206                     var year = this.viewDate.getUTCFullYear(),
21207                         month = this.viewDate.getUTCMonth();
21208
21209                     if (className.indexOf('old') > -1) {
21210                         if(month === 0 ){
21211                             month = 11;
21212                             year -= 1;
21213                         }else{
21214                             month -= 1;
21215                         }
21216                     } else if (className.indexOf('new') > -1) {
21217                         if (month == 11) {
21218                             month = 0;
21219                             year += 1;
21220                         } else {
21221                             month += 1;
21222                         }
21223                     }
21224                     //Roo.log([year,month,day]);
21225                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21226                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21227 //                    this.fill();
21228                     //Roo.log(this.formatDate(this.date));
21229                     this.setValue(this.formatDate(this.date));
21230                     this.hidePopup();
21231                 }
21232                 break;
21233         }
21234     },
21235     
21236     setStartDate: function(startDate)
21237     {
21238         this.startDate = startDate || -Infinity;
21239         if (this.startDate !== -Infinity) {
21240             this.startDate = this.parseDate(this.startDate);
21241         }
21242         this.update();
21243         this.updateNavArrows();
21244     },
21245
21246     setEndDate: function(endDate)
21247     {
21248         this.endDate = endDate || Infinity;
21249         if (this.endDate !== Infinity) {
21250             this.endDate = this.parseDate(this.endDate);
21251         }
21252         this.update();
21253         this.updateNavArrows();
21254     },
21255     
21256     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21257     {
21258         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21259         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21260             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21261         }
21262         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21263             return parseInt(d, 10);
21264         });
21265         this.update();
21266         this.updateNavArrows();
21267     },
21268     
21269     updateNavArrows: function() 
21270     {
21271         if(this.singleMode){
21272             return;
21273         }
21274         
21275         var d = new Date(this.viewDate),
21276         year = d.getUTCFullYear(),
21277         month = d.getUTCMonth();
21278         
21279         Roo.each(this.picker().select('.prev', true).elements, function(v){
21280             v.show();
21281             switch (this.viewMode) {
21282                 case 0:
21283
21284                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21285                         v.hide();
21286                     }
21287                     break;
21288                 case 1:
21289                 case 2:
21290                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21291                         v.hide();
21292                     }
21293                     break;
21294             }
21295         });
21296         
21297         Roo.each(this.picker().select('.next', true).elements, function(v){
21298             v.show();
21299             switch (this.viewMode) {
21300                 case 0:
21301
21302                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21303                         v.hide();
21304                     }
21305                     break;
21306                 case 1:
21307                 case 2:
21308                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21309                         v.hide();
21310                     }
21311                     break;
21312             }
21313         })
21314     },
21315     
21316     moveMonth: function(date, dir)
21317     {
21318         if (!dir) {
21319             return date;
21320         }
21321         var new_date = new Date(date.valueOf()),
21322         day = new_date.getUTCDate(),
21323         month = new_date.getUTCMonth(),
21324         mag = Math.abs(dir),
21325         new_month, test;
21326         dir = dir > 0 ? 1 : -1;
21327         if (mag == 1){
21328             test = dir == -1
21329             // If going back one month, make sure month is not current month
21330             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21331             ? function(){
21332                 return new_date.getUTCMonth() == month;
21333             }
21334             // If going forward one month, make sure month is as expected
21335             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21336             : function(){
21337                 return new_date.getUTCMonth() != new_month;
21338             };
21339             new_month = month + dir;
21340             new_date.setUTCMonth(new_month);
21341             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21342             if (new_month < 0 || new_month > 11) {
21343                 new_month = (new_month + 12) % 12;
21344             }
21345         } else {
21346             // For magnitudes >1, move one month at a time...
21347             for (var i=0; i<mag; i++) {
21348                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21349                 new_date = this.moveMonth(new_date, dir);
21350             }
21351             // ...then reset the day, keeping it in the new month
21352             new_month = new_date.getUTCMonth();
21353             new_date.setUTCDate(day);
21354             test = function(){
21355                 return new_month != new_date.getUTCMonth();
21356             };
21357         }
21358         // Common date-resetting loop -- if date is beyond end of month, make it
21359         // end of month
21360         while (test()){
21361             new_date.setUTCDate(--day);
21362             new_date.setUTCMonth(new_month);
21363         }
21364         return new_date;
21365     },
21366
21367     moveYear: function(date, dir)
21368     {
21369         return this.moveMonth(date, dir*12);
21370     },
21371
21372     dateWithinRange: function(date)
21373     {
21374         return date >= this.startDate && date <= this.endDate;
21375     },
21376
21377     
21378     remove: function() 
21379     {
21380         this.picker().remove();
21381     },
21382     
21383     validateValue : function(value)
21384     {
21385         if(this.getVisibilityEl().hasClass('hidden')){
21386             return true;
21387         }
21388         
21389         if(value.length < 1)  {
21390             if(this.allowBlank){
21391                 return true;
21392             }
21393             return false;
21394         }
21395         
21396         if(value.length < this.minLength){
21397             return false;
21398         }
21399         if(value.length > this.maxLength){
21400             return false;
21401         }
21402         if(this.vtype){
21403             var vt = Roo.form.VTypes;
21404             if(!vt[this.vtype](value, this)){
21405                 return false;
21406             }
21407         }
21408         if(typeof this.validator == "function"){
21409             var msg = this.validator(value);
21410             if(msg !== true){
21411                 return false;
21412             }
21413         }
21414         
21415         if(this.regex && !this.regex.test(value)){
21416             return false;
21417         }
21418         
21419         if(typeof(this.parseDate(value)) == 'undefined'){
21420             return false;
21421         }
21422         
21423         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21424             return false;
21425         }      
21426         
21427         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21428             return false;
21429         } 
21430         
21431         
21432         return true;
21433     },
21434     
21435     reset : function()
21436     {
21437         this.date = this.viewDate = '';
21438         
21439         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21440     }
21441    
21442 });
21443
21444 Roo.apply(Roo.bootstrap.DateField,  {
21445     
21446     head : {
21447         tag: 'thead',
21448         cn: [
21449         {
21450             tag: 'tr',
21451             cn: [
21452             {
21453                 tag: 'th',
21454                 cls: 'prev',
21455                 html: '<i class="fa fa-arrow-left"/>'
21456             },
21457             {
21458                 tag: 'th',
21459                 cls: 'switch',
21460                 colspan: '5'
21461             },
21462             {
21463                 tag: 'th',
21464                 cls: 'next',
21465                 html: '<i class="fa fa-arrow-right"/>'
21466             }
21467
21468             ]
21469         }
21470         ]
21471     },
21472     
21473     content : {
21474         tag: 'tbody',
21475         cn: [
21476         {
21477             tag: 'tr',
21478             cn: [
21479             {
21480                 tag: 'td',
21481                 colspan: '7'
21482             }
21483             ]
21484         }
21485         ]
21486     },
21487     
21488     footer : {
21489         tag: 'tfoot',
21490         cn: [
21491         {
21492             tag: 'tr',
21493             cn: [
21494             {
21495                 tag: 'th',
21496                 colspan: '7',
21497                 cls: 'today'
21498             }
21499                     
21500             ]
21501         }
21502         ]
21503     },
21504     
21505     dates:{
21506         en: {
21507             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21508             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21509             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21510             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21511             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21512             today: "Today"
21513         }
21514     },
21515     
21516     modes: [
21517     {
21518         clsName: 'days',
21519         navFnc: 'Month',
21520         navStep: 1
21521     },
21522     {
21523         clsName: 'months',
21524         navFnc: 'FullYear',
21525         navStep: 1
21526     },
21527     {
21528         clsName: 'years',
21529         navFnc: 'FullYear',
21530         navStep: 10
21531     }]
21532 });
21533
21534 Roo.apply(Roo.bootstrap.DateField,  {
21535   
21536     template : {
21537         tag: 'div',
21538         cls: 'datepicker dropdown-menu roo-dynamic',
21539         cn: [
21540         {
21541             tag: 'div',
21542             cls: 'datepicker-days',
21543             cn: [
21544             {
21545                 tag: 'table',
21546                 cls: 'table-condensed',
21547                 cn:[
21548                 Roo.bootstrap.DateField.head,
21549                 {
21550                     tag: 'tbody'
21551                 },
21552                 Roo.bootstrap.DateField.footer
21553                 ]
21554             }
21555             ]
21556         },
21557         {
21558             tag: 'div',
21559             cls: 'datepicker-months',
21560             cn: [
21561             {
21562                 tag: 'table',
21563                 cls: 'table-condensed',
21564                 cn:[
21565                 Roo.bootstrap.DateField.head,
21566                 Roo.bootstrap.DateField.content,
21567                 Roo.bootstrap.DateField.footer
21568                 ]
21569             }
21570             ]
21571         },
21572         {
21573             tag: 'div',
21574             cls: 'datepicker-years',
21575             cn: [
21576             {
21577                 tag: 'table',
21578                 cls: 'table-condensed',
21579                 cn:[
21580                 Roo.bootstrap.DateField.head,
21581                 Roo.bootstrap.DateField.content,
21582                 Roo.bootstrap.DateField.footer
21583                 ]
21584             }
21585             ]
21586         }
21587         ]
21588     }
21589 });
21590
21591  
21592
21593  /*
21594  * - LGPL
21595  *
21596  * TimeField
21597  * 
21598  */
21599
21600 /**
21601  * @class Roo.bootstrap.TimeField
21602  * @extends Roo.bootstrap.Input
21603  * Bootstrap DateField class
21604  * 
21605  * 
21606  * @constructor
21607  * Create a new TimeField
21608  * @param {Object} config The config object
21609  */
21610
21611 Roo.bootstrap.TimeField = function(config){
21612     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21613     this.addEvents({
21614             /**
21615              * @event show
21616              * Fires when this field show.
21617              * @param {Roo.bootstrap.DateField} thisthis
21618              * @param {Mixed} date The date value
21619              */
21620             show : true,
21621             /**
21622              * @event show
21623              * Fires when this field hide.
21624              * @param {Roo.bootstrap.DateField} this
21625              * @param {Mixed} date The date value
21626              */
21627             hide : true,
21628             /**
21629              * @event select
21630              * Fires when select a date.
21631              * @param {Roo.bootstrap.DateField} this
21632              * @param {Mixed} date The date value
21633              */
21634             select : true
21635         });
21636 };
21637
21638 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21639     
21640     /**
21641      * @cfg {String} format
21642      * The default time format string which can be overriden for localization support.  The format must be
21643      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21644      */
21645     format : "H:i",
21646        
21647     onRender: function(ct, position)
21648     {
21649         
21650         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21651                 
21652         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21653         
21654         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21655         
21656         this.pop = this.picker().select('>.datepicker-time',true).first();
21657         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21658         
21659         this.picker().on('mousedown', this.onMousedown, this);
21660         this.picker().on('click', this.onClick, this);
21661         
21662         this.picker().addClass('datepicker-dropdown');
21663     
21664         this.fillTime();
21665         this.update();
21666             
21667         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21668         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21669         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21670         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21671         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21672         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21673
21674     },
21675     
21676     fireKey: function(e){
21677         if (!this.picker().isVisible()){
21678             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21679                 this.show();
21680             }
21681             return;
21682         }
21683
21684         e.preventDefault();
21685         
21686         switch(e.keyCode){
21687             case 27: // escape
21688                 this.hide();
21689                 break;
21690             case 37: // left
21691             case 39: // right
21692                 this.onTogglePeriod();
21693                 break;
21694             case 38: // up
21695                 this.onIncrementMinutes();
21696                 break;
21697             case 40: // down
21698                 this.onDecrementMinutes();
21699                 break;
21700             case 13: // enter
21701             case 9: // tab
21702                 this.setTime();
21703                 break;
21704         }
21705     },
21706     
21707     onClick: function(e) {
21708         e.stopPropagation();
21709         e.preventDefault();
21710     },
21711     
21712     picker : function()
21713     {
21714         return this.el.select('.datepicker', true).first();
21715     },
21716     
21717     fillTime: function()
21718     {    
21719         var time = this.pop.select('tbody', true).first();
21720         
21721         time.dom.innerHTML = '';
21722         
21723         time.createChild({
21724             tag: 'tr',
21725             cn: [
21726                 {
21727                     tag: 'td',
21728                     cn: [
21729                         {
21730                             tag: 'a',
21731                             href: '#',
21732                             cls: 'btn',
21733                             cn: [
21734                                 {
21735                                     tag: 'span',
21736                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21737                                 }
21738                             ]
21739                         } 
21740                     ]
21741                 },
21742                 {
21743                     tag: 'td',
21744                     cls: 'separator'
21745                 },
21746                 {
21747                     tag: 'td',
21748                     cn: [
21749                         {
21750                             tag: 'a',
21751                             href: '#',
21752                             cls: 'btn',
21753                             cn: [
21754                                 {
21755                                     tag: 'span',
21756                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21757                                 }
21758                             ]
21759                         }
21760                     ]
21761                 },
21762                 {
21763                     tag: 'td',
21764                     cls: 'separator'
21765                 }
21766             ]
21767         });
21768         
21769         time.createChild({
21770             tag: 'tr',
21771             cn: [
21772                 {
21773                     tag: 'td',
21774                     cn: [
21775                         {
21776                             tag: 'span',
21777                             cls: 'timepicker-hour',
21778                             html: '00'
21779                         }  
21780                     ]
21781                 },
21782                 {
21783                     tag: 'td',
21784                     cls: 'separator',
21785                     html: ':'
21786                 },
21787                 {
21788                     tag: 'td',
21789                     cn: [
21790                         {
21791                             tag: 'span',
21792                             cls: 'timepicker-minute',
21793                             html: '00'
21794                         }  
21795                     ]
21796                 },
21797                 {
21798                     tag: 'td',
21799                     cls: 'separator'
21800                 },
21801                 {
21802                     tag: 'td',
21803                     cn: [
21804                         {
21805                             tag: 'button',
21806                             type: 'button',
21807                             cls: 'btn btn-primary period',
21808                             html: 'AM'
21809                             
21810                         }
21811                     ]
21812                 }
21813             ]
21814         });
21815         
21816         time.createChild({
21817             tag: 'tr',
21818             cn: [
21819                 {
21820                     tag: 'td',
21821                     cn: [
21822                         {
21823                             tag: 'a',
21824                             href: '#',
21825                             cls: 'btn',
21826                             cn: [
21827                                 {
21828                                     tag: 'span',
21829                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21830                                 }
21831                             ]
21832                         }
21833                     ]
21834                 },
21835                 {
21836                     tag: 'td',
21837                     cls: 'separator'
21838                 },
21839                 {
21840                     tag: 'td',
21841                     cn: [
21842                         {
21843                             tag: 'a',
21844                             href: '#',
21845                             cls: 'btn',
21846                             cn: [
21847                                 {
21848                                     tag: 'span',
21849                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21850                                 }
21851                             ]
21852                         }
21853                     ]
21854                 },
21855                 {
21856                     tag: 'td',
21857                     cls: 'separator'
21858                 }
21859             ]
21860         });
21861         
21862     },
21863     
21864     update: function()
21865     {
21866         
21867         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21868         
21869         this.fill();
21870     },
21871     
21872     fill: function() 
21873     {
21874         var hours = this.time.getHours();
21875         var minutes = this.time.getMinutes();
21876         var period = 'AM';
21877         
21878         if(hours > 11){
21879             period = 'PM';
21880         }
21881         
21882         if(hours == 0){
21883             hours = 12;
21884         }
21885         
21886         
21887         if(hours > 12){
21888             hours = hours - 12;
21889         }
21890         
21891         if(hours < 10){
21892             hours = '0' + hours;
21893         }
21894         
21895         if(minutes < 10){
21896             minutes = '0' + minutes;
21897         }
21898         
21899         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21900         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21901         this.pop.select('button', true).first().dom.innerHTML = period;
21902         
21903     },
21904     
21905     place: function()
21906     {   
21907         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21908         
21909         var cls = ['bottom'];
21910         
21911         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21912             cls.pop();
21913             cls.push('top');
21914         }
21915         
21916         cls.push('right');
21917         
21918         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21919             cls.pop();
21920             cls.push('left');
21921         }
21922         
21923         this.picker().addClass(cls.join('-'));
21924         
21925         var _this = this;
21926         
21927         Roo.each(cls, function(c){
21928             if(c == 'bottom'){
21929                 _this.picker().setTop(_this.inputEl().getHeight());
21930                 return;
21931             }
21932             if(c == 'top'){
21933                 _this.picker().setTop(0 - _this.picker().getHeight());
21934                 return;
21935             }
21936             
21937             if(c == 'left'){
21938                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21939                 return;
21940             }
21941             if(c == 'right'){
21942                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21943                 return;
21944             }
21945         });
21946         
21947     },
21948   
21949     onFocus : function()
21950     {
21951         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21952         this.show();
21953     },
21954     
21955     onBlur : function()
21956     {
21957         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21958         this.hide();
21959     },
21960     
21961     show : function()
21962     {
21963         this.picker().show();
21964         this.pop.show();
21965         this.update();
21966         this.place();
21967         
21968         this.fireEvent('show', this, this.date);
21969     },
21970     
21971     hide : function()
21972     {
21973         this.picker().hide();
21974         this.pop.hide();
21975         
21976         this.fireEvent('hide', this, this.date);
21977     },
21978     
21979     setTime : function()
21980     {
21981         this.hide();
21982         this.setValue(this.time.format(this.format));
21983         
21984         this.fireEvent('select', this, this.date);
21985         
21986         
21987     },
21988     
21989     onMousedown: function(e){
21990         e.stopPropagation();
21991         e.preventDefault();
21992     },
21993     
21994     onIncrementHours: function()
21995     {
21996         Roo.log('onIncrementHours');
21997         this.time = this.time.add(Date.HOUR, 1);
21998         this.update();
21999         
22000     },
22001     
22002     onDecrementHours: function()
22003     {
22004         Roo.log('onDecrementHours');
22005         this.time = this.time.add(Date.HOUR, -1);
22006         this.update();
22007     },
22008     
22009     onIncrementMinutes: function()
22010     {
22011         Roo.log('onIncrementMinutes');
22012         this.time = this.time.add(Date.MINUTE, 1);
22013         this.update();
22014     },
22015     
22016     onDecrementMinutes: function()
22017     {
22018         Roo.log('onDecrementMinutes');
22019         this.time = this.time.add(Date.MINUTE, -1);
22020         this.update();
22021     },
22022     
22023     onTogglePeriod: function()
22024     {
22025         Roo.log('onTogglePeriod');
22026         this.time = this.time.add(Date.HOUR, 12);
22027         this.update();
22028     }
22029     
22030    
22031 });
22032
22033 Roo.apply(Roo.bootstrap.TimeField,  {
22034     
22035     content : {
22036         tag: 'tbody',
22037         cn: [
22038             {
22039                 tag: 'tr',
22040                 cn: [
22041                 {
22042                     tag: 'td',
22043                     colspan: '7'
22044                 }
22045                 ]
22046             }
22047         ]
22048     },
22049     
22050     footer : {
22051         tag: 'tfoot',
22052         cn: [
22053             {
22054                 tag: 'tr',
22055                 cn: [
22056                 {
22057                     tag: 'th',
22058                     colspan: '7',
22059                     cls: '',
22060                     cn: [
22061                         {
22062                             tag: 'button',
22063                             cls: 'btn btn-info ok',
22064                             html: 'OK'
22065                         }
22066                     ]
22067                 }
22068
22069                 ]
22070             }
22071         ]
22072     }
22073 });
22074
22075 Roo.apply(Roo.bootstrap.TimeField,  {
22076   
22077     template : {
22078         tag: 'div',
22079         cls: 'datepicker dropdown-menu',
22080         cn: [
22081             {
22082                 tag: 'div',
22083                 cls: 'datepicker-time',
22084                 cn: [
22085                 {
22086                     tag: 'table',
22087                     cls: 'table-condensed',
22088                     cn:[
22089                     Roo.bootstrap.TimeField.content,
22090                     Roo.bootstrap.TimeField.footer
22091                     ]
22092                 }
22093                 ]
22094             }
22095         ]
22096     }
22097 });
22098
22099  
22100
22101  /*
22102  * - LGPL
22103  *
22104  * MonthField
22105  * 
22106  */
22107
22108 /**
22109  * @class Roo.bootstrap.MonthField
22110  * @extends Roo.bootstrap.Input
22111  * Bootstrap MonthField class
22112  * 
22113  * @cfg {String} language default en
22114  * 
22115  * @constructor
22116  * Create a new MonthField
22117  * @param {Object} config The config object
22118  */
22119
22120 Roo.bootstrap.MonthField = function(config){
22121     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22122     
22123     this.addEvents({
22124         /**
22125          * @event show
22126          * Fires when this field show.
22127          * @param {Roo.bootstrap.MonthField} this
22128          * @param {Mixed} date The date value
22129          */
22130         show : true,
22131         /**
22132          * @event show
22133          * Fires when this field hide.
22134          * @param {Roo.bootstrap.MonthField} this
22135          * @param {Mixed} date The date value
22136          */
22137         hide : true,
22138         /**
22139          * @event select
22140          * Fires when select a date.
22141          * @param {Roo.bootstrap.MonthField} this
22142          * @param {String} oldvalue The old value
22143          * @param {String} newvalue The new value
22144          */
22145         select : true
22146     });
22147 };
22148
22149 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22150     
22151     onRender: function(ct, position)
22152     {
22153         
22154         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22155         
22156         this.language = this.language || 'en';
22157         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22158         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22159         
22160         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22161         this.isInline = false;
22162         this.isInput = true;
22163         this.component = this.el.select('.add-on', true).first() || false;
22164         this.component = (this.component && this.component.length === 0) ? false : this.component;
22165         this.hasInput = this.component && this.inputEL().length;
22166         
22167         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22168         
22169         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22170         
22171         this.picker().on('mousedown', this.onMousedown, this);
22172         this.picker().on('click', this.onClick, this);
22173         
22174         this.picker().addClass('datepicker-dropdown');
22175         
22176         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22177             v.setStyle('width', '189px');
22178         });
22179         
22180         this.fillMonths();
22181         
22182         this.update();
22183         
22184         if(this.isInline) {
22185             this.show();
22186         }
22187         
22188     },
22189     
22190     setValue: function(v, suppressEvent)
22191     {   
22192         var o = this.getValue();
22193         
22194         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22195         
22196         this.update();
22197
22198         if(suppressEvent !== true){
22199             this.fireEvent('select', this, o, v);
22200         }
22201         
22202     },
22203     
22204     getValue: function()
22205     {
22206         return this.value;
22207     },
22208     
22209     onClick: function(e) 
22210     {
22211         e.stopPropagation();
22212         e.preventDefault();
22213         
22214         var target = e.getTarget();
22215         
22216         if(target.nodeName.toLowerCase() === 'i'){
22217             target = Roo.get(target).dom.parentNode;
22218         }
22219         
22220         var nodeName = target.nodeName;
22221         var className = target.className;
22222         var html = target.innerHTML;
22223         
22224         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22225             return;
22226         }
22227         
22228         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22229         
22230         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22231         
22232         this.hide();
22233                         
22234     },
22235     
22236     picker : function()
22237     {
22238         return this.pickerEl;
22239     },
22240     
22241     fillMonths: function()
22242     {    
22243         var i = 0;
22244         var months = this.picker().select('>.datepicker-months td', true).first();
22245         
22246         months.dom.innerHTML = '';
22247         
22248         while (i < 12) {
22249             var month = {
22250                 tag: 'span',
22251                 cls: 'month',
22252                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22253             };
22254             
22255             months.createChild(month);
22256         }
22257         
22258     },
22259     
22260     update: function()
22261     {
22262         var _this = this;
22263         
22264         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22265             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22266         }
22267         
22268         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22269             e.removeClass('active');
22270             
22271             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22272                 e.addClass('active');
22273             }
22274         })
22275     },
22276     
22277     place: function()
22278     {
22279         if(this.isInline) {
22280             return;
22281         }
22282         
22283         this.picker().removeClass(['bottom', 'top']);
22284         
22285         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22286             /*
22287              * place to the top of element!
22288              *
22289              */
22290             
22291             this.picker().addClass('top');
22292             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22293             
22294             return;
22295         }
22296         
22297         this.picker().addClass('bottom');
22298         
22299         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22300     },
22301     
22302     onFocus : function()
22303     {
22304         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22305         this.show();
22306     },
22307     
22308     onBlur : function()
22309     {
22310         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22311         
22312         var d = this.inputEl().getValue();
22313         
22314         this.setValue(d);
22315                 
22316         this.hide();
22317     },
22318     
22319     show : function()
22320     {
22321         this.picker().show();
22322         this.picker().select('>.datepicker-months', true).first().show();
22323         this.update();
22324         this.place();
22325         
22326         this.fireEvent('show', this, this.date);
22327     },
22328     
22329     hide : function()
22330     {
22331         if(this.isInline) {
22332             return;
22333         }
22334         this.picker().hide();
22335         this.fireEvent('hide', this, this.date);
22336         
22337     },
22338     
22339     onMousedown: function(e)
22340     {
22341         e.stopPropagation();
22342         e.preventDefault();
22343     },
22344     
22345     keyup: function(e)
22346     {
22347         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22348         this.update();
22349     },
22350
22351     fireKey: function(e)
22352     {
22353         if (!this.picker().isVisible()){
22354             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22355                 this.show();
22356             }
22357             return;
22358         }
22359         
22360         var dir;
22361         
22362         switch(e.keyCode){
22363             case 27: // escape
22364                 this.hide();
22365                 e.preventDefault();
22366                 break;
22367             case 37: // left
22368             case 39: // right
22369                 dir = e.keyCode == 37 ? -1 : 1;
22370                 
22371                 this.vIndex = this.vIndex + dir;
22372                 
22373                 if(this.vIndex < 0){
22374                     this.vIndex = 0;
22375                 }
22376                 
22377                 if(this.vIndex > 11){
22378                     this.vIndex = 11;
22379                 }
22380                 
22381                 if(isNaN(this.vIndex)){
22382                     this.vIndex = 0;
22383                 }
22384                 
22385                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22386                 
22387                 break;
22388             case 38: // up
22389             case 40: // down
22390                 
22391                 dir = e.keyCode == 38 ? -1 : 1;
22392                 
22393                 this.vIndex = this.vIndex + dir * 4;
22394                 
22395                 if(this.vIndex < 0){
22396                     this.vIndex = 0;
22397                 }
22398                 
22399                 if(this.vIndex > 11){
22400                     this.vIndex = 11;
22401                 }
22402                 
22403                 if(isNaN(this.vIndex)){
22404                     this.vIndex = 0;
22405                 }
22406                 
22407                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22408                 break;
22409                 
22410             case 13: // enter
22411                 
22412                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22413                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22414                 }
22415                 
22416                 this.hide();
22417                 e.preventDefault();
22418                 break;
22419             case 9: // tab
22420                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22421                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22422                 }
22423                 this.hide();
22424                 break;
22425             case 16: // shift
22426             case 17: // ctrl
22427             case 18: // alt
22428                 break;
22429             default :
22430                 this.hide();
22431                 
22432         }
22433     },
22434     
22435     remove: function() 
22436     {
22437         this.picker().remove();
22438     }
22439    
22440 });
22441
22442 Roo.apply(Roo.bootstrap.MonthField,  {
22443     
22444     content : {
22445         tag: 'tbody',
22446         cn: [
22447         {
22448             tag: 'tr',
22449             cn: [
22450             {
22451                 tag: 'td',
22452                 colspan: '7'
22453             }
22454             ]
22455         }
22456         ]
22457     },
22458     
22459     dates:{
22460         en: {
22461             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22462             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22463         }
22464     }
22465 });
22466
22467 Roo.apply(Roo.bootstrap.MonthField,  {
22468   
22469     template : {
22470         tag: 'div',
22471         cls: 'datepicker dropdown-menu roo-dynamic',
22472         cn: [
22473             {
22474                 tag: 'div',
22475                 cls: 'datepicker-months',
22476                 cn: [
22477                 {
22478                     tag: 'table',
22479                     cls: 'table-condensed',
22480                     cn:[
22481                         Roo.bootstrap.DateField.content
22482                     ]
22483                 }
22484                 ]
22485             }
22486         ]
22487     }
22488 });
22489
22490  
22491
22492  
22493  /*
22494  * - LGPL
22495  *
22496  * CheckBox
22497  * 
22498  */
22499
22500 /**
22501  * @class Roo.bootstrap.CheckBox
22502  * @extends Roo.bootstrap.Input
22503  * Bootstrap CheckBox class
22504  * 
22505  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22506  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22507  * @cfg {String} boxLabel The text that appears beside the checkbox
22508  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22509  * @cfg {Boolean} checked initnal the element
22510  * @cfg {Boolean} inline inline the element (default false)
22511  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22512  * @cfg {String} tooltip label tooltip
22513  * 
22514  * @constructor
22515  * Create a new CheckBox
22516  * @param {Object} config The config object
22517  */
22518
22519 Roo.bootstrap.CheckBox = function(config){
22520     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22521    
22522     this.addEvents({
22523         /**
22524         * @event check
22525         * Fires when the element is checked or unchecked.
22526         * @param {Roo.bootstrap.CheckBox} this This input
22527         * @param {Boolean} checked The new checked value
22528         */
22529        check : true,
22530        /**
22531         * @event click
22532         * Fires when the element is click.
22533         * @param {Roo.bootstrap.CheckBox} this This input
22534         */
22535        click : true
22536     });
22537     
22538 };
22539
22540 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22541   
22542     inputType: 'checkbox',
22543     inputValue: 1,
22544     valueOff: 0,
22545     boxLabel: false,
22546     checked: false,
22547     weight : false,
22548     inline: false,
22549     tooltip : '',
22550     
22551     // checkbox success does not make any sense really.. 
22552     invalidClass : "",
22553     validClass : "",
22554     
22555     
22556     getAutoCreate : function()
22557     {
22558         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22559         
22560         var id = Roo.id();
22561         
22562         var cfg = {};
22563         
22564         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22565         
22566         if(this.inline){
22567             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22568         }
22569         
22570         var input =  {
22571             tag: 'input',
22572             id : id,
22573             type : this.inputType,
22574             value : this.inputValue,
22575             cls : 'roo-' + this.inputType, //'form-box',
22576             placeholder : this.placeholder || ''
22577             
22578         };
22579         
22580         if(this.inputType != 'radio'){
22581             var hidden =  {
22582                 tag: 'input',
22583                 type : 'hidden',
22584                 cls : 'roo-hidden-value',
22585                 value : this.checked ? this.inputValue : this.valueOff
22586             };
22587         }
22588         
22589             
22590         if (this.weight) { // Validity check?
22591             cfg.cls += " " + this.inputType + "-" + this.weight;
22592         }
22593         
22594         if (this.disabled) {
22595             input.disabled=true;
22596         }
22597         
22598         if(this.checked){
22599             input.checked = this.checked;
22600         }
22601         
22602         if (this.name) {
22603             
22604             input.name = this.name;
22605             
22606             if(this.inputType != 'radio'){
22607                 hidden.name = this.name;
22608                 input.name = '_hidden_' + this.name;
22609             }
22610         }
22611         
22612         if (this.size) {
22613             input.cls += ' input-' + this.size;
22614         }
22615         
22616         var settings=this;
22617         
22618         ['xs','sm','md','lg'].map(function(size){
22619             if (settings[size]) {
22620                 cfg.cls += ' col-' + size + '-' + settings[size];
22621             }
22622         });
22623         
22624         var inputblock = input;
22625          
22626         if (this.before || this.after) {
22627             
22628             inputblock = {
22629                 cls : 'input-group',
22630                 cn :  [] 
22631             };
22632             
22633             if (this.before) {
22634                 inputblock.cn.push({
22635                     tag :'span',
22636                     cls : 'input-group-addon',
22637                     html : this.before
22638                 });
22639             }
22640             
22641             inputblock.cn.push(input);
22642             
22643             if(this.inputType != 'radio'){
22644                 inputblock.cn.push(hidden);
22645             }
22646             
22647             if (this.after) {
22648                 inputblock.cn.push({
22649                     tag :'span',
22650                     cls : 'input-group-addon',
22651                     html : this.after
22652                 });
22653             }
22654             
22655         }
22656         var boxLabelCfg = false;
22657         
22658         if(this.boxLabel){
22659            
22660             boxLabelCfg = {
22661                 tag: 'label',
22662                 //'for': id, // box label is handled by onclick - so no for...
22663                 cls: 'box-label',
22664                 html: this.boxLabel
22665             };
22666             if(this.tooltip){
22667                 boxLabelCfg.tooltip = this.tooltip;
22668             }
22669              
22670         }
22671         
22672         
22673         if (align ==='left' && this.fieldLabel.length) {
22674 //                Roo.log("left and has label");
22675             cfg.cn = [
22676                 {
22677                     tag: 'label',
22678                     'for' :  id,
22679                     cls : 'control-label',
22680                     html : this.fieldLabel
22681                 },
22682                 {
22683                     cls : "", 
22684                     cn: [
22685                         inputblock
22686                     ]
22687                 }
22688             ];
22689             
22690             if (boxLabelCfg) {
22691                 cfg.cn[1].cn.push(boxLabelCfg);
22692             }
22693             
22694             if(this.labelWidth > 12){
22695                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22696             }
22697             
22698             if(this.labelWidth < 13 && this.labelmd == 0){
22699                 this.labelmd = this.labelWidth;
22700             }
22701             
22702             if(this.labellg > 0){
22703                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22704                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22705             }
22706             
22707             if(this.labelmd > 0){
22708                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22709                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22710             }
22711             
22712             if(this.labelsm > 0){
22713                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22714                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22715             }
22716             
22717             if(this.labelxs > 0){
22718                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22719                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22720             }
22721             
22722         } else if ( this.fieldLabel.length) {
22723 //                Roo.log(" label");
22724                 cfg.cn = [
22725                    
22726                     {
22727                         tag: this.boxLabel ? 'span' : 'label',
22728                         'for': id,
22729                         cls: 'control-label box-input-label',
22730                         //cls : 'input-group-addon',
22731                         html : this.fieldLabel
22732                     },
22733                     
22734                     inputblock
22735                     
22736                 ];
22737                 if (boxLabelCfg) {
22738                     cfg.cn.push(boxLabelCfg);
22739                 }
22740
22741         } else {
22742             
22743 //                Roo.log(" no label && no align");
22744                 cfg.cn = [  inputblock ] ;
22745                 if (boxLabelCfg) {
22746                     cfg.cn.push(boxLabelCfg);
22747                 }
22748
22749                 
22750         }
22751         
22752        
22753         
22754         if(this.inputType != 'radio'){
22755             cfg.cn.push(hidden);
22756         }
22757         
22758         return cfg;
22759         
22760     },
22761     
22762     /**
22763      * return the real input element.
22764      */
22765     inputEl: function ()
22766     {
22767         return this.el.select('input.roo-' + this.inputType,true).first();
22768     },
22769     hiddenEl: function ()
22770     {
22771         return this.el.select('input.roo-hidden-value',true).first();
22772     },
22773     
22774     labelEl: function()
22775     {
22776         return this.el.select('label.control-label',true).first();
22777     },
22778     /* depricated... */
22779     
22780     label: function()
22781     {
22782         return this.labelEl();
22783     },
22784     
22785     boxLabelEl: function()
22786     {
22787         return this.el.select('label.box-label',true).first();
22788     },
22789     
22790     initEvents : function()
22791     {
22792 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22793         
22794         this.inputEl().on('click', this.onClick,  this);
22795         
22796         if (this.boxLabel) { 
22797             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22798         }
22799         
22800         this.startValue = this.getValue();
22801         
22802         if(this.groupId){
22803             Roo.bootstrap.CheckBox.register(this);
22804         }
22805     },
22806     
22807     onClick : function(e)
22808     {   
22809         if(this.fireEvent('click', this, e) !== false){
22810             this.setChecked(!this.checked);
22811         }
22812         
22813     },
22814     
22815     setChecked : function(state,suppressEvent)
22816     {
22817         this.startValue = this.getValue();
22818
22819         if(this.inputType == 'radio'){
22820             
22821             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22822                 e.dom.checked = false;
22823             });
22824             
22825             this.inputEl().dom.checked = true;
22826             
22827             this.inputEl().dom.value = this.inputValue;
22828             
22829             if(suppressEvent !== true){
22830                 this.fireEvent('check', this, true);
22831             }
22832             
22833             this.validate();
22834             
22835             return;
22836         }
22837         
22838         this.checked = state;
22839         
22840         this.inputEl().dom.checked = state;
22841         
22842         
22843         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22844         
22845         if(suppressEvent !== true){
22846             this.fireEvent('check', this, state);
22847         }
22848         
22849         this.validate();
22850     },
22851     
22852     getValue : function()
22853     {
22854         if(this.inputType == 'radio'){
22855             return this.getGroupValue();
22856         }
22857         
22858         return this.hiddenEl().dom.value;
22859         
22860     },
22861     
22862     getGroupValue : function()
22863     {
22864         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22865             return '';
22866         }
22867         
22868         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22869     },
22870     
22871     setValue : function(v,suppressEvent)
22872     {
22873         if(this.inputType == 'radio'){
22874             this.setGroupValue(v, suppressEvent);
22875             return;
22876         }
22877         
22878         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22879         
22880         this.validate();
22881     },
22882     
22883     setGroupValue : function(v, suppressEvent)
22884     {
22885         this.startValue = this.getValue();
22886         
22887         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22888             e.dom.checked = false;
22889             
22890             if(e.dom.value == v){
22891                 e.dom.checked = true;
22892             }
22893         });
22894         
22895         if(suppressEvent !== true){
22896             this.fireEvent('check', this, true);
22897         }
22898
22899         this.validate();
22900         
22901         return;
22902     },
22903     
22904     validate : function()
22905     {
22906         if(this.getVisibilityEl().hasClass('hidden')){
22907             return true;
22908         }
22909         
22910         if(
22911                 this.disabled || 
22912                 (this.inputType == 'radio' && this.validateRadio()) ||
22913                 (this.inputType == 'checkbox' && this.validateCheckbox())
22914         ){
22915             this.markValid();
22916             return true;
22917         }
22918         
22919         this.markInvalid();
22920         return false;
22921     },
22922     
22923     validateRadio : function()
22924     {
22925         if(this.getVisibilityEl().hasClass('hidden')){
22926             return true;
22927         }
22928         
22929         if(this.allowBlank){
22930             return true;
22931         }
22932         
22933         var valid = false;
22934         
22935         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22936             if(!e.dom.checked){
22937                 return;
22938             }
22939             
22940             valid = true;
22941             
22942             return false;
22943         });
22944         
22945         return valid;
22946     },
22947     
22948     validateCheckbox : function()
22949     {
22950         if(!this.groupId){
22951             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22952             //return (this.getValue() == this.inputValue) ? true : false;
22953         }
22954         
22955         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22956         
22957         if(!group){
22958             return false;
22959         }
22960         
22961         var r = false;
22962         
22963         for(var i in group){
22964             if(group[i].el.isVisible(true)){
22965                 r = false;
22966                 break;
22967             }
22968             
22969             r = true;
22970         }
22971         
22972         for(var i in group){
22973             if(r){
22974                 break;
22975             }
22976             
22977             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22978         }
22979         
22980         return r;
22981     },
22982     
22983     /**
22984      * Mark this field as valid
22985      */
22986     markValid : function()
22987     {
22988         var _this = this;
22989         
22990         this.fireEvent('valid', this);
22991         
22992         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22993         
22994         if(this.groupId){
22995             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22996         }
22997         
22998         if(label){
22999             label.markValid();
23000         }
23001
23002         if(this.inputType == 'radio'){
23003             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23004                 var fg = e.findParent('.form-group', false, true);
23005                 if (Roo.bootstrap.version == 3) {
23006                     fg.removeClass([_this.invalidClass, _this.validClass]);
23007                     fg.addClass(_this.validClass);
23008                 } else {
23009                     fg.removeClass(['is-valid', 'is-invalid']);
23010                     fg.addClass('is-valid');
23011                 }
23012             });
23013             
23014             return;
23015         }
23016
23017         if(!this.groupId){
23018             var fg = this.el.findParent('.form-group', false, true);
23019             if (Roo.bootstrap.version == 3) {
23020                 fg.removeClass([this.invalidClass, this.validClass]);
23021                 fg.addClass(this.validClass);
23022             } else {
23023                 fg.removeClass(['is-valid', 'is-invalid']);
23024                 fg.addClass('is-valid');
23025             }
23026             return;
23027         }
23028         
23029         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23030         
23031         if(!group){
23032             return;
23033         }
23034         
23035         for(var i in group){
23036             var fg = group[i].el.findParent('.form-group', false, true);
23037             if (Roo.bootstrap.version == 3) {
23038                 fg.removeClass([this.invalidClass, this.validClass]);
23039                 fg.addClass(this.validClass);
23040             } else {
23041                 fg.removeClass(['is-valid', 'is-invalid']);
23042                 fg.addClass('is-valid');
23043             }
23044         }
23045     },
23046     
23047      /**
23048      * Mark this field as invalid
23049      * @param {String} msg The validation message
23050      */
23051     markInvalid : function(msg)
23052     {
23053         if(this.allowBlank){
23054             return;
23055         }
23056         
23057         var _this = this;
23058         
23059         this.fireEvent('invalid', this, msg);
23060         
23061         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23062         
23063         if(this.groupId){
23064             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23065         }
23066         
23067         if(label){
23068             label.markInvalid();
23069         }
23070             
23071         if(this.inputType == 'radio'){
23072             
23073             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23074                 var fg = e.findParent('.form-group', false, true);
23075                 if (Roo.bootstrap.version == 3) {
23076                     fg.removeClass([_this.invalidClass, _this.validClass]);
23077                     fg.addClass(_this.invalidClass);
23078                 } else {
23079                     fg.removeClass(['is-invalid', 'is-valid']);
23080                     fg.addClass('is-invalid');
23081                 }
23082             });
23083             
23084             return;
23085         }
23086         
23087         if(!this.groupId){
23088             var fg = this.el.findParent('.form-group', false, true);
23089             if (Roo.bootstrap.version == 3) {
23090                 fg.removeClass([_this.invalidClass, _this.validClass]);
23091                 fg.addClass(_this.invalidClass);
23092             } else {
23093                 fg.removeClass(['is-invalid', 'is-valid']);
23094                 fg.addClass('is-invalid');
23095             }
23096             return;
23097         }
23098         
23099         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23100         
23101         if(!group){
23102             return;
23103         }
23104         
23105         for(var i in group){
23106             var fg = group[i].el.findParent('.form-group', false, true);
23107             if (Roo.bootstrap.version == 3) {
23108                 fg.removeClass([_this.invalidClass, _this.validClass]);
23109                 fg.addClass(_this.invalidClass);
23110             } else {
23111                 fg.removeClass(['is-invalid', 'is-valid']);
23112                 fg.addClass('is-invalid');
23113             }
23114         }
23115         
23116     },
23117     
23118     clearInvalid : function()
23119     {
23120         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23121         
23122         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23123         
23124         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23125         
23126         if (label && label.iconEl) {
23127             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23128             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23129         }
23130     },
23131     
23132     disable : function()
23133     {
23134         if(this.inputType != 'radio'){
23135             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23136             return;
23137         }
23138         
23139         var _this = this;
23140         
23141         if(this.rendered){
23142             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23143                 _this.getActionEl().addClass(this.disabledClass);
23144                 e.dom.disabled = true;
23145             });
23146         }
23147         
23148         this.disabled = true;
23149         this.fireEvent("disable", this);
23150         return this;
23151     },
23152
23153     enable : function()
23154     {
23155         if(this.inputType != 'radio'){
23156             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23157             return;
23158         }
23159         
23160         var _this = this;
23161         
23162         if(this.rendered){
23163             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23164                 _this.getActionEl().removeClass(this.disabledClass);
23165                 e.dom.disabled = false;
23166             });
23167         }
23168         
23169         this.disabled = false;
23170         this.fireEvent("enable", this);
23171         return this;
23172     },
23173     
23174     setBoxLabel : function(v)
23175     {
23176         this.boxLabel = v;
23177         
23178         if(this.rendered){
23179             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23180         }
23181     }
23182
23183 });
23184
23185 Roo.apply(Roo.bootstrap.CheckBox, {
23186     
23187     groups: {},
23188     
23189      /**
23190     * register a CheckBox Group
23191     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23192     */
23193     register : function(checkbox)
23194     {
23195         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23196             this.groups[checkbox.groupId] = {};
23197         }
23198         
23199         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23200             return;
23201         }
23202         
23203         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23204         
23205     },
23206     /**
23207     * fetch a CheckBox Group based on the group ID
23208     * @param {string} the group ID
23209     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23210     */
23211     get: function(groupId) {
23212         if (typeof(this.groups[groupId]) == 'undefined') {
23213             return false;
23214         }
23215         
23216         return this.groups[groupId] ;
23217     }
23218     
23219     
23220 });
23221 /*
23222  * - LGPL
23223  *
23224  * RadioItem
23225  * 
23226  */
23227
23228 /**
23229  * @class Roo.bootstrap.Radio
23230  * @extends Roo.bootstrap.Component
23231  * Bootstrap Radio class
23232  * @cfg {String} boxLabel - the label associated
23233  * @cfg {String} value - the value of radio
23234  * 
23235  * @constructor
23236  * Create a new Radio
23237  * @param {Object} config The config object
23238  */
23239 Roo.bootstrap.Radio = function(config){
23240     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23241     
23242 };
23243
23244 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23245     
23246     boxLabel : '',
23247     
23248     value : '',
23249     
23250     getAutoCreate : function()
23251     {
23252         var cfg = {
23253             tag : 'div',
23254             cls : 'form-group radio',
23255             cn : [
23256                 {
23257                     tag : 'label',
23258                     cls : 'box-label',
23259                     html : this.boxLabel
23260                 }
23261             ]
23262         };
23263         
23264         return cfg;
23265     },
23266     
23267     initEvents : function() 
23268     {
23269         this.parent().register(this);
23270         
23271         this.el.on('click', this.onClick, this);
23272         
23273     },
23274     
23275     onClick : function(e)
23276     {
23277         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23278             this.setChecked(true);
23279         }
23280     },
23281     
23282     setChecked : function(state, suppressEvent)
23283     {
23284         this.parent().setValue(this.value, suppressEvent);
23285         
23286     },
23287     
23288     setBoxLabel : function(v)
23289     {
23290         this.boxLabel = v;
23291         
23292         if(this.rendered){
23293             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23294         }
23295     }
23296     
23297 });
23298  
23299
23300  /*
23301  * - LGPL
23302  *
23303  * Input
23304  * 
23305  */
23306
23307 /**
23308  * @class Roo.bootstrap.SecurePass
23309  * @extends Roo.bootstrap.Input
23310  * Bootstrap SecurePass class
23311  *
23312  * 
23313  * @constructor
23314  * Create a new SecurePass
23315  * @param {Object} config The config object
23316  */
23317  
23318 Roo.bootstrap.SecurePass = function (config) {
23319     // these go here, so the translation tool can replace them..
23320     this.errors = {
23321         PwdEmpty: "Please type a password, and then retype it to confirm.",
23322         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23323         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23324         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23325         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23326         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23327         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23328         TooWeak: "Your password is Too Weak."
23329     },
23330     this.meterLabel = "Password strength:";
23331     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23332     this.meterClass = [
23333         "roo-password-meter-tooweak", 
23334         "roo-password-meter-weak", 
23335         "roo-password-meter-medium", 
23336         "roo-password-meter-strong", 
23337         "roo-password-meter-grey"
23338     ];
23339     
23340     this.errors = {};
23341     
23342     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23343 }
23344
23345 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23346     /**
23347      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23348      * {
23349      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23350      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23351      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23352      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23353      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23354      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23355      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23356      * })
23357      */
23358     // private
23359     
23360     meterWidth: 300,
23361     errorMsg :'',    
23362     errors: false,
23363     imageRoot: '/',
23364     /**
23365      * @cfg {String/Object} Label for the strength meter (defaults to
23366      * 'Password strength:')
23367      */
23368     // private
23369     meterLabel: '',
23370     /**
23371      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23372      * ['Weak', 'Medium', 'Strong'])
23373      */
23374     // private    
23375     pwdStrengths: false,    
23376     // private
23377     strength: 0,
23378     // private
23379     _lastPwd: null,
23380     // private
23381     kCapitalLetter: 0,
23382     kSmallLetter: 1,
23383     kDigit: 2,
23384     kPunctuation: 3,
23385     
23386     insecure: false,
23387     // private
23388     initEvents: function ()
23389     {
23390         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23391
23392         if (this.el.is('input[type=password]') && Roo.isSafari) {
23393             this.el.on('keydown', this.SafariOnKeyDown, this);
23394         }
23395
23396         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23397     },
23398     // private
23399     onRender: function (ct, position)
23400     {
23401         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23402         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23403         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23404
23405         this.trigger.createChild({
23406                    cn: [
23407                     {
23408                     //id: 'PwdMeter',
23409                     tag: 'div',
23410                     cls: 'roo-password-meter-grey col-xs-12',
23411                     style: {
23412                         //width: 0,
23413                         //width: this.meterWidth + 'px'                                                
23414                         }
23415                     },
23416                     {                            
23417                          cls: 'roo-password-meter-text'                          
23418                     }
23419                 ]            
23420         });
23421
23422          
23423         if (this.hideTrigger) {
23424             this.trigger.setDisplayed(false);
23425         }
23426         this.setSize(this.width || '', this.height || '');
23427     },
23428     // private
23429     onDestroy: function ()
23430     {
23431         if (this.trigger) {
23432             this.trigger.removeAllListeners();
23433             this.trigger.remove();
23434         }
23435         if (this.wrap) {
23436             this.wrap.remove();
23437         }
23438         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23439     },
23440     // private
23441     checkStrength: function ()
23442     {
23443         var pwd = this.inputEl().getValue();
23444         if (pwd == this._lastPwd) {
23445             return;
23446         }
23447
23448         var strength;
23449         if (this.ClientSideStrongPassword(pwd)) {
23450             strength = 3;
23451         } else if (this.ClientSideMediumPassword(pwd)) {
23452             strength = 2;
23453         } else if (this.ClientSideWeakPassword(pwd)) {
23454             strength = 1;
23455         } else {
23456             strength = 0;
23457         }
23458         
23459         Roo.log('strength1: ' + strength);
23460         
23461         //var pm = this.trigger.child('div/div/div').dom;
23462         var pm = this.trigger.child('div/div');
23463         pm.removeClass(this.meterClass);
23464         pm.addClass(this.meterClass[strength]);
23465                 
23466         
23467         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23468                 
23469         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23470         
23471         this._lastPwd = pwd;
23472     },
23473     reset: function ()
23474     {
23475         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23476         
23477         this._lastPwd = '';
23478         
23479         var pm = this.trigger.child('div/div');
23480         pm.removeClass(this.meterClass);
23481         pm.addClass('roo-password-meter-grey');        
23482         
23483         
23484         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23485         
23486         pt.innerHTML = '';
23487         this.inputEl().dom.type='password';
23488     },
23489     // private
23490     validateValue: function (value)
23491     {
23492         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23493             return false;
23494         }
23495         if (value.length == 0) {
23496             if (this.allowBlank) {
23497                 this.clearInvalid();
23498                 return true;
23499             }
23500
23501             this.markInvalid(this.errors.PwdEmpty);
23502             this.errorMsg = this.errors.PwdEmpty;
23503             return false;
23504         }
23505         
23506         if(this.insecure){
23507             return true;
23508         }
23509         
23510         if (!value.match(/[\x21-\x7e]+/)) {
23511             this.markInvalid(this.errors.PwdBadChar);
23512             this.errorMsg = this.errors.PwdBadChar;
23513             return false;
23514         }
23515         if (value.length < 6) {
23516             this.markInvalid(this.errors.PwdShort);
23517             this.errorMsg = this.errors.PwdShort;
23518             return false;
23519         }
23520         if (value.length > 16) {
23521             this.markInvalid(this.errors.PwdLong);
23522             this.errorMsg = this.errors.PwdLong;
23523             return false;
23524         }
23525         var strength;
23526         if (this.ClientSideStrongPassword(value)) {
23527             strength = 3;
23528         } else if (this.ClientSideMediumPassword(value)) {
23529             strength = 2;
23530         } else if (this.ClientSideWeakPassword(value)) {
23531             strength = 1;
23532         } else {
23533             strength = 0;
23534         }
23535
23536         
23537         if (strength < 2) {
23538             //this.markInvalid(this.errors.TooWeak);
23539             this.errorMsg = this.errors.TooWeak;
23540             //return false;
23541         }
23542         
23543         
23544         console.log('strength2: ' + strength);
23545         
23546         //var pm = this.trigger.child('div/div/div').dom;
23547         
23548         var pm = this.trigger.child('div/div');
23549         pm.removeClass(this.meterClass);
23550         pm.addClass(this.meterClass[strength]);
23551                 
23552         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23553                 
23554         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23555         
23556         this.errorMsg = ''; 
23557         return true;
23558     },
23559     // private
23560     CharacterSetChecks: function (type)
23561     {
23562         this.type = type;
23563         this.fResult = false;
23564     },
23565     // private
23566     isctype: function (character, type)
23567     {
23568         switch (type) {  
23569             case this.kCapitalLetter:
23570                 if (character >= 'A' && character <= 'Z') {
23571                     return true;
23572                 }
23573                 break;
23574             
23575             case this.kSmallLetter:
23576                 if (character >= 'a' && character <= 'z') {
23577                     return true;
23578                 }
23579                 break;
23580             
23581             case this.kDigit:
23582                 if (character >= '0' && character <= '9') {
23583                     return true;
23584                 }
23585                 break;
23586             
23587             case this.kPunctuation:
23588                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23589                     return true;
23590                 }
23591                 break;
23592             
23593             default:
23594                 return false;
23595         }
23596
23597     },
23598     // private
23599     IsLongEnough: function (pwd, size)
23600     {
23601         return !(pwd == null || isNaN(size) || pwd.length < size);
23602     },
23603     // private
23604     SpansEnoughCharacterSets: function (word, nb)
23605     {
23606         if (!this.IsLongEnough(word, nb))
23607         {
23608             return false;
23609         }
23610
23611         var characterSetChecks = new Array(
23612             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23613             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23614         );
23615         
23616         for (var index = 0; index < word.length; ++index) {
23617             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23618                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23619                     characterSetChecks[nCharSet].fResult = true;
23620                     break;
23621                 }
23622             }
23623         }
23624
23625         var nCharSets = 0;
23626         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23627             if (characterSetChecks[nCharSet].fResult) {
23628                 ++nCharSets;
23629             }
23630         }
23631
23632         if (nCharSets < nb) {
23633             return false;
23634         }
23635         return true;
23636     },
23637     // private
23638     ClientSideStrongPassword: function (pwd)
23639     {
23640         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23641     },
23642     // private
23643     ClientSideMediumPassword: function (pwd)
23644     {
23645         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23646     },
23647     // private
23648     ClientSideWeakPassword: function (pwd)
23649     {
23650         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23651     }
23652           
23653 })//<script type="text/javascript">
23654
23655 /*
23656  * Based  Ext JS Library 1.1.1
23657  * Copyright(c) 2006-2007, Ext JS, LLC.
23658  * LGPL
23659  *
23660  */
23661  
23662 /**
23663  * @class Roo.HtmlEditorCore
23664  * @extends Roo.Component
23665  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23666  *
23667  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23668  */
23669
23670 Roo.HtmlEditorCore = function(config){
23671     
23672     
23673     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23674     
23675     
23676     this.addEvents({
23677         /**
23678          * @event initialize
23679          * Fires when the editor is fully initialized (including the iframe)
23680          * @param {Roo.HtmlEditorCore} this
23681          */
23682         initialize: true,
23683         /**
23684          * @event activate
23685          * Fires when the editor is first receives the focus. Any insertion must wait
23686          * until after this event.
23687          * @param {Roo.HtmlEditorCore} this
23688          */
23689         activate: true,
23690          /**
23691          * @event beforesync
23692          * Fires before the textarea is updated with content from the editor iframe. Return false
23693          * to cancel the sync.
23694          * @param {Roo.HtmlEditorCore} this
23695          * @param {String} html
23696          */
23697         beforesync: true,
23698          /**
23699          * @event beforepush
23700          * Fires before the iframe editor is updated with content from the textarea. Return false
23701          * to cancel the push.
23702          * @param {Roo.HtmlEditorCore} this
23703          * @param {String} html
23704          */
23705         beforepush: true,
23706          /**
23707          * @event sync
23708          * Fires when the textarea is updated with content from the editor iframe.
23709          * @param {Roo.HtmlEditorCore} this
23710          * @param {String} html
23711          */
23712         sync: true,
23713          /**
23714          * @event push
23715          * Fires when the iframe editor is updated with content from the textarea.
23716          * @param {Roo.HtmlEditorCore} this
23717          * @param {String} html
23718          */
23719         push: true,
23720         
23721         /**
23722          * @event editorevent
23723          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23724          * @param {Roo.HtmlEditorCore} this
23725          */
23726         editorevent: true
23727         
23728     });
23729     
23730     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23731     
23732     // defaults : white / black...
23733     this.applyBlacklists();
23734     
23735     
23736     
23737 };
23738
23739
23740 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23741
23742
23743      /**
23744      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23745      */
23746     
23747     owner : false,
23748     
23749      /**
23750      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23751      *                        Roo.resizable.
23752      */
23753     resizable : false,
23754      /**
23755      * @cfg {Number} height (in pixels)
23756      */   
23757     height: 300,
23758    /**
23759      * @cfg {Number} width (in pixels)
23760      */   
23761     width: 500,
23762     
23763     /**
23764      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23765      * 
23766      */
23767     stylesheets: false,
23768     
23769     // id of frame..
23770     frameId: false,
23771     
23772     // private properties
23773     validationEvent : false,
23774     deferHeight: true,
23775     initialized : false,
23776     activated : false,
23777     sourceEditMode : false,
23778     onFocus : Roo.emptyFn,
23779     iframePad:3,
23780     hideMode:'offsets',
23781     
23782     clearUp: true,
23783     
23784     // blacklist + whitelisted elements..
23785     black: false,
23786     white: false,
23787      
23788     bodyCls : '',
23789
23790     /**
23791      * Protected method that will not generally be called directly. It
23792      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23793      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23794      */
23795     getDocMarkup : function(){
23796         // body styles..
23797         var st = '';
23798         
23799         // inherit styels from page...?? 
23800         if (this.stylesheets === false) {
23801             
23802             Roo.get(document.head).select('style').each(function(node) {
23803                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23804             });
23805             
23806             Roo.get(document.head).select('link').each(function(node) { 
23807                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23808             });
23809             
23810         } else if (!this.stylesheets.length) {
23811                 // simple..
23812                 st = '<style type="text/css">' +
23813                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23814                    '</style>';
23815         } else {
23816             for (var i in this.stylesheets) { 
23817                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23818             }
23819             
23820         }
23821         
23822         st +=  '<style type="text/css">' +
23823             'IMG { cursor: pointer } ' +
23824         '</style>';
23825
23826         var cls = 'roo-htmleditor-body';
23827         
23828         if(this.bodyCls.length){
23829             cls += ' ' + this.bodyCls;
23830         }
23831         
23832         return '<html><head>' + st  +
23833             //<style type="text/css">' +
23834             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23835             //'</style>' +
23836             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23837     },
23838
23839     // private
23840     onRender : function(ct, position)
23841     {
23842         var _t = this;
23843         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23844         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23845         
23846         
23847         this.el.dom.style.border = '0 none';
23848         this.el.dom.setAttribute('tabIndex', -1);
23849         this.el.addClass('x-hidden hide');
23850         
23851         
23852         
23853         if(Roo.isIE){ // fix IE 1px bogus margin
23854             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23855         }
23856        
23857         
23858         this.frameId = Roo.id();
23859         
23860          
23861         
23862         var iframe = this.owner.wrap.createChild({
23863             tag: 'iframe',
23864             cls: 'form-control', // bootstrap..
23865             id: this.frameId,
23866             name: this.frameId,
23867             frameBorder : 'no',
23868             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23869         }, this.el
23870         );
23871         
23872         
23873         this.iframe = iframe.dom;
23874
23875          this.assignDocWin();
23876         
23877         this.doc.designMode = 'on';
23878        
23879         this.doc.open();
23880         this.doc.write(this.getDocMarkup());
23881         this.doc.close();
23882
23883         
23884         var task = { // must defer to wait for browser to be ready
23885             run : function(){
23886                 //console.log("run task?" + this.doc.readyState);
23887                 this.assignDocWin();
23888                 if(this.doc.body || this.doc.readyState == 'complete'){
23889                     try {
23890                         this.doc.designMode="on";
23891                     } catch (e) {
23892                         return;
23893                     }
23894                     Roo.TaskMgr.stop(task);
23895                     this.initEditor.defer(10, this);
23896                 }
23897             },
23898             interval : 10,
23899             duration: 10000,
23900             scope: this
23901         };
23902         Roo.TaskMgr.start(task);
23903
23904     },
23905
23906     // private
23907     onResize : function(w, h)
23908     {
23909          Roo.log('resize: ' +w + ',' + h );
23910         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23911         if(!this.iframe){
23912             return;
23913         }
23914         if(typeof w == 'number'){
23915             
23916             this.iframe.style.width = w + 'px';
23917         }
23918         if(typeof h == 'number'){
23919             
23920             this.iframe.style.height = h + 'px';
23921             if(this.doc){
23922                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23923             }
23924         }
23925         
23926     },
23927
23928     /**
23929      * Toggles the editor between standard and source edit mode.
23930      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23931      */
23932     toggleSourceEdit : function(sourceEditMode){
23933         
23934         this.sourceEditMode = sourceEditMode === true;
23935         
23936         if(this.sourceEditMode){
23937  
23938             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23939             
23940         }else{
23941             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23942             //this.iframe.className = '';
23943             this.deferFocus();
23944         }
23945         //this.setSize(this.owner.wrap.getSize());
23946         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23947     },
23948
23949     
23950   
23951
23952     /**
23953      * Protected method that will not generally be called directly. If you need/want
23954      * custom HTML cleanup, this is the method you should override.
23955      * @param {String} html The HTML to be cleaned
23956      * return {String} The cleaned HTML
23957      */
23958     cleanHtml : function(html){
23959         html = String(html);
23960         if(html.length > 5){
23961             if(Roo.isSafari){ // strip safari nonsense
23962                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23963             }
23964         }
23965         if(html == '&nbsp;'){
23966             html = '';
23967         }
23968         return html;
23969     },
23970
23971     /**
23972      * HTML Editor -> Textarea
23973      * Protected method that will not generally be called directly. Syncs the contents
23974      * of the editor iframe with the textarea.
23975      */
23976     syncValue : function(){
23977         if(this.initialized){
23978             var bd = (this.doc.body || this.doc.documentElement);
23979             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23980             var html = bd.innerHTML;
23981             if(Roo.isSafari){
23982                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23983                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23984                 if(m && m[1]){
23985                     html = '<div style="'+m[0]+'">' + html + '</div>';
23986                 }
23987             }
23988             html = this.cleanHtml(html);
23989             // fix up the special chars.. normaly like back quotes in word...
23990             // however we do not want to do this with chinese..
23991             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23992                 
23993                 var cc = match.charCodeAt();
23994
23995                 // Get the character value, handling surrogate pairs
23996                 if (match.length == 2) {
23997                     // It's a surrogate pair, calculate the Unicode code point
23998                     var high = match.charCodeAt(0) - 0xD800;
23999                     var low  = match.charCodeAt(1) - 0xDC00;
24000                     cc = (high * 0x400) + low + 0x10000;
24001                 }  else if (
24002                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24003                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24004                     (cc >= 0xf900 && cc < 0xfb00 )
24005                 ) {
24006                         return match;
24007                 }  
24008          
24009                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24010                 return "&#" + cc + ";";
24011                 
24012                 
24013             });
24014             
24015             
24016              
24017             if(this.owner.fireEvent('beforesync', this, html) !== false){
24018                 this.el.dom.value = html;
24019                 this.owner.fireEvent('sync', this, html);
24020             }
24021         }
24022     },
24023
24024     /**
24025      * Protected method that will not generally be called directly. Pushes the value of the textarea
24026      * into the iframe editor.
24027      */
24028     pushValue : function(){
24029         if(this.initialized){
24030             var v = this.el.dom.value.trim();
24031             
24032 //            if(v.length < 1){
24033 //                v = '&#160;';
24034 //            }
24035             
24036             if(this.owner.fireEvent('beforepush', this, v) !== false){
24037                 var d = (this.doc.body || this.doc.documentElement);
24038                 d.innerHTML = v;
24039                 this.cleanUpPaste();
24040                 this.el.dom.value = d.innerHTML;
24041                 this.owner.fireEvent('push', this, v);
24042             }
24043         }
24044     },
24045
24046     // private
24047     deferFocus : function(){
24048         this.focus.defer(10, this);
24049     },
24050
24051     // doc'ed in Field
24052     focus : function(){
24053         if(this.win && !this.sourceEditMode){
24054             this.win.focus();
24055         }else{
24056             this.el.focus();
24057         }
24058     },
24059     
24060     assignDocWin: function()
24061     {
24062         var iframe = this.iframe;
24063         
24064          if(Roo.isIE){
24065             this.doc = iframe.contentWindow.document;
24066             this.win = iframe.contentWindow;
24067         } else {
24068 //            if (!Roo.get(this.frameId)) {
24069 //                return;
24070 //            }
24071 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24072 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24073             
24074             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24075                 return;
24076             }
24077             
24078             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24079             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24080         }
24081     },
24082     
24083     // private
24084     initEditor : function(){
24085         //console.log("INIT EDITOR");
24086         this.assignDocWin();
24087         
24088         
24089         
24090         this.doc.designMode="on";
24091         this.doc.open();
24092         this.doc.write(this.getDocMarkup());
24093         this.doc.close();
24094         
24095         var dbody = (this.doc.body || this.doc.documentElement);
24096         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24097         // this copies styles from the containing element into thsi one..
24098         // not sure why we need all of this..
24099         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24100         
24101         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24102         //ss['background-attachment'] = 'fixed'; // w3c
24103         dbody.bgProperties = 'fixed'; // ie
24104         //Roo.DomHelper.applyStyles(dbody, ss);
24105         Roo.EventManager.on(this.doc, {
24106             //'mousedown': this.onEditorEvent,
24107             'mouseup': this.onEditorEvent,
24108             'dblclick': this.onEditorEvent,
24109             'click': this.onEditorEvent,
24110             'keyup': this.onEditorEvent,
24111             buffer:100,
24112             scope: this
24113         });
24114         if(Roo.isGecko){
24115             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24116         }
24117         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24118             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24119         }
24120         this.initialized = true;
24121
24122         this.owner.fireEvent('initialize', this);
24123         this.pushValue();
24124     },
24125
24126     // private
24127     onDestroy : function(){
24128         
24129         
24130         
24131         if(this.rendered){
24132             
24133             //for (var i =0; i < this.toolbars.length;i++) {
24134             //    // fixme - ask toolbars for heights?
24135             //    this.toolbars[i].onDestroy();
24136            // }
24137             
24138             //this.wrap.dom.innerHTML = '';
24139             //this.wrap.remove();
24140         }
24141     },
24142
24143     // private
24144     onFirstFocus : function(){
24145         
24146         this.assignDocWin();
24147         
24148         
24149         this.activated = true;
24150          
24151     
24152         if(Roo.isGecko){ // prevent silly gecko errors
24153             this.win.focus();
24154             var s = this.win.getSelection();
24155             if(!s.focusNode || s.focusNode.nodeType != 3){
24156                 var r = s.getRangeAt(0);
24157                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24158                 r.collapse(true);
24159                 this.deferFocus();
24160             }
24161             try{
24162                 this.execCmd('useCSS', true);
24163                 this.execCmd('styleWithCSS', false);
24164             }catch(e){}
24165         }
24166         this.owner.fireEvent('activate', this);
24167     },
24168
24169     // private
24170     adjustFont: function(btn){
24171         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24172         //if(Roo.isSafari){ // safari
24173         //    adjust *= 2;
24174        // }
24175         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24176         if(Roo.isSafari){ // safari
24177             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24178             v =  (v < 10) ? 10 : v;
24179             v =  (v > 48) ? 48 : v;
24180             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24181             
24182         }
24183         
24184         
24185         v = Math.max(1, v+adjust);
24186         
24187         this.execCmd('FontSize', v  );
24188     },
24189
24190     onEditorEvent : function(e)
24191     {
24192         this.owner.fireEvent('editorevent', this, e);
24193       //  this.updateToolbar();
24194         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24195     },
24196
24197     insertTag : function(tg)
24198     {
24199         // could be a bit smarter... -> wrap the current selected tRoo..
24200         if (tg.toLowerCase() == 'span' ||
24201             tg.toLowerCase() == 'code' ||
24202             tg.toLowerCase() == 'sup' ||
24203             tg.toLowerCase() == 'sub' 
24204             ) {
24205             
24206             range = this.createRange(this.getSelection());
24207             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24208             wrappingNode.appendChild(range.extractContents());
24209             range.insertNode(wrappingNode);
24210
24211             return;
24212             
24213             
24214             
24215         }
24216         this.execCmd("formatblock",   tg);
24217         
24218     },
24219     
24220     insertText : function(txt)
24221     {
24222         
24223         
24224         var range = this.createRange();
24225         range.deleteContents();
24226                //alert(Sender.getAttribute('label'));
24227                
24228         range.insertNode(this.doc.createTextNode(txt));
24229     } ,
24230     
24231      
24232
24233     /**
24234      * Executes a Midas editor command on the editor document and performs necessary focus and
24235      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24236      * @param {String} cmd The Midas command
24237      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24238      */
24239     relayCmd : function(cmd, value){
24240         this.win.focus();
24241         this.execCmd(cmd, value);
24242         this.owner.fireEvent('editorevent', this);
24243         //this.updateToolbar();
24244         this.owner.deferFocus();
24245     },
24246
24247     /**
24248      * Executes a Midas editor command directly on the editor document.
24249      * For visual commands, you should use {@link #relayCmd} instead.
24250      * <b>This should only be called after the editor is initialized.</b>
24251      * @param {String} cmd The Midas command
24252      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24253      */
24254     execCmd : function(cmd, value){
24255         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24256         this.syncValue();
24257     },
24258  
24259  
24260    
24261     /**
24262      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24263      * to insert tRoo.
24264      * @param {String} text | dom node.. 
24265      */
24266     insertAtCursor : function(text)
24267     {
24268         
24269         if(!this.activated){
24270             return;
24271         }
24272         /*
24273         if(Roo.isIE){
24274             this.win.focus();
24275             var r = this.doc.selection.createRange();
24276             if(r){
24277                 r.collapse(true);
24278                 r.pasteHTML(text);
24279                 this.syncValue();
24280                 this.deferFocus();
24281             
24282             }
24283             return;
24284         }
24285         */
24286         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24287             this.win.focus();
24288             
24289             
24290             // from jquery ui (MIT licenced)
24291             var range, node;
24292             var win = this.win;
24293             
24294             if (win.getSelection && win.getSelection().getRangeAt) {
24295                 range = win.getSelection().getRangeAt(0);
24296                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24297                 range.insertNode(node);
24298             } else if (win.document.selection && win.document.selection.createRange) {
24299                 // no firefox support
24300                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24301                 win.document.selection.createRange().pasteHTML(txt);
24302             } else {
24303                 // no firefox support
24304                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24305                 this.execCmd('InsertHTML', txt);
24306             } 
24307             
24308             this.syncValue();
24309             
24310             this.deferFocus();
24311         }
24312     },
24313  // private
24314     mozKeyPress : function(e){
24315         if(e.ctrlKey){
24316             var c = e.getCharCode(), cmd;
24317           
24318             if(c > 0){
24319                 c = String.fromCharCode(c).toLowerCase();
24320                 switch(c){
24321                     case 'b':
24322                         cmd = 'bold';
24323                         break;
24324                     case 'i':
24325                         cmd = 'italic';
24326                         break;
24327                     
24328                     case 'u':
24329                         cmd = 'underline';
24330                         break;
24331                     
24332                     case 'v':
24333                         this.cleanUpPaste.defer(100, this);
24334                         return;
24335                         
24336                 }
24337                 if(cmd){
24338                     this.win.focus();
24339                     this.execCmd(cmd);
24340                     this.deferFocus();
24341                     e.preventDefault();
24342                 }
24343                 
24344             }
24345         }
24346     },
24347
24348     // private
24349     fixKeys : function(){ // load time branching for fastest keydown performance
24350         if(Roo.isIE){
24351             return function(e){
24352                 var k = e.getKey(), r;
24353                 if(k == e.TAB){
24354                     e.stopEvent();
24355                     r = this.doc.selection.createRange();
24356                     if(r){
24357                         r.collapse(true);
24358                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24359                         this.deferFocus();
24360                     }
24361                     return;
24362                 }
24363                 
24364                 if(k == e.ENTER){
24365                     r = this.doc.selection.createRange();
24366                     if(r){
24367                         var target = r.parentElement();
24368                         if(!target || target.tagName.toLowerCase() != 'li'){
24369                             e.stopEvent();
24370                             r.pasteHTML('<br />');
24371                             r.collapse(false);
24372                             r.select();
24373                         }
24374                     }
24375                 }
24376                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24377                     this.cleanUpPaste.defer(100, this);
24378                     return;
24379                 }
24380                 
24381                 
24382             };
24383         }else if(Roo.isOpera){
24384             return function(e){
24385                 var k = e.getKey();
24386                 if(k == e.TAB){
24387                     e.stopEvent();
24388                     this.win.focus();
24389                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24390                     this.deferFocus();
24391                 }
24392                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24393                     this.cleanUpPaste.defer(100, this);
24394                     return;
24395                 }
24396                 
24397             };
24398         }else if(Roo.isSafari){
24399             return function(e){
24400                 var k = e.getKey();
24401                 
24402                 if(k == e.TAB){
24403                     e.stopEvent();
24404                     this.execCmd('InsertText','\t');
24405                     this.deferFocus();
24406                     return;
24407                 }
24408                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24409                     this.cleanUpPaste.defer(100, this);
24410                     return;
24411                 }
24412                 
24413              };
24414         }
24415     }(),
24416     
24417     getAllAncestors: function()
24418     {
24419         var p = this.getSelectedNode();
24420         var a = [];
24421         if (!p) {
24422             a.push(p); // push blank onto stack..
24423             p = this.getParentElement();
24424         }
24425         
24426         
24427         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24428             a.push(p);
24429             p = p.parentNode;
24430         }
24431         a.push(this.doc.body);
24432         return a;
24433     },
24434     lastSel : false,
24435     lastSelNode : false,
24436     
24437     
24438     getSelection : function() 
24439     {
24440         this.assignDocWin();
24441         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24442     },
24443     
24444     getSelectedNode: function() 
24445     {
24446         // this may only work on Gecko!!!
24447         
24448         // should we cache this!!!!
24449         
24450         
24451         
24452          
24453         var range = this.createRange(this.getSelection()).cloneRange();
24454         
24455         if (Roo.isIE) {
24456             var parent = range.parentElement();
24457             while (true) {
24458                 var testRange = range.duplicate();
24459                 testRange.moveToElementText(parent);
24460                 if (testRange.inRange(range)) {
24461                     break;
24462                 }
24463                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24464                     break;
24465                 }
24466                 parent = parent.parentElement;
24467             }
24468             return parent;
24469         }
24470         
24471         // is ancestor a text element.
24472         var ac =  range.commonAncestorContainer;
24473         if (ac.nodeType == 3) {
24474             ac = ac.parentNode;
24475         }
24476         
24477         var ar = ac.childNodes;
24478          
24479         var nodes = [];
24480         var other_nodes = [];
24481         var has_other_nodes = false;
24482         for (var i=0;i<ar.length;i++) {
24483             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24484                 continue;
24485             }
24486             // fullly contained node.
24487             
24488             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24489                 nodes.push(ar[i]);
24490                 continue;
24491             }
24492             
24493             // probably selected..
24494             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24495                 other_nodes.push(ar[i]);
24496                 continue;
24497             }
24498             // outer..
24499             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24500                 continue;
24501             }
24502             
24503             
24504             has_other_nodes = true;
24505         }
24506         if (!nodes.length && other_nodes.length) {
24507             nodes= other_nodes;
24508         }
24509         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24510             return false;
24511         }
24512         
24513         return nodes[0];
24514     },
24515     createRange: function(sel)
24516     {
24517         // this has strange effects when using with 
24518         // top toolbar - not sure if it's a great idea.
24519         //this.editor.contentWindow.focus();
24520         if (typeof sel != "undefined") {
24521             try {
24522                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24523             } catch(e) {
24524                 return this.doc.createRange();
24525             }
24526         } else {
24527             return this.doc.createRange();
24528         }
24529     },
24530     getParentElement: function()
24531     {
24532         
24533         this.assignDocWin();
24534         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24535         
24536         var range = this.createRange(sel);
24537          
24538         try {
24539             var p = range.commonAncestorContainer;
24540             while (p.nodeType == 3) { // text node
24541                 p = p.parentNode;
24542             }
24543             return p;
24544         } catch (e) {
24545             return null;
24546         }
24547     
24548     },
24549     /***
24550      *
24551      * Range intersection.. the hard stuff...
24552      *  '-1' = before
24553      *  '0' = hits..
24554      *  '1' = after.
24555      *         [ -- selected range --- ]
24556      *   [fail]                        [fail]
24557      *
24558      *    basically..
24559      *      if end is before start or  hits it. fail.
24560      *      if start is after end or hits it fail.
24561      *
24562      *   if either hits (but other is outside. - then it's not 
24563      *   
24564      *    
24565      **/
24566     
24567     
24568     // @see http://www.thismuchiknow.co.uk/?p=64.
24569     rangeIntersectsNode : function(range, node)
24570     {
24571         var nodeRange = node.ownerDocument.createRange();
24572         try {
24573             nodeRange.selectNode(node);
24574         } catch (e) {
24575             nodeRange.selectNodeContents(node);
24576         }
24577     
24578         var rangeStartRange = range.cloneRange();
24579         rangeStartRange.collapse(true);
24580     
24581         var rangeEndRange = range.cloneRange();
24582         rangeEndRange.collapse(false);
24583     
24584         var nodeStartRange = nodeRange.cloneRange();
24585         nodeStartRange.collapse(true);
24586     
24587         var nodeEndRange = nodeRange.cloneRange();
24588         nodeEndRange.collapse(false);
24589     
24590         return rangeStartRange.compareBoundaryPoints(
24591                  Range.START_TO_START, nodeEndRange) == -1 &&
24592                rangeEndRange.compareBoundaryPoints(
24593                  Range.START_TO_START, nodeStartRange) == 1;
24594         
24595          
24596     },
24597     rangeCompareNode : function(range, node)
24598     {
24599         var nodeRange = node.ownerDocument.createRange();
24600         try {
24601             nodeRange.selectNode(node);
24602         } catch (e) {
24603             nodeRange.selectNodeContents(node);
24604         }
24605         
24606         
24607         range.collapse(true);
24608     
24609         nodeRange.collapse(true);
24610      
24611         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24612         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24613          
24614         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24615         
24616         var nodeIsBefore   =  ss == 1;
24617         var nodeIsAfter    = ee == -1;
24618         
24619         if (nodeIsBefore && nodeIsAfter) {
24620             return 0; // outer
24621         }
24622         if (!nodeIsBefore && nodeIsAfter) {
24623             return 1; //right trailed.
24624         }
24625         
24626         if (nodeIsBefore && !nodeIsAfter) {
24627             return 2;  // left trailed.
24628         }
24629         // fully contined.
24630         return 3;
24631     },
24632
24633     // private? - in a new class?
24634     cleanUpPaste :  function()
24635     {
24636         // cleans up the whole document..
24637         Roo.log('cleanuppaste');
24638         
24639         this.cleanUpChildren(this.doc.body);
24640         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24641         if (clean != this.doc.body.innerHTML) {
24642             this.doc.body.innerHTML = clean;
24643         }
24644         
24645     },
24646     
24647     cleanWordChars : function(input) {// change the chars to hex code
24648         var he = Roo.HtmlEditorCore;
24649         
24650         var output = input;
24651         Roo.each(he.swapCodes, function(sw) { 
24652             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24653             
24654             output = output.replace(swapper, sw[1]);
24655         });
24656         
24657         return output;
24658     },
24659     
24660     
24661     cleanUpChildren : function (n)
24662     {
24663         if (!n.childNodes.length) {
24664             return;
24665         }
24666         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24667            this.cleanUpChild(n.childNodes[i]);
24668         }
24669     },
24670     
24671     
24672         
24673     
24674     cleanUpChild : function (node)
24675     {
24676         var ed = this;
24677         //console.log(node);
24678         if (node.nodeName == "#text") {
24679             // clean up silly Windows -- stuff?
24680             return; 
24681         }
24682         if (node.nodeName == "#comment") {
24683             node.parentNode.removeChild(node);
24684             // clean up silly Windows -- stuff?
24685             return; 
24686         }
24687         var lcname = node.tagName.toLowerCase();
24688         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24689         // whitelist of tags..
24690         
24691         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24692             // remove node.
24693             node.parentNode.removeChild(node);
24694             return;
24695             
24696         }
24697         
24698         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24699         
24700         // spans with no attributes - just remove them..
24701         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24702             remove_keep_children = true;
24703         }
24704         
24705         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24706         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24707         
24708         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24709         //    remove_keep_children = true;
24710         //}
24711         
24712         if (remove_keep_children) {
24713             this.cleanUpChildren(node);
24714             // inserts everything just before this node...
24715             while (node.childNodes.length) {
24716                 var cn = node.childNodes[0];
24717                 node.removeChild(cn);
24718                 node.parentNode.insertBefore(cn, node);
24719             }
24720             node.parentNode.removeChild(node);
24721             return;
24722         }
24723         
24724         if (!node.attributes || !node.attributes.length) {
24725             
24726           
24727             
24728             
24729             this.cleanUpChildren(node);
24730             return;
24731         }
24732         
24733         function cleanAttr(n,v)
24734         {
24735             
24736             if (v.match(/^\./) || v.match(/^\//)) {
24737                 return;
24738             }
24739             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24740                 return;
24741             }
24742             if (v.match(/^#/)) {
24743                 return;
24744             }
24745             if (v.match(/^\{/)) { // allow template editing.
24746                 return;
24747             }
24748 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24749             node.removeAttribute(n);
24750             
24751         }
24752         
24753         var cwhite = this.cwhite;
24754         var cblack = this.cblack;
24755             
24756         function cleanStyle(n,v)
24757         {
24758             if (v.match(/expression/)) { //XSS?? should we even bother..
24759                 node.removeAttribute(n);
24760                 return;
24761             }
24762             
24763             var parts = v.split(/;/);
24764             var clean = [];
24765             
24766             Roo.each(parts, function(p) {
24767                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24768                 if (!p.length) {
24769                     return true;
24770                 }
24771                 var l = p.split(':').shift().replace(/\s+/g,'');
24772                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24773                 
24774                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24775 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24776                     //node.removeAttribute(n);
24777                     return true;
24778                 }
24779                 //Roo.log()
24780                 // only allow 'c whitelisted system attributes'
24781                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24782 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24783                     //node.removeAttribute(n);
24784                     return true;
24785                 }
24786                 
24787                 
24788                  
24789                 
24790                 clean.push(p);
24791                 return true;
24792             });
24793             if (clean.length) { 
24794                 node.setAttribute(n, clean.join(';'));
24795             } else {
24796                 node.removeAttribute(n);
24797             }
24798             
24799         }
24800         
24801         
24802         for (var i = node.attributes.length-1; i > -1 ; i--) {
24803             var a = node.attributes[i];
24804             //console.log(a);
24805             
24806             if (a.name.toLowerCase().substr(0,2)=='on')  {
24807                 node.removeAttribute(a.name);
24808                 continue;
24809             }
24810             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24811                 node.removeAttribute(a.name);
24812                 continue;
24813             }
24814             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24815                 cleanAttr(a.name,a.value); // fixme..
24816                 continue;
24817             }
24818             if (a.name == 'style') {
24819                 cleanStyle(a.name,a.value);
24820                 continue;
24821             }
24822             /// clean up MS crap..
24823             // tecnically this should be a list of valid class'es..
24824             
24825             
24826             if (a.name == 'class') {
24827                 if (a.value.match(/^Mso/)) {
24828                     node.removeAttribute('class');
24829                 }
24830                 
24831                 if (a.value.match(/^body$/)) {
24832                     node.removeAttribute('class');
24833                 }
24834                 continue;
24835             }
24836             
24837             // style cleanup!?
24838             // class cleanup?
24839             
24840         }
24841         
24842         
24843         this.cleanUpChildren(node);
24844         
24845         
24846     },
24847     
24848     /**
24849      * Clean up MS wordisms...
24850      */
24851     cleanWord : function(node)
24852     {
24853         if (!node) {
24854             this.cleanWord(this.doc.body);
24855             return;
24856         }
24857         
24858         if(
24859                 node.nodeName == 'SPAN' &&
24860                 !node.hasAttributes() &&
24861                 node.childNodes.length == 1 &&
24862                 node.firstChild.nodeName == "#text"  
24863         ) {
24864             var textNode = node.firstChild;
24865             node.removeChild(textNode);
24866             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24867                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24868             }
24869             node.parentNode.insertBefore(textNode, node);
24870             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24871                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24872             }
24873             node.parentNode.removeChild(node);
24874         }
24875         
24876         if (node.nodeName == "#text") {
24877             // clean up silly Windows -- stuff?
24878             return; 
24879         }
24880         if (node.nodeName == "#comment") {
24881             node.parentNode.removeChild(node);
24882             // clean up silly Windows -- stuff?
24883             return; 
24884         }
24885         
24886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24887             node.parentNode.removeChild(node);
24888             return;
24889         }
24890         //Roo.log(node.tagName);
24891         // remove - but keep children..
24892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24893             //Roo.log('-- removed');
24894             while (node.childNodes.length) {
24895                 var cn = node.childNodes[0];
24896                 node.removeChild(cn);
24897                 node.parentNode.insertBefore(cn, node);
24898                 // move node to parent - and clean it..
24899                 this.cleanWord(cn);
24900             }
24901             node.parentNode.removeChild(node);
24902             /// no need to iterate chidlren = it's got none..
24903             //this.iterateChildren(node, this.cleanWord);
24904             return;
24905         }
24906         // clean styles
24907         if (node.className.length) {
24908             
24909             var cn = node.className.split(/\W+/);
24910             var cna = [];
24911             Roo.each(cn, function(cls) {
24912                 if (cls.match(/Mso[a-zA-Z]+/)) {
24913                     return;
24914                 }
24915                 cna.push(cls);
24916             });
24917             node.className = cna.length ? cna.join(' ') : '';
24918             if (!cna.length) {
24919                 node.removeAttribute("class");
24920             }
24921         }
24922         
24923         if (node.hasAttribute("lang")) {
24924             node.removeAttribute("lang");
24925         }
24926         
24927         if (node.hasAttribute("style")) {
24928             
24929             var styles = node.getAttribute("style").split(";");
24930             var nstyle = [];
24931             Roo.each(styles, function(s) {
24932                 if (!s.match(/:/)) {
24933                     return;
24934                 }
24935                 var kv = s.split(":");
24936                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24937                     return;
24938                 }
24939                 // what ever is left... we allow.
24940                 nstyle.push(s);
24941             });
24942             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24943             if (!nstyle.length) {
24944                 node.removeAttribute('style');
24945             }
24946         }
24947         this.iterateChildren(node, this.cleanWord);
24948         
24949         
24950         
24951     },
24952     /**
24953      * iterateChildren of a Node, calling fn each time, using this as the scole..
24954      * @param {DomNode} node node to iterate children of.
24955      * @param {Function} fn method of this class to call on each item.
24956      */
24957     iterateChildren : function(node, fn)
24958     {
24959         if (!node.childNodes.length) {
24960                 return;
24961         }
24962         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24963            fn.call(this, node.childNodes[i])
24964         }
24965     },
24966     
24967     
24968     /**
24969      * cleanTableWidths.
24970      *
24971      * Quite often pasting from word etc.. results in tables with column and widths.
24972      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24973      *
24974      */
24975     cleanTableWidths : function(node)
24976     {
24977          
24978          
24979         if (!node) {
24980             this.cleanTableWidths(this.doc.body);
24981             return;
24982         }
24983         
24984         // ignore list...
24985         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24986             return; 
24987         }
24988         Roo.log(node.tagName);
24989         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24990             this.iterateChildren(node, this.cleanTableWidths);
24991             return;
24992         }
24993         if (node.hasAttribute('width')) {
24994             node.removeAttribute('width');
24995         }
24996         
24997          
24998         if (node.hasAttribute("style")) {
24999             // pretty basic...
25000             
25001             var styles = node.getAttribute("style").split(";");
25002             var nstyle = [];
25003             Roo.each(styles, function(s) {
25004                 if (!s.match(/:/)) {
25005                     return;
25006                 }
25007                 var kv = s.split(":");
25008                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25009                     return;
25010                 }
25011                 // what ever is left... we allow.
25012                 nstyle.push(s);
25013             });
25014             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25015             if (!nstyle.length) {
25016                 node.removeAttribute('style');
25017             }
25018         }
25019         
25020         this.iterateChildren(node, this.cleanTableWidths);
25021         
25022         
25023     },
25024     
25025     
25026     
25027     
25028     domToHTML : function(currentElement, depth, nopadtext) {
25029         
25030         depth = depth || 0;
25031         nopadtext = nopadtext || false;
25032     
25033         if (!currentElement) {
25034             return this.domToHTML(this.doc.body);
25035         }
25036         
25037         //Roo.log(currentElement);
25038         var j;
25039         var allText = false;
25040         var nodeName = currentElement.nodeName;
25041         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25042         
25043         if  (nodeName == '#text') {
25044             
25045             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25046         }
25047         
25048         
25049         var ret = '';
25050         if (nodeName != 'BODY') {
25051              
25052             var i = 0;
25053             // Prints the node tagName, such as <A>, <IMG>, etc
25054             if (tagName) {
25055                 var attr = [];
25056                 for(i = 0; i < currentElement.attributes.length;i++) {
25057                     // quoting?
25058                     var aname = currentElement.attributes.item(i).name;
25059                     if (!currentElement.attributes.item(i).value.length) {
25060                         continue;
25061                     }
25062                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25063                 }
25064                 
25065                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25066             } 
25067             else {
25068                 
25069                 // eack
25070             }
25071         } else {
25072             tagName = false;
25073         }
25074         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25075             return ret;
25076         }
25077         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25078             nopadtext = true;
25079         }
25080         
25081         
25082         // Traverse the tree
25083         i = 0;
25084         var currentElementChild = currentElement.childNodes.item(i);
25085         var allText = true;
25086         var innerHTML  = '';
25087         lastnode = '';
25088         while (currentElementChild) {
25089             // Formatting code (indent the tree so it looks nice on the screen)
25090             var nopad = nopadtext;
25091             if (lastnode == 'SPAN') {
25092                 nopad  = true;
25093             }
25094             // text
25095             if  (currentElementChild.nodeName == '#text') {
25096                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25097                 toadd = nopadtext ? toadd : toadd.trim();
25098                 if (!nopad && toadd.length > 80) {
25099                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25100                 }
25101                 innerHTML  += toadd;
25102                 
25103                 i++;
25104                 currentElementChild = currentElement.childNodes.item(i);
25105                 lastNode = '';
25106                 continue;
25107             }
25108             allText = false;
25109             
25110             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25111                 
25112             // Recursively traverse the tree structure of the child node
25113             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25114             lastnode = currentElementChild.nodeName;
25115             i++;
25116             currentElementChild=currentElement.childNodes.item(i);
25117         }
25118         
25119         ret += innerHTML;
25120         
25121         if (!allText) {
25122                 // The remaining code is mostly for formatting the tree
25123             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25124         }
25125         
25126         
25127         if (tagName) {
25128             ret+= "</"+tagName+">";
25129         }
25130         return ret;
25131         
25132     },
25133         
25134     applyBlacklists : function()
25135     {
25136         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25137         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25138         
25139         this.white = [];
25140         this.black = [];
25141         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25142             if (b.indexOf(tag) > -1) {
25143                 return;
25144             }
25145             this.white.push(tag);
25146             
25147         }, this);
25148         
25149         Roo.each(w, function(tag) {
25150             if (b.indexOf(tag) > -1) {
25151                 return;
25152             }
25153             if (this.white.indexOf(tag) > -1) {
25154                 return;
25155             }
25156             this.white.push(tag);
25157             
25158         }, this);
25159         
25160         
25161         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25162             if (w.indexOf(tag) > -1) {
25163                 return;
25164             }
25165             this.black.push(tag);
25166             
25167         }, this);
25168         
25169         Roo.each(b, function(tag) {
25170             if (w.indexOf(tag) > -1) {
25171                 return;
25172             }
25173             if (this.black.indexOf(tag) > -1) {
25174                 return;
25175             }
25176             this.black.push(tag);
25177             
25178         }, this);
25179         
25180         
25181         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25182         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25183         
25184         this.cwhite = [];
25185         this.cblack = [];
25186         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25187             if (b.indexOf(tag) > -1) {
25188                 return;
25189             }
25190             this.cwhite.push(tag);
25191             
25192         }, this);
25193         
25194         Roo.each(w, function(tag) {
25195             if (b.indexOf(tag) > -1) {
25196                 return;
25197             }
25198             if (this.cwhite.indexOf(tag) > -1) {
25199                 return;
25200             }
25201             this.cwhite.push(tag);
25202             
25203         }, this);
25204         
25205         
25206         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25207             if (w.indexOf(tag) > -1) {
25208                 return;
25209             }
25210             this.cblack.push(tag);
25211             
25212         }, this);
25213         
25214         Roo.each(b, function(tag) {
25215             if (w.indexOf(tag) > -1) {
25216                 return;
25217             }
25218             if (this.cblack.indexOf(tag) > -1) {
25219                 return;
25220             }
25221             this.cblack.push(tag);
25222             
25223         }, this);
25224     },
25225     
25226     setStylesheets : function(stylesheets)
25227     {
25228         if(typeof(stylesheets) == 'string'){
25229             Roo.get(this.iframe.contentDocument.head).createChild({
25230                 tag : 'link',
25231                 rel : 'stylesheet',
25232                 type : 'text/css',
25233                 href : stylesheets
25234             });
25235             
25236             return;
25237         }
25238         var _this = this;
25239      
25240         Roo.each(stylesheets, function(s) {
25241             if(!s.length){
25242                 return;
25243             }
25244             
25245             Roo.get(_this.iframe.contentDocument.head).createChild({
25246                 tag : 'link',
25247                 rel : 'stylesheet',
25248                 type : 'text/css',
25249                 href : s
25250             });
25251         });
25252
25253         
25254     },
25255     
25256     removeStylesheets : function()
25257     {
25258         var _this = this;
25259         
25260         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25261             s.remove();
25262         });
25263     },
25264     
25265     setStyle : function(style)
25266     {
25267         Roo.get(this.iframe.contentDocument.head).createChild({
25268             tag : 'style',
25269             type : 'text/css',
25270             html : style
25271         });
25272
25273         return;
25274     }
25275     
25276     // hide stuff that is not compatible
25277     /**
25278      * @event blur
25279      * @hide
25280      */
25281     /**
25282      * @event change
25283      * @hide
25284      */
25285     /**
25286      * @event focus
25287      * @hide
25288      */
25289     /**
25290      * @event specialkey
25291      * @hide
25292      */
25293     /**
25294      * @cfg {String} fieldClass @hide
25295      */
25296     /**
25297      * @cfg {String} focusClass @hide
25298      */
25299     /**
25300      * @cfg {String} autoCreate @hide
25301      */
25302     /**
25303      * @cfg {String} inputType @hide
25304      */
25305     /**
25306      * @cfg {String} invalidClass @hide
25307      */
25308     /**
25309      * @cfg {String} invalidText @hide
25310      */
25311     /**
25312      * @cfg {String} msgFx @hide
25313      */
25314     /**
25315      * @cfg {String} validateOnBlur @hide
25316      */
25317 });
25318
25319 Roo.HtmlEditorCore.white = [
25320         'area', 'br', 'img', 'input', 'hr', 'wbr',
25321         
25322        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25323        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25324        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25325        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25326        'table',   'ul',         'xmp', 
25327        
25328        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25329       'thead',   'tr', 
25330      
25331       'dir', 'menu', 'ol', 'ul', 'dl',
25332        
25333       'embed',  'object'
25334 ];
25335
25336
25337 Roo.HtmlEditorCore.black = [
25338     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25339         'applet', // 
25340         'base',   'basefont', 'bgsound', 'blink',  'body', 
25341         'frame',  'frameset', 'head',    'html',   'ilayer', 
25342         'iframe', 'layer',  'link',     'meta',    'object',   
25343         'script', 'style' ,'title',  'xml' // clean later..
25344 ];
25345 Roo.HtmlEditorCore.clean = [
25346     'script', 'style', 'title', 'xml'
25347 ];
25348 Roo.HtmlEditorCore.remove = [
25349     'font'
25350 ];
25351 // attributes..
25352
25353 Roo.HtmlEditorCore.ablack = [
25354     'on'
25355 ];
25356     
25357 Roo.HtmlEditorCore.aclean = [ 
25358     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25359 ];
25360
25361 // protocols..
25362 Roo.HtmlEditorCore.pwhite= [
25363         'http',  'https',  'mailto'
25364 ];
25365
25366 // white listed style attributes.
25367 Roo.HtmlEditorCore.cwhite= [
25368       //  'text-align', /// default is to allow most things..
25369       
25370          
25371 //        'font-size'//??
25372 ];
25373
25374 // black listed style attributes.
25375 Roo.HtmlEditorCore.cblack= [
25376       //  'font-size' -- this can be set by the project 
25377 ];
25378
25379
25380 Roo.HtmlEditorCore.swapCodes   =[ 
25381     [    8211, "--" ], 
25382     [    8212, "--" ], 
25383     [    8216,  "'" ],  
25384     [    8217, "'" ],  
25385     [    8220, '"' ],  
25386     [    8221, '"' ],  
25387     [    8226, "*" ],  
25388     [    8230, "..." ]
25389 ]; 
25390
25391     /*
25392  * - LGPL
25393  *
25394  * HtmlEditor
25395  * 
25396  */
25397
25398 /**
25399  * @class Roo.bootstrap.HtmlEditor
25400  * @extends Roo.bootstrap.TextArea
25401  * Bootstrap HtmlEditor class
25402
25403  * @constructor
25404  * Create a new HtmlEditor
25405  * @param {Object} config The config object
25406  */
25407
25408 Roo.bootstrap.HtmlEditor = function(config){
25409     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25410     if (!this.toolbars) {
25411         this.toolbars = [];
25412     }
25413     
25414     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25415     this.addEvents({
25416             /**
25417              * @event initialize
25418              * Fires when the editor is fully initialized (including the iframe)
25419              * @param {HtmlEditor} this
25420              */
25421             initialize: true,
25422             /**
25423              * @event activate
25424              * Fires when the editor is first receives the focus. Any insertion must wait
25425              * until after this event.
25426              * @param {HtmlEditor} this
25427              */
25428             activate: true,
25429              /**
25430              * @event beforesync
25431              * Fires before the textarea is updated with content from the editor iframe. Return false
25432              * to cancel the sync.
25433              * @param {HtmlEditor} this
25434              * @param {String} html
25435              */
25436             beforesync: true,
25437              /**
25438              * @event beforepush
25439              * Fires before the iframe editor is updated with content from the textarea. Return false
25440              * to cancel the push.
25441              * @param {HtmlEditor} this
25442              * @param {String} html
25443              */
25444             beforepush: true,
25445              /**
25446              * @event sync
25447              * Fires when the textarea is updated with content from the editor iframe.
25448              * @param {HtmlEditor} this
25449              * @param {String} html
25450              */
25451             sync: true,
25452              /**
25453              * @event push
25454              * Fires when the iframe editor is updated with content from the textarea.
25455              * @param {HtmlEditor} this
25456              * @param {String} html
25457              */
25458             push: true,
25459              /**
25460              * @event editmodechange
25461              * Fires when the editor switches edit modes
25462              * @param {HtmlEditor} this
25463              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25464              */
25465             editmodechange: true,
25466             /**
25467              * @event editorevent
25468              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25469              * @param {HtmlEditor} this
25470              */
25471             editorevent: true,
25472             /**
25473              * @event firstfocus
25474              * Fires when on first focus - needed by toolbars..
25475              * @param {HtmlEditor} this
25476              */
25477             firstfocus: true,
25478             /**
25479              * @event autosave
25480              * Auto save the htmlEditor value as a file into Events
25481              * @param {HtmlEditor} this
25482              */
25483             autosave: true,
25484             /**
25485              * @event savedpreview
25486              * preview the saved version of htmlEditor
25487              * @param {HtmlEditor} this
25488              */
25489             savedpreview: true
25490         });
25491 };
25492
25493
25494 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25495     
25496     
25497       /**
25498      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25499      */
25500     toolbars : false,
25501     
25502      /**
25503     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25504     */
25505     btns : [],
25506    
25507      /**
25508      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25509      *                        Roo.resizable.
25510      */
25511     resizable : false,
25512      /**
25513      * @cfg {Number} height (in pixels)
25514      */   
25515     height: 300,
25516    /**
25517      * @cfg {Number} width (in pixels)
25518      */   
25519     width: false,
25520     
25521     /**
25522      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25523      * 
25524      */
25525     stylesheets: false,
25526     
25527     // id of frame..
25528     frameId: false,
25529     
25530     // private properties
25531     validationEvent : false,
25532     deferHeight: true,
25533     initialized : false,
25534     activated : false,
25535     
25536     onFocus : Roo.emptyFn,
25537     iframePad:3,
25538     hideMode:'offsets',
25539     
25540     tbContainer : false,
25541     
25542     bodyCls : '',
25543     
25544     toolbarContainer :function() {
25545         return this.wrap.select('.x-html-editor-tb',true).first();
25546     },
25547
25548     /**
25549      * Protected method that will not generally be called directly. It
25550      * is called when the editor creates its toolbar. Override this method if you need to
25551      * add custom toolbar buttons.
25552      * @param {HtmlEditor} editor
25553      */
25554     createToolbar : function(){
25555         Roo.log('renewing');
25556         Roo.log("create toolbars");
25557         
25558         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25559         this.toolbars[0].render(this.toolbarContainer());
25560         
25561         return;
25562         
25563 //        if (!editor.toolbars || !editor.toolbars.length) {
25564 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25565 //        }
25566 //        
25567 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25568 //            editor.toolbars[i] = Roo.factory(
25569 //                    typeof(editor.toolbars[i]) == 'string' ?
25570 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25571 //                Roo.bootstrap.HtmlEditor);
25572 //            editor.toolbars[i].init(editor);
25573 //        }
25574     },
25575
25576      
25577     // private
25578     onRender : function(ct, position)
25579     {
25580        // Roo.log("Call onRender: " + this.xtype);
25581         var _t = this;
25582         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25583       
25584         this.wrap = this.inputEl().wrap({
25585             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25586         });
25587         
25588         this.editorcore.onRender(ct, position);
25589          
25590         if (this.resizable) {
25591             this.resizeEl = new Roo.Resizable(this.wrap, {
25592                 pinned : true,
25593                 wrap: true,
25594                 dynamic : true,
25595                 minHeight : this.height,
25596                 height: this.height,
25597                 handles : this.resizable,
25598                 width: this.width,
25599                 listeners : {
25600                     resize : function(r, w, h) {
25601                         _t.onResize(w,h); // -something
25602                     }
25603                 }
25604             });
25605             
25606         }
25607         this.createToolbar(this);
25608        
25609         
25610         if(!this.width && this.resizable){
25611             this.setSize(this.wrap.getSize());
25612         }
25613         if (this.resizeEl) {
25614             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25615             // should trigger onReize..
25616         }
25617         
25618     },
25619
25620     // private
25621     onResize : function(w, h)
25622     {
25623         Roo.log('resize: ' +w + ',' + h );
25624         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25625         var ew = false;
25626         var eh = false;
25627         
25628         if(this.inputEl() ){
25629             if(typeof w == 'number'){
25630                 var aw = w - this.wrap.getFrameWidth('lr');
25631                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25632                 ew = aw;
25633             }
25634             if(typeof h == 'number'){
25635                  var tbh = -11;  // fixme it needs to tool bar size!
25636                 for (var i =0; i < this.toolbars.length;i++) {
25637                     // fixme - ask toolbars for heights?
25638                     tbh += this.toolbars[i].el.getHeight();
25639                     //if (this.toolbars[i].footer) {
25640                     //    tbh += this.toolbars[i].footer.el.getHeight();
25641                     //}
25642                 }
25643               
25644                 
25645                 
25646                 
25647                 
25648                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25649                 ah -= 5; // knock a few pixes off for look..
25650                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25651                 var eh = ah;
25652             }
25653         }
25654         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25655         this.editorcore.onResize(ew,eh);
25656         
25657     },
25658
25659     /**
25660      * Toggles the editor between standard and source edit mode.
25661      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25662      */
25663     toggleSourceEdit : function(sourceEditMode)
25664     {
25665         this.editorcore.toggleSourceEdit(sourceEditMode);
25666         
25667         if(this.editorcore.sourceEditMode){
25668             Roo.log('editor - showing textarea');
25669             
25670 //            Roo.log('in');
25671 //            Roo.log(this.syncValue());
25672             this.syncValue();
25673             this.inputEl().removeClass(['hide', 'x-hidden']);
25674             this.inputEl().dom.removeAttribute('tabIndex');
25675             this.inputEl().focus();
25676         }else{
25677             Roo.log('editor - hiding textarea');
25678 //            Roo.log('out')
25679 //            Roo.log(this.pushValue()); 
25680             this.pushValue();
25681             
25682             this.inputEl().addClass(['hide', 'x-hidden']);
25683             this.inputEl().dom.setAttribute('tabIndex', -1);
25684             //this.deferFocus();
25685         }
25686          
25687         if(this.resizable){
25688             this.setSize(this.wrap.getSize());
25689         }
25690         
25691         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25692     },
25693  
25694     // private (for BoxComponent)
25695     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25696
25697     // private (for BoxComponent)
25698     getResizeEl : function(){
25699         return this.wrap;
25700     },
25701
25702     // private (for BoxComponent)
25703     getPositionEl : function(){
25704         return this.wrap;
25705     },
25706
25707     // private
25708     initEvents : function(){
25709         this.originalValue = this.getValue();
25710     },
25711
25712 //    /**
25713 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25714 //     * @method
25715 //     */
25716 //    markInvalid : Roo.emptyFn,
25717 //    /**
25718 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25719 //     * @method
25720 //     */
25721 //    clearInvalid : Roo.emptyFn,
25722
25723     setValue : function(v){
25724         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25725         this.editorcore.pushValue();
25726     },
25727
25728      
25729     // private
25730     deferFocus : function(){
25731         this.focus.defer(10, this);
25732     },
25733
25734     // doc'ed in Field
25735     focus : function(){
25736         this.editorcore.focus();
25737         
25738     },
25739       
25740
25741     // private
25742     onDestroy : function(){
25743         
25744         
25745         
25746         if(this.rendered){
25747             
25748             for (var i =0; i < this.toolbars.length;i++) {
25749                 // fixme - ask toolbars for heights?
25750                 this.toolbars[i].onDestroy();
25751             }
25752             
25753             this.wrap.dom.innerHTML = '';
25754             this.wrap.remove();
25755         }
25756     },
25757
25758     // private
25759     onFirstFocus : function(){
25760         //Roo.log("onFirstFocus");
25761         this.editorcore.onFirstFocus();
25762          for (var i =0; i < this.toolbars.length;i++) {
25763             this.toolbars[i].onFirstFocus();
25764         }
25765         
25766     },
25767     
25768     // private
25769     syncValue : function()
25770     {   
25771         this.editorcore.syncValue();
25772     },
25773     
25774     pushValue : function()
25775     {   
25776         this.editorcore.pushValue();
25777     }
25778      
25779     
25780     // hide stuff that is not compatible
25781     /**
25782      * @event blur
25783      * @hide
25784      */
25785     /**
25786      * @event change
25787      * @hide
25788      */
25789     /**
25790      * @event focus
25791      * @hide
25792      */
25793     /**
25794      * @event specialkey
25795      * @hide
25796      */
25797     /**
25798      * @cfg {String} fieldClass @hide
25799      */
25800     /**
25801      * @cfg {String} focusClass @hide
25802      */
25803     /**
25804      * @cfg {String} autoCreate @hide
25805      */
25806     /**
25807      * @cfg {String} inputType @hide
25808      */
25809      
25810     /**
25811      * @cfg {String} invalidText @hide
25812      */
25813     /**
25814      * @cfg {String} msgFx @hide
25815      */
25816     /**
25817      * @cfg {String} validateOnBlur @hide
25818      */
25819 });
25820  
25821     
25822    
25823    
25824    
25825       
25826 Roo.namespace('Roo.bootstrap.htmleditor');
25827 /**
25828  * @class Roo.bootstrap.HtmlEditorToolbar1
25829  * Basic Toolbar
25830  * 
25831  * @example
25832  * Usage:
25833  *
25834  new Roo.bootstrap.HtmlEditor({
25835     ....
25836     toolbars : [
25837         new Roo.bootstrap.HtmlEditorToolbar1({
25838             disable : { fonts: 1 , format: 1, ..., ... , ...],
25839             btns : [ .... ]
25840         })
25841     }
25842      
25843  * 
25844  * @cfg {Object} disable List of elements to disable..
25845  * @cfg {Array} btns List of additional buttons.
25846  * 
25847  * 
25848  * NEEDS Extra CSS? 
25849  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25850  */
25851  
25852 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25853 {
25854     
25855     Roo.apply(this, config);
25856     
25857     // default disabled, based on 'good practice'..
25858     this.disable = this.disable || {};
25859     Roo.applyIf(this.disable, {
25860         fontSize : true,
25861         colors : true,
25862         specialElements : true
25863     });
25864     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25865     
25866     this.editor = config.editor;
25867     this.editorcore = config.editor.editorcore;
25868     
25869     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25870     
25871     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25872     // dont call parent... till later.
25873 }
25874 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25875      
25876     bar : true,
25877     
25878     editor : false,
25879     editorcore : false,
25880     
25881     
25882     formats : [
25883         "p" ,  
25884         "h1","h2","h3","h4","h5","h6", 
25885         "pre", "code", 
25886         "abbr", "acronym", "address", "cite", "samp", "var",
25887         'div','span'
25888     ],
25889     
25890     onRender : function(ct, position)
25891     {
25892        // Roo.log("Call onRender: " + this.xtype);
25893         
25894        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25895        Roo.log(this.el);
25896        this.el.dom.style.marginBottom = '0';
25897        var _this = this;
25898        var editorcore = this.editorcore;
25899        var editor= this.editor;
25900        
25901        var children = [];
25902        var btn = function(id,cmd , toggle, handler, html){
25903        
25904             var  event = toggle ? 'toggle' : 'click';
25905        
25906             var a = {
25907                 size : 'sm',
25908                 xtype: 'Button',
25909                 xns: Roo.bootstrap,
25910                 //glyphicon : id,
25911                 fa: id,
25912                 cmd : id || cmd,
25913                 enableToggle:toggle !== false,
25914                 html : html || '',
25915                 pressed : toggle ? false : null,
25916                 listeners : {}
25917             };
25918             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25919                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25920             };
25921             children.push(a);
25922             return a;
25923        }
25924        
25925     //    var cb_box = function...
25926         
25927         var style = {
25928                 xtype: 'Button',
25929                 size : 'sm',
25930                 xns: Roo.bootstrap,
25931                 fa : 'font',
25932                 //html : 'submit'
25933                 menu : {
25934                     xtype: 'Menu',
25935                     xns: Roo.bootstrap,
25936                     items:  []
25937                 }
25938         };
25939         Roo.each(this.formats, function(f) {
25940             style.menu.items.push({
25941                 xtype :'MenuItem',
25942                 xns: Roo.bootstrap,
25943                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25944                 tagname : f,
25945                 listeners : {
25946                     click : function()
25947                     {
25948                         editorcore.insertTag(this.tagname);
25949                         editor.focus();
25950                     }
25951                 }
25952                 
25953             });
25954         });
25955         children.push(style);   
25956         
25957         btn('bold',false,true);
25958         btn('italic',false,true);
25959         btn('align-left', 'justifyleft',true);
25960         btn('align-center', 'justifycenter',true);
25961         btn('align-right' , 'justifyright',true);
25962         btn('link', false, false, function(btn) {
25963             //Roo.log("create link?");
25964             var url = prompt(this.createLinkText, this.defaultLinkValue);
25965             if(url && url != 'http:/'+'/'){
25966                 this.editorcore.relayCmd('createlink', url);
25967             }
25968         }),
25969         btn('list','insertunorderedlist',true);
25970         btn('pencil', false,true, function(btn){
25971                 Roo.log(this);
25972                 this.toggleSourceEdit(btn.pressed);
25973         });
25974         
25975         if (this.editor.btns.length > 0) {
25976             for (var i = 0; i<this.editor.btns.length; i++) {
25977                 children.push(this.editor.btns[i]);
25978             }
25979         }
25980         
25981         /*
25982         var cog = {
25983                 xtype: 'Button',
25984                 size : 'sm',
25985                 xns: Roo.bootstrap,
25986                 glyphicon : 'cog',
25987                 //html : 'submit'
25988                 menu : {
25989                     xtype: 'Menu',
25990                     xns: Roo.bootstrap,
25991                     items:  []
25992                 }
25993         };
25994         
25995         cog.menu.items.push({
25996             xtype :'MenuItem',
25997             xns: Roo.bootstrap,
25998             html : Clean styles,
25999             tagname : f,
26000             listeners : {
26001                 click : function()
26002                 {
26003                     editorcore.insertTag(this.tagname);
26004                     editor.focus();
26005                 }
26006             }
26007             
26008         });
26009        */
26010         
26011          
26012        this.xtype = 'NavSimplebar';
26013         
26014         for(var i=0;i< children.length;i++) {
26015             
26016             this.buttons.add(this.addxtypeChild(children[i]));
26017             
26018         }
26019         
26020         editor.on('editorevent', this.updateToolbar, this);
26021     },
26022     onBtnClick : function(id)
26023     {
26024        this.editorcore.relayCmd(id);
26025        this.editorcore.focus();
26026     },
26027     
26028     /**
26029      * Protected method that will not generally be called directly. It triggers
26030      * a toolbar update by reading the markup state of the current selection in the editor.
26031      */
26032     updateToolbar: function(){
26033
26034         if(!this.editorcore.activated){
26035             this.editor.onFirstFocus(); // is this neeed?
26036             return;
26037         }
26038
26039         var btns = this.buttons; 
26040         var doc = this.editorcore.doc;
26041         btns.get('bold').setActive(doc.queryCommandState('bold'));
26042         btns.get('italic').setActive(doc.queryCommandState('italic'));
26043         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26044         
26045         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26046         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26047         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26048         
26049         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26050         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26051          /*
26052         
26053         var ans = this.editorcore.getAllAncestors();
26054         if (this.formatCombo) {
26055             
26056             
26057             var store = this.formatCombo.store;
26058             this.formatCombo.setValue("");
26059             for (var i =0; i < ans.length;i++) {
26060                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26061                     // select it..
26062                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26063                     break;
26064                 }
26065             }
26066         }
26067         
26068         
26069         
26070         // hides menus... - so this cant be on a menu...
26071         Roo.bootstrap.MenuMgr.hideAll();
26072         */
26073         Roo.bootstrap.MenuMgr.hideAll();
26074         //this.editorsyncValue();
26075     },
26076     onFirstFocus: function() {
26077         this.buttons.each(function(item){
26078            item.enable();
26079         });
26080     },
26081     toggleSourceEdit : function(sourceEditMode){
26082         
26083           
26084         if(sourceEditMode){
26085             Roo.log("disabling buttons");
26086            this.buttons.each( function(item){
26087                 if(item.cmd != 'pencil'){
26088                     item.disable();
26089                 }
26090             });
26091           
26092         }else{
26093             Roo.log("enabling buttons");
26094             if(this.editorcore.initialized){
26095                 this.buttons.each( function(item){
26096                     item.enable();
26097                 });
26098             }
26099             
26100         }
26101         Roo.log("calling toggole on editor");
26102         // tell the editor that it's been pressed..
26103         this.editor.toggleSourceEdit(sourceEditMode);
26104        
26105     }
26106 });
26107
26108
26109
26110
26111  
26112 /*
26113  * - LGPL
26114  */
26115
26116 /**
26117  * @class Roo.bootstrap.Markdown
26118  * @extends Roo.bootstrap.TextArea
26119  * Bootstrap Showdown editable area
26120  * @cfg {string} content
26121  * 
26122  * @constructor
26123  * Create a new Showdown
26124  */
26125
26126 Roo.bootstrap.Markdown = function(config){
26127     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26128    
26129 };
26130
26131 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26132     
26133     editing :false,
26134     
26135     initEvents : function()
26136     {
26137         
26138         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26139         this.markdownEl = this.el.createChild({
26140             cls : 'roo-markdown-area'
26141         });
26142         this.inputEl().addClass('d-none');
26143         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26144         this.markdownEl.on('click', this.toggleTextEdit, this);
26145         this.on('blur', this.toggleTextEdit, this);
26146         this.on('specialkey', this.resizeTextArea, this);
26147     },
26148     
26149     toggleTextEdit : function()
26150     {
26151         var sh = this.markdownEl.getHeight();
26152         this.inputEl().addClass('d-none');
26153         this.markdownEl.addClass('d-none');
26154         if (!this.editing) {
26155             // show editor?
26156             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26157             this.inputEl().removeClass('d-none');
26158             this.inputEl().focus();
26159             this.editing = true;
26160             return;
26161         }
26162         // show showdown...
26163         this.updateMarkdown();
26164         this.markdownEl.removeClass('d-none');
26165         this.editing = false;
26166         return;
26167     },
26168     updateMarkdown : function()
26169     {
26170         if (this.getValue() == '') {
26171             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26172             return;
26173         }
26174         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26175     },
26176     
26177     resizeTextArea: function () {
26178         
26179         var sh = 100;
26180         Roo.log([sh, this.getValue().split("\n").length * 30]);
26181         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26182     },
26183     setValue : function(val)
26184     {
26185         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26186         if (!this.editing) {
26187             this.updateMarkdown();
26188         }
26189         
26190     },
26191     focus : function()
26192     {
26193         if (!this.editing) {
26194             this.toggleTextEdit();
26195         }
26196         
26197     }
26198
26199
26200 });
26201 /**
26202  * @class Roo.bootstrap.Table.AbstractSelectionModel
26203  * @extends Roo.util.Observable
26204  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26205  * implemented by descendant classes.  This class should not be directly instantiated.
26206  * @constructor
26207  */
26208 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26209     this.locked = false;
26210     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26211 };
26212
26213
26214 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26215     /** @ignore Called by the grid automatically. Do not call directly. */
26216     init : function(grid){
26217         this.grid = grid;
26218         this.initEvents();
26219     },
26220
26221     /**
26222      * Locks the selections.
26223      */
26224     lock : function(){
26225         this.locked = true;
26226     },
26227
26228     /**
26229      * Unlocks the selections.
26230      */
26231     unlock : function(){
26232         this.locked = false;
26233     },
26234
26235     /**
26236      * Returns true if the selections are locked.
26237      * @return {Boolean}
26238      */
26239     isLocked : function(){
26240         return this.locked;
26241     },
26242     
26243     
26244     initEvents : function ()
26245     {
26246         
26247     }
26248 });
26249 /**
26250  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26251  * @class Roo.bootstrap.Table.RowSelectionModel
26252  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26253  * It supports multiple selections and keyboard selection/navigation. 
26254  * @constructor
26255  * @param {Object} config
26256  */
26257
26258 Roo.bootstrap.Table.RowSelectionModel = function(config){
26259     Roo.apply(this, config);
26260     this.selections = new Roo.util.MixedCollection(false, function(o){
26261         return o.id;
26262     });
26263
26264     this.last = false;
26265     this.lastActive = false;
26266
26267     this.addEvents({
26268         /**
26269              * @event selectionchange
26270              * Fires when the selection changes
26271              * @param {SelectionModel} this
26272              */
26273             "selectionchange" : true,
26274         /**
26275              * @event afterselectionchange
26276              * Fires after the selection changes (eg. by key press or clicking)
26277              * @param {SelectionModel} this
26278              */
26279             "afterselectionchange" : true,
26280         /**
26281              * @event beforerowselect
26282              * Fires when a row is selected being selected, return false to cancel.
26283              * @param {SelectionModel} this
26284              * @param {Number} rowIndex The selected index
26285              * @param {Boolean} keepExisting False if other selections will be cleared
26286              */
26287             "beforerowselect" : true,
26288         /**
26289              * @event rowselect
26290              * Fires when a row is selected.
26291              * @param {SelectionModel} this
26292              * @param {Number} rowIndex The selected index
26293              * @param {Roo.data.Record} r The record
26294              */
26295             "rowselect" : true,
26296         /**
26297              * @event rowdeselect
26298              * Fires when a row is deselected.
26299              * @param {SelectionModel} this
26300              * @param {Number} rowIndex The selected index
26301              */
26302         "rowdeselect" : true
26303     });
26304     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26305     this.locked = false;
26306  };
26307
26308 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26309     /**
26310      * @cfg {Boolean} singleSelect
26311      * True to allow selection of only one row at a time (defaults to false)
26312      */
26313     singleSelect : false,
26314
26315     // private
26316     initEvents : function()
26317     {
26318
26319         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26320         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26321         //}else{ // allow click to work like normal
26322          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26323         //}
26324         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26325         this.grid.on("rowclick", this.handleMouseDown, this);
26326         
26327         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26328             "up" : function(e){
26329                 if(!e.shiftKey){
26330                     this.selectPrevious(e.shiftKey);
26331                 }else if(this.last !== false && this.lastActive !== false){
26332                     var last = this.last;
26333                     this.selectRange(this.last,  this.lastActive-1);
26334                     this.grid.getView().focusRow(this.lastActive);
26335                     if(last !== false){
26336                         this.last = last;
26337                     }
26338                 }else{
26339                     this.selectFirstRow();
26340                 }
26341                 this.fireEvent("afterselectionchange", this);
26342             },
26343             "down" : function(e){
26344                 if(!e.shiftKey){
26345                     this.selectNext(e.shiftKey);
26346                 }else if(this.last !== false && this.lastActive !== false){
26347                     var last = this.last;
26348                     this.selectRange(this.last,  this.lastActive+1);
26349                     this.grid.getView().focusRow(this.lastActive);
26350                     if(last !== false){
26351                         this.last = last;
26352                     }
26353                 }else{
26354                     this.selectFirstRow();
26355                 }
26356                 this.fireEvent("afterselectionchange", this);
26357             },
26358             scope: this
26359         });
26360         this.grid.store.on('load', function(){
26361             this.selections.clear();
26362         },this);
26363         /*
26364         var view = this.grid.view;
26365         view.on("refresh", this.onRefresh, this);
26366         view.on("rowupdated", this.onRowUpdated, this);
26367         view.on("rowremoved", this.onRemove, this);
26368         */
26369     },
26370
26371     // private
26372     onRefresh : function()
26373     {
26374         var ds = this.grid.store, i, v = this.grid.view;
26375         var s = this.selections;
26376         s.each(function(r){
26377             if((i = ds.indexOfId(r.id)) != -1){
26378                 v.onRowSelect(i);
26379             }else{
26380                 s.remove(r);
26381             }
26382         });
26383     },
26384
26385     // private
26386     onRemove : function(v, index, r){
26387         this.selections.remove(r);
26388     },
26389
26390     // private
26391     onRowUpdated : function(v, index, r){
26392         if(this.isSelected(r)){
26393             v.onRowSelect(index);
26394         }
26395     },
26396
26397     /**
26398      * Select records.
26399      * @param {Array} records The records to select
26400      * @param {Boolean} keepExisting (optional) True to keep existing selections
26401      */
26402     selectRecords : function(records, keepExisting)
26403     {
26404         if(!keepExisting){
26405             this.clearSelections();
26406         }
26407             var ds = this.grid.store;
26408         for(var i = 0, len = records.length; i < len; i++){
26409             this.selectRow(ds.indexOf(records[i]), true);
26410         }
26411     },
26412
26413     /**
26414      * Gets the number of selected rows.
26415      * @return {Number}
26416      */
26417     getCount : function(){
26418         return this.selections.length;
26419     },
26420
26421     /**
26422      * Selects the first row in the grid.
26423      */
26424     selectFirstRow : function(){
26425         this.selectRow(0);
26426     },
26427
26428     /**
26429      * Select the last row.
26430      * @param {Boolean} keepExisting (optional) True to keep existing selections
26431      */
26432     selectLastRow : function(keepExisting){
26433         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26434         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26435     },
26436
26437     /**
26438      * Selects the row immediately following the last selected row.
26439      * @param {Boolean} keepExisting (optional) True to keep existing selections
26440      */
26441     selectNext : function(keepExisting)
26442     {
26443             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26444             this.selectRow(this.last+1, keepExisting);
26445             this.grid.getView().focusRow(this.last);
26446         }
26447     },
26448
26449     /**
26450      * Selects the row that precedes the last selected row.
26451      * @param {Boolean} keepExisting (optional) True to keep existing selections
26452      */
26453     selectPrevious : function(keepExisting){
26454         if(this.last){
26455             this.selectRow(this.last-1, keepExisting);
26456             this.grid.getView().focusRow(this.last);
26457         }
26458     },
26459
26460     /**
26461      * Returns the selected records
26462      * @return {Array} Array of selected records
26463      */
26464     getSelections : function(){
26465         return [].concat(this.selections.items);
26466     },
26467
26468     /**
26469      * Returns the first selected record.
26470      * @return {Record}
26471      */
26472     getSelected : function(){
26473         return this.selections.itemAt(0);
26474     },
26475
26476
26477     /**
26478      * Clears all selections.
26479      */
26480     clearSelections : function(fast)
26481     {
26482         if(this.locked) {
26483             return;
26484         }
26485         if(fast !== true){
26486                 var ds = this.grid.store;
26487             var s = this.selections;
26488             s.each(function(r){
26489                 this.deselectRow(ds.indexOfId(r.id));
26490             }, this);
26491             s.clear();
26492         }else{
26493             this.selections.clear();
26494         }
26495         this.last = false;
26496     },
26497
26498
26499     /**
26500      * Selects all rows.
26501      */
26502     selectAll : function(){
26503         if(this.locked) {
26504             return;
26505         }
26506         this.selections.clear();
26507         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26508             this.selectRow(i, true);
26509         }
26510     },
26511
26512     /**
26513      * Returns True if there is a selection.
26514      * @return {Boolean}
26515      */
26516     hasSelection : function(){
26517         return this.selections.length > 0;
26518     },
26519
26520     /**
26521      * Returns True if the specified row is selected.
26522      * @param {Number/Record} record The record or index of the record to check
26523      * @return {Boolean}
26524      */
26525     isSelected : function(index){
26526             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26527         return (r && this.selections.key(r.id) ? true : false);
26528     },
26529
26530     /**
26531      * Returns True if the specified record id is selected.
26532      * @param {String} id The id of record to check
26533      * @return {Boolean}
26534      */
26535     isIdSelected : function(id){
26536         return (this.selections.key(id) ? true : false);
26537     },
26538
26539
26540     // private
26541     handleMouseDBClick : function(e, t){
26542         
26543     },
26544     // private
26545     handleMouseDown : function(e, t)
26546     {
26547             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26548         if(this.isLocked() || rowIndex < 0 ){
26549             return;
26550         };
26551         if(e.shiftKey && this.last !== false){
26552             var last = this.last;
26553             this.selectRange(last, rowIndex, e.ctrlKey);
26554             this.last = last; // reset the last
26555             t.focus();
26556     
26557         }else{
26558             var isSelected = this.isSelected(rowIndex);
26559             //Roo.log("select row:" + rowIndex);
26560             if(isSelected){
26561                 this.deselectRow(rowIndex);
26562             } else {
26563                         this.selectRow(rowIndex, true);
26564             }
26565     
26566             /*
26567                 if(e.button !== 0 && isSelected){
26568                 alert('rowIndex 2: ' + rowIndex);
26569                     view.focusRow(rowIndex);
26570                 }else if(e.ctrlKey && isSelected){
26571                     this.deselectRow(rowIndex);
26572                 }else if(!isSelected){
26573                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26574                     view.focusRow(rowIndex);
26575                 }
26576             */
26577         }
26578         this.fireEvent("afterselectionchange", this);
26579     },
26580     // private
26581     handleDragableRowClick :  function(grid, rowIndex, e) 
26582     {
26583         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26584             this.selectRow(rowIndex, false);
26585             grid.view.focusRow(rowIndex);
26586              this.fireEvent("afterselectionchange", this);
26587         }
26588     },
26589     
26590     /**
26591      * Selects multiple rows.
26592      * @param {Array} rows Array of the indexes of the row to select
26593      * @param {Boolean} keepExisting (optional) True to keep existing selections
26594      */
26595     selectRows : function(rows, keepExisting){
26596         if(!keepExisting){
26597             this.clearSelections();
26598         }
26599         for(var i = 0, len = rows.length; i < len; i++){
26600             this.selectRow(rows[i], true);
26601         }
26602     },
26603
26604     /**
26605      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26606      * @param {Number} startRow The index of the first row in the range
26607      * @param {Number} endRow The index of the last row in the range
26608      * @param {Boolean} keepExisting (optional) True to retain existing selections
26609      */
26610     selectRange : function(startRow, endRow, keepExisting){
26611         if(this.locked) {
26612             return;
26613         }
26614         if(!keepExisting){
26615             this.clearSelections();
26616         }
26617         if(startRow <= endRow){
26618             for(var i = startRow; i <= endRow; i++){
26619                 this.selectRow(i, true);
26620             }
26621         }else{
26622             for(var i = startRow; i >= endRow; i--){
26623                 this.selectRow(i, true);
26624             }
26625         }
26626     },
26627
26628     /**
26629      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26630      * @param {Number} startRow The index of the first row in the range
26631      * @param {Number} endRow The index of the last row in the range
26632      */
26633     deselectRange : function(startRow, endRow, preventViewNotify){
26634         if(this.locked) {
26635             return;
26636         }
26637         for(var i = startRow; i <= endRow; i++){
26638             this.deselectRow(i, preventViewNotify);
26639         }
26640     },
26641
26642     /**
26643      * Selects a row.
26644      * @param {Number} row The index of the row to select
26645      * @param {Boolean} keepExisting (optional) True to keep existing selections
26646      */
26647     selectRow : function(index, keepExisting, preventViewNotify)
26648     {
26649             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26650             return;
26651         }
26652         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26653             if(!keepExisting || this.singleSelect){
26654                 this.clearSelections();
26655             }
26656             
26657             var r = this.grid.store.getAt(index);
26658             //console.log('selectRow - record id :' + r.id);
26659             
26660             this.selections.add(r);
26661             this.last = this.lastActive = index;
26662             if(!preventViewNotify){
26663                 var proxy = new Roo.Element(
26664                                 this.grid.getRowDom(index)
26665                 );
26666                 proxy.addClass('bg-info info');
26667             }
26668             this.fireEvent("rowselect", this, index, r);
26669             this.fireEvent("selectionchange", this);
26670         }
26671     },
26672
26673     /**
26674      * Deselects a row.
26675      * @param {Number} row The index of the row to deselect
26676      */
26677     deselectRow : function(index, preventViewNotify)
26678     {
26679         if(this.locked) {
26680             return;
26681         }
26682         if(this.last == index){
26683             this.last = false;
26684         }
26685         if(this.lastActive == index){
26686             this.lastActive = false;
26687         }
26688         
26689         var r = this.grid.store.getAt(index);
26690         if (!r) {
26691             return;
26692         }
26693         
26694         this.selections.remove(r);
26695         //.console.log('deselectRow - record id :' + r.id);
26696         if(!preventViewNotify){
26697         
26698             var proxy = new Roo.Element(
26699                 this.grid.getRowDom(index)
26700             );
26701             proxy.removeClass('bg-info info');
26702         }
26703         this.fireEvent("rowdeselect", this, index);
26704         this.fireEvent("selectionchange", this);
26705     },
26706
26707     // private
26708     restoreLast : function(){
26709         if(this._last){
26710             this.last = this._last;
26711         }
26712     },
26713
26714     // private
26715     acceptsNav : function(row, col, cm){
26716         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26717     },
26718
26719     // private
26720     onEditorKey : function(field, e){
26721         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26722         if(k == e.TAB){
26723             e.stopEvent();
26724             ed.completeEdit();
26725             if(e.shiftKey){
26726                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26727             }else{
26728                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26729             }
26730         }else if(k == e.ENTER && !e.ctrlKey){
26731             e.stopEvent();
26732             ed.completeEdit();
26733             if(e.shiftKey){
26734                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26735             }else{
26736                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26737             }
26738         }else if(k == e.ESC){
26739             ed.cancelEdit();
26740         }
26741         if(newCell){
26742             g.startEditing(newCell[0], newCell[1]);
26743         }
26744     }
26745 });
26746 /*
26747  * Based on:
26748  * Ext JS Library 1.1.1
26749  * Copyright(c) 2006-2007, Ext JS, LLC.
26750  *
26751  * Originally Released Under LGPL - original licence link has changed is not relivant.
26752  *
26753  * Fork - LGPL
26754  * <script type="text/javascript">
26755  */
26756  
26757 /**
26758  * @class Roo.bootstrap.PagingToolbar
26759  * @extends Roo.bootstrap.NavSimplebar
26760  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26761  * @constructor
26762  * Create a new PagingToolbar
26763  * @param {Object} config The config object
26764  * @param {Roo.data.Store} store
26765  */
26766 Roo.bootstrap.PagingToolbar = function(config)
26767 {
26768     // old args format still supported... - xtype is prefered..
26769         // created from xtype...
26770     
26771     this.ds = config.dataSource;
26772     
26773     if (config.store && !this.ds) {
26774         this.store= Roo.factory(config.store, Roo.data);
26775         this.ds = this.store;
26776         this.ds.xmodule = this.xmodule || false;
26777     }
26778     
26779     this.toolbarItems = [];
26780     if (config.items) {
26781         this.toolbarItems = config.items;
26782     }
26783     
26784     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26785     
26786     this.cursor = 0;
26787     
26788     if (this.ds) { 
26789         this.bind(this.ds);
26790     }
26791     
26792     if (Roo.bootstrap.version == 4) {
26793         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26794     } else {
26795         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26796     }
26797     
26798 };
26799
26800 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26801     /**
26802      * @cfg {Roo.data.Store} dataSource
26803      * The underlying data store providing the paged data
26804      */
26805     /**
26806      * @cfg {String/HTMLElement/Element} container
26807      * container The id or element that will contain the toolbar
26808      */
26809     /**
26810      * @cfg {Boolean} displayInfo
26811      * True to display the displayMsg (defaults to false)
26812      */
26813     /**
26814      * @cfg {Number} pageSize
26815      * The number of records to display per page (defaults to 20)
26816      */
26817     pageSize: 20,
26818     /**
26819      * @cfg {String} displayMsg
26820      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26821      */
26822     displayMsg : 'Displaying {0} - {1} of {2}',
26823     /**
26824      * @cfg {String} emptyMsg
26825      * The message to display when no records are found (defaults to "No data to display")
26826      */
26827     emptyMsg : 'No data to display',
26828     /**
26829      * Customizable piece of the default paging text (defaults to "Page")
26830      * @type String
26831      */
26832     beforePageText : "Page",
26833     /**
26834      * Customizable piece of the default paging text (defaults to "of %0")
26835      * @type String
26836      */
26837     afterPageText : "of {0}",
26838     /**
26839      * Customizable piece of the default paging text (defaults to "First Page")
26840      * @type String
26841      */
26842     firstText : "First Page",
26843     /**
26844      * Customizable piece of the default paging text (defaults to "Previous Page")
26845      * @type String
26846      */
26847     prevText : "Previous Page",
26848     /**
26849      * Customizable piece of the default paging text (defaults to "Next Page")
26850      * @type String
26851      */
26852     nextText : "Next Page",
26853     /**
26854      * Customizable piece of the default paging text (defaults to "Last Page")
26855      * @type String
26856      */
26857     lastText : "Last Page",
26858     /**
26859      * Customizable piece of the default paging text (defaults to "Refresh")
26860      * @type String
26861      */
26862     refreshText : "Refresh",
26863
26864     buttons : false,
26865     // private
26866     onRender : function(ct, position) 
26867     {
26868         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26869         this.navgroup.parentId = this.id;
26870         this.navgroup.onRender(this.el, null);
26871         // add the buttons to the navgroup
26872         
26873         if(this.displayInfo){
26874             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26875             this.displayEl = this.el.select('.x-paging-info', true).first();
26876 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26877 //            this.displayEl = navel.el.select('span',true).first();
26878         }
26879         
26880         var _this = this;
26881         
26882         if(this.buttons){
26883             Roo.each(_this.buttons, function(e){ // this might need to use render????
26884                Roo.factory(e).render(_this.el);
26885             });
26886         }
26887             
26888         Roo.each(_this.toolbarItems, function(e) {
26889             _this.navgroup.addItem(e);
26890         });
26891         
26892         
26893         this.first = this.navgroup.addItem({
26894             tooltip: this.firstText,
26895             cls: "prev btn-outline-secondary",
26896             html : ' <i class="fa fa-step-backward"></i>',
26897             disabled: true,
26898             preventDefault: true,
26899             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26900         });
26901         
26902         this.prev =  this.navgroup.addItem({
26903             tooltip: this.prevText,
26904             cls: "prev btn-outline-secondary",
26905             html : ' <i class="fa fa-backward"></i>',
26906             disabled: true,
26907             preventDefault: true,
26908             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26909         });
26910     //this.addSeparator();
26911         
26912         
26913         var field = this.navgroup.addItem( {
26914             tagtype : 'span',
26915             cls : 'x-paging-position  btn-outline-secondary',
26916              disabled: true,
26917             html : this.beforePageText  +
26918                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26919                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26920          } ); //?? escaped?
26921         
26922         this.field = field.el.select('input', true).first();
26923         this.field.on("keydown", this.onPagingKeydown, this);
26924         this.field.on("focus", function(){this.dom.select();});
26925     
26926     
26927         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26928         //this.field.setHeight(18);
26929         //this.addSeparator();
26930         this.next = this.navgroup.addItem({
26931             tooltip: this.nextText,
26932             cls: "next btn-outline-secondary",
26933             html : ' <i class="fa fa-forward"></i>',
26934             disabled: true,
26935             preventDefault: true,
26936             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26937         });
26938         this.last = this.navgroup.addItem({
26939             tooltip: this.lastText,
26940             html : ' <i class="fa fa-step-forward"></i>',
26941             cls: "next btn-outline-secondary",
26942             disabled: true,
26943             preventDefault: true,
26944             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26945         });
26946     //this.addSeparator();
26947         this.loading = this.navgroup.addItem({
26948             tooltip: this.refreshText,
26949             cls: "btn-outline-secondary",
26950             html : ' <i class="fa fa-refresh"></i>',
26951             preventDefault: true,
26952             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26953         });
26954         
26955     },
26956
26957     // private
26958     updateInfo : function(){
26959         if(this.displayEl){
26960             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26961             var msg = count == 0 ?
26962                 this.emptyMsg :
26963                 String.format(
26964                     this.displayMsg,
26965                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26966                 );
26967             this.displayEl.update(msg);
26968         }
26969     },
26970
26971     // private
26972     onLoad : function(ds, r, o)
26973     {
26974         this.cursor = o.params.start ? o.params.start : 0;
26975         
26976         var d = this.getPageData(),
26977             ap = d.activePage,
26978             ps = d.pages;
26979         
26980         
26981         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26982         this.field.dom.value = ap;
26983         this.first.setDisabled(ap == 1);
26984         this.prev.setDisabled(ap == 1);
26985         this.next.setDisabled(ap == ps);
26986         this.last.setDisabled(ap == ps);
26987         this.loading.enable();
26988         this.updateInfo();
26989     },
26990
26991     // private
26992     getPageData : function(){
26993         var total = this.ds.getTotalCount();
26994         return {
26995             total : total,
26996             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26997             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26998         };
26999     },
27000
27001     // private
27002     onLoadError : function(){
27003         this.loading.enable();
27004     },
27005
27006     // private
27007     onPagingKeydown : function(e){
27008         var k = e.getKey();
27009         var d = this.getPageData();
27010         if(k == e.RETURN){
27011             var v = this.field.dom.value, pageNum;
27012             if(!v || isNaN(pageNum = parseInt(v, 10))){
27013                 this.field.dom.value = d.activePage;
27014                 return;
27015             }
27016             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27017             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27018             e.stopEvent();
27019         }
27020         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))
27021         {
27022           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27023           this.field.dom.value = pageNum;
27024           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27025           e.stopEvent();
27026         }
27027         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27028         {
27029           var v = this.field.dom.value, pageNum; 
27030           var increment = (e.shiftKey) ? 10 : 1;
27031           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27032                 increment *= -1;
27033           }
27034           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27035             this.field.dom.value = d.activePage;
27036             return;
27037           }
27038           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27039           {
27040             this.field.dom.value = parseInt(v, 10) + increment;
27041             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27042             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27043           }
27044           e.stopEvent();
27045         }
27046     },
27047
27048     // private
27049     beforeLoad : function(){
27050         if(this.loading){
27051             this.loading.disable();
27052         }
27053     },
27054
27055     // private
27056     onClick : function(which){
27057         
27058         var ds = this.ds;
27059         if (!ds) {
27060             return;
27061         }
27062         
27063         switch(which){
27064             case "first":
27065                 ds.load({params:{start: 0, limit: this.pageSize}});
27066             break;
27067             case "prev":
27068                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27069             break;
27070             case "next":
27071                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27072             break;
27073             case "last":
27074                 var total = ds.getTotalCount();
27075                 var extra = total % this.pageSize;
27076                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27077                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27078             break;
27079             case "refresh":
27080                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27081             break;
27082         }
27083     },
27084
27085     /**
27086      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27087      * @param {Roo.data.Store} store The data store to unbind
27088      */
27089     unbind : function(ds){
27090         ds.un("beforeload", this.beforeLoad, this);
27091         ds.un("load", this.onLoad, this);
27092         ds.un("loadexception", this.onLoadError, this);
27093         ds.un("remove", this.updateInfo, this);
27094         ds.un("add", this.updateInfo, this);
27095         this.ds = undefined;
27096     },
27097
27098     /**
27099      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27100      * @param {Roo.data.Store} store The data store to bind
27101      */
27102     bind : function(ds){
27103         ds.on("beforeload", this.beforeLoad, this);
27104         ds.on("load", this.onLoad, this);
27105         ds.on("loadexception", this.onLoadError, this);
27106         ds.on("remove", this.updateInfo, this);
27107         ds.on("add", this.updateInfo, this);
27108         this.ds = ds;
27109     }
27110 });/*
27111  * - LGPL
27112  *
27113  * element
27114  * 
27115  */
27116
27117 /**
27118  * @class Roo.bootstrap.MessageBar
27119  * @extends Roo.bootstrap.Component
27120  * Bootstrap MessageBar class
27121  * @cfg {String} html contents of the MessageBar
27122  * @cfg {String} weight (info | success | warning | danger) default info
27123  * @cfg {String} beforeClass insert the bar before the given class
27124  * @cfg {Boolean} closable (true | false) default false
27125  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27126  * 
27127  * @constructor
27128  * Create a new Element
27129  * @param {Object} config The config object
27130  */
27131
27132 Roo.bootstrap.MessageBar = function(config){
27133     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27134 };
27135
27136 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27137     
27138     html: '',
27139     weight: 'info',
27140     closable: false,
27141     fixed: false,
27142     beforeClass: 'bootstrap-sticky-wrap',
27143     
27144     getAutoCreate : function(){
27145         
27146         var cfg = {
27147             tag: 'div',
27148             cls: 'alert alert-dismissable alert-' + this.weight,
27149             cn: [
27150                 {
27151                     tag: 'span',
27152                     cls: 'message',
27153                     html: this.html || ''
27154                 }
27155             ]
27156         };
27157         
27158         if(this.fixed){
27159             cfg.cls += ' alert-messages-fixed';
27160         }
27161         
27162         if(this.closable){
27163             cfg.cn.push({
27164                 tag: 'button',
27165                 cls: 'close',
27166                 html: 'x'
27167             });
27168         }
27169         
27170         return cfg;
27171     },
27172     
27173     onRender : function(ct, position)
27174     {
27175         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27176         
27177         if(!this.el){
27178             var cfg = Roo.apply({},  this.getAutoCreate());
27179             cfg.id = Roo.id();
27180             
27181             if (this.cls) {
27182                 cfg.cls += ' ' + this.cls;
27183             }
27184             if (this.style) {
27185                 cfg.style = this.style;
27186             }
27187             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27188             
27189             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27190         }
27191         
27192         this.el.select('>button.close').on('click', this.hide, this);
27193         
27194     },
27195     
27196     show : function()
27197     {
27198         if (!this.rendered) {
27199             this.render();
27200         }
27201         
27202         this.el.show();
27203         
27204         this.fireEvent('show', this);
27205         
27206     },
27207     
27208     hide : function()
27209     {
27210         if (!this.rendered) {
27211             this.render();
27212         }
27213         
27214         this.el.hide();
27215         
27216         this.fireEvent('hide', this);
27217     },
27218     
27219     update : function()
27220     {
27221 //        var e = this.el.dom.firstChild;
27222 //        
27223 //        if(this.closable){
27224 //            e = e.nextSibling;
27225 //        }
27226 //        
27227 //        e.data = this.html || '';
27228
27229         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27230     }
27231    
27232 });
27233
27234  
27235
27236      /*
27237  * - LGPL
27238  *
27239  * Graph
27240  * 
27241  */
27242
27243
27244 /**
27245  * @class Roo.bootstrap.Graph
27246  * @extends Roo.bootstrap.Component
27247  * Bootstrap Graph class
27248 > Prameters
27249  -sm {number} sm 4
27250  -md {number} md 5
27251  @cfg {String} graphtype  bar | vbar | pie
27252  @cfg {number} g_x coodinator | centre x (pie)
27253  @cfg {number} g_y coodinator | centre y (pie)
27254  @cfg {number} g_r radius (pie)
27255  @cfg {number} g_height height of the chart (respected by all elements in the set)
27256  @cfg {number} g_width width of the chart (respected by all elements in the set)
27257  @cfg {Object} title The title of the chart
27258     
27259  -{Array}  values
27260  -opts (object) options for the chart 
27261      o {
27262      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27263      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27264      o vgutter (number)
27265      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.
27266      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27267      o to
27268      o stretch (boolean)
27269      o }
27270  -opts (object) options for the pie
27271      o{
27272      o cut
27273      o startAngle (number)
27274      o endAngle (number)
27275      } 
27276  *
27277  * @constructor
27278  * Create a new Input
27279  * @param {Object} config The config object
27280  */
27281
27282 Roo.bootstrap.Graph = function(config){
27283     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27284     
27285     this.addEvents({
27286         // img events
27287         /**
27288          * @event click
27289          * The img click event for the img.
27290          * @param {Roo.EventObject} e
27291          */
27292         "click" : true
27293     });
27294 };
27295
27296 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27297     
27298     sm: 4,
27299     md: 5,
27300     graphtype: 'bar',
27301     g_height: 250,
27302     g_width: 400,
27303     g_x: 50,
27304     g_y: 50,
27305     g_r: 30,
27306     opts:{
27307         //g_colors: this.colors,
27308         g_type: 'soft',
27309         g_gutter: '20%'
27310
27311     },
27312     title : false,
27313
27314     getAutoCreate : function(){
27315         
27316         var cfg = {
27317             tag: 'div',
27318             html : null
27319         };
27320         
27321         
27322         return  cfg;
27323     },
27324
27325     onRender : function(ct,position){
27326         
27327         
27328         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27329         
27330         if (typeof(Raphael) == 'undefined') {
27331             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27332             return;
27333         }
27334         
27335         this.raphael = Raphael(this.el.dom);
27336         
27337                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27339                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27340                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27341                 /*
27342                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27343                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27344                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27345                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27346                 
27347                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27348                 r.barchart(330, 10, 300, 220, data1);
27349                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27350                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27351                 */
27352                 
27353                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27354                 // r.barchart(30, 30, 560, 250,  xdata, {
27355                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27356                 //     axis : "0 0 1 1",
27357                 //     axisxlabels :  xdata
27358                 //     //yvalues : cols,
27359                    
27360                 // });
27361 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27362 //        
27363 //        this.load(null,xdata,{
27364 //                axis : "0 0 1 1",
27365 //                axisxlabels :  xdata
27366 //                });
27367
27368     },
27369
27370     load : function(graphtype,xdata,opts)
27371     {
27372         this.raphael.clear();
27373         if(!graphtype) {
27374             graphtype = this.graphtype;
27375         }
27376         if(!opts){
27377             opts = this.opts;
27378         }
27379         var r = this.raphael,
27380             fin = function () {
27381                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27382             },
27383             fout = function () {
27384                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27385             },
27386             pfin = function() {
27387                 this.sector.stop();
27388                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27389
27390                 if (this.label) {
27391                     this.label[0].stop();
27392                     this.label[0].attr({ r: 7.5 });
27393                     this.label[1].attr({ "font-weight": 800 });
27394                 }
27395             },
27396             pfout = function() {
27397                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27398
27399                 if (this.label) {
27400                     this.label[0].animate({ r: 5 }, 500, "bounce");
27401                     this.label[1].attr({ "font-weight": 400 });
27402                 }
27403             };
27404
27405         switch(graphtype){
27406             case 'bar':
27407                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27408                 break;
27409             case 'hbar':
27410                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27411                 break;
27412             case 'pie':
27413 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27414 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27415 //            
27416                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27417                 
27418                 break;
27419
27420         }
27421         
27422         if(this.title){
27423             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27424         }
27425         
27426     },
27427     
27428     setTitle: function(o)
27429     {
27430         this.title = o;
27431     },
27432     
27433     initEvents: function() {
27434         
27435         if(!this.href){
27436             this.el.on('click', this.onClick, this);
27437         }
27438     },
27439     
27440     onClick : function(e)
27441     {
27442         Roo.log('img onclick');
27443         this.fireEvent('click', this, e);
27444     }
27445    
27446 });
27447
27448  
27449 /*
27450  * - LGPL
27451  *
27452  * numberBox
27453  * 
27454  */
27455 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27456
27457 /**
27458  * @class Roo.bootstrap.dash.NumberBox
27459  * @extends Roo.bootstrap.Component
27460  * Bootstrap NumberBox class
27461  * @cfg {String} headline Box headline
27462  * @cfg {String} content Box content
27463  * @cfg {String} icon Box icon
27464  * @cfg {String} footer Footer text
27465  * @cfg {String} fhref Footer href
27466  * 
27467  * @constructor
27468  * Create a new NumberBox
27469  * @param {Object} config The config object
27470  */
27471
27472
27473 Roo.bootstrap.dash.NumberBox = function(config){
27474     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27475     
27476 };
27477
27478 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27479     
27480     headline : '',
27481     content : '',
27482     icon : '',
27483     footer : '',
27484     fhref : '',
27485     ficon : '',
27486     
27487     getAutoCreate : function(){
27488         
27489         var cfg = {
27490             tag : 'div',
27491             cls : 'small-box ',
27492             cn : [
27493                 {
27494                     tag : 'div',
27495                     cls : 'inner',
27496                     cn :[
27497                         {
27498                             tag : 'h3',
27499                             cls : 'roo-headline',
27500                             html : this.headline
27501                         },
27502                         {
27503                             tag : 'p',
27504                             cls : 'roo-content',
27505                             html : this.content
27506                         }
27507                     ]
27508                 }
27509             ]
27510         };
27511         
27512         if(this.icon){
27513             cfg.cn.push({
27514                 tag : 'div',
27515                 cls : 'icon',
27516                 cn :[
27517                     {
27518                         tag : 'i',
27519                         cls : 'ion ' + this.icon
27520                     }
27521                 ]
27522             });
27523         }
27524         
27525         if(this.footer){
27526             var footer = {
27527                 tag : 'a',
27528                 cls : 'small-box-footer',
27529                 href : this.fhref || '#',
27530                 html : this.footer
27531             };
27532             
27533             cfg.cn.push(footer);
27534             
27535         }
27536         
27537         return  cfg;
27538     },
27539
27540     onRender : function(ct,position){
27541         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27542
27543
27544        
27545                 
27546     },
27547
27548     setHeadline: function (value)
27549     {
27550         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27551     },
27552     
27553     setFooter: function (value, href)
27554     {
27555         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27556         
27557         if(href){
27558             this.el.select('a.small-box-footer',true).first().attr('href', href);
27559         }
27560         
27561     },
27562
27563     setContent: function (value)
27564     {
27565         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27566     },
27567
27568     initEvents: function() 
27569     {   
27570         
27571     }
27572     
27573 });
27574
27575  
27576 /*
27577  * - LGPL
27578  *
27579  * TabBox
27580  * 
27581  */
27582 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27583
27584 /**
27585  * @class Roo.bootstrap.dash.TabBox
27586  * @extends Roo.bootstrap.Component
27587  * Bootstrap TabBox class
27588  * @cfg {String} title Title of the TabBox
27589  * @cfg {String} icon Icon of the TabBox
27590  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27591  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27592  * 
27593  * @constructor
27594  * Create a new TabBox
27595  * @param {Object} config The config object
27596  */
27597
27598
27599 Roo.bootstrap.dash.TabBox = function(config){
27600     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27601     this.addEvents({
27602         // raw events
27603         /**
27604          * @event addpane
27605          * When a pane is added
27606          * @param {Roo.bootstrap.dash.TabPane} pane
27607          */
27608         "addpane" : true,
27609         /**
27610          * @event activatepane
27611          * When a pane is activated
27612          * @param {Roo.bootstrap.dash.TabPane} pane
27613          */
27614         "activatepane" : true
27615         
27616          
27617     });
27618     
27619     this.panes = [];
27620 };
27621
27622 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27623
27624     title : '',
27625     icon : false,
27626     showtabs : true,
27627     tabScrollable : false,
27628     
27629     getChildContainer : function()
27630     {
27631         return this.el.select('.tab-content', true).first();
27632     },
27633     
27634     getAutoCreate : function(){
27635         
27636         var header = {
27637             tag: 'li',
27638             cls: 'pull-left header',
27639             html: this.title,
27640             cn : []
27641         };
27642         
27643         if(this.icon){
27644             header.cn.push({
27645                 tag: 'i',
27646                 cls: 'fa ' + this.icon
27647             });
27648         }
27649         
27650         var h = {
27651             tag: 'ul',
27652             cls: 'nav nav-tabs pull-right',
27653             cn: [
27654                 header
27655             ]
27656         };
27657         
27658         if(this.tabScrollable){
27659             h = {
27660                 tag: 'div',
27661                 cls: 'tab-header',
27662                 cn: [
27663                     {
27664                         tag: 'ul',
27665                         cls: 'nav nav-tabs pull-right',
27666                         cn: [
27667                             header
27668                         ]
27669                     }
27670                 ]
27671             };
27672         }
27673         
27674         var cfg = {
27675             tag: 'div',
27676             cls: 'nav-tabs-custom',
27677             cn: [
27678                 h,
27679                 {
27680                     tag: 'div',
27681                     cls: 'tab-content no-padding',
27682                     cn: []
27683                 }
27684             ]
27685         };
27686
27687         return  cfg;
27688     },
27689     initEvents : function()
27690     {
27691         //Roo.log('add add pane handler');
27692         this.on('addpane', this.onAddPane, this);
27693     },
27694      /**
27695      * Updates the box title
27696      * @param {String} html to set the title to.
27697      */
27698     setTitle : function(value)
27699     {
27700         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27701     },
27702     onAddPane : function(pane)
27703     {
27704         this.panes.push(pane);
27705         //Roo.log('addpane');
27706         //Roo.log(pane);
27707         // tabs are rendere left to right..
27708         if(!this.showtabs){
27709             return;
27710         }
27711         
27712         var ctr = this.el.select('.nav-tabs', true).first();
27713          
27714          
27715         var existing = ctr.select('.nav-tab',true);
27716         var qty = existing.getCount();;
27717         
27718         
27719         var tab = ctr.createChild({
27720             tag : 'li',
27721             cls : 'nav-tab' + (qty ? '' : ' active'),
27722             cn : [
27723                 {
27724                     tag : 'a',
27725                     href:'#',
27726                     html : pane.title
27727                 }
27728             ]
27729         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27730         pane.tab = tab;
27731         
27732         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27733         if (!qty) {
27734             pane.el.addClass('active');
27735         }
27736         
27737                 
27738     },
27739     onTabClick : function(ev,un,ob,pane)
27740     {
27741         //Roo.log('tab - prev default');
27742         ev.preventDefault();
27743         
27744         
27745         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27746         pane.tab.addClass('active');
27747         //Roo.log(pane.title);
27748         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27749         // technically we should have a deactivate event.. but maybe add later.
27750         // and it should not de-activate the selected tab...
27751         this.fireEvent('activatepane', pane);
27752         pane.el.addClass('active');
27753         pane.fireEvent('activate');
27754         
27755         
27756     },
27757     
27758     getActivePane : function()
27759     {
27760         var r = false;
27761         Roo.each(this.panes, function(p) {
27762             if(p.el.hasClass('active')){
27763                 r = p;
27764                 return false;
27765             }
27766             
27767             return;
27768         });
27769         
27770         return r;
27771     }
27772     
27773     
27774 });
27775
27776  
27777 /*
27778  * - LGPL
27779  *
27780  * Tab pane
27781  * 
27782  */
27783 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27784 /**
27785  * @class Roo.bootstrap.TabPane
27786  * @extends Roo.bootstrap.Component
27787  * Bootstrap TabPane class
27788  * @cfg {Boolean} active (false | true) Default false
27789  * @cfg {String} title title of panel
27790
27791  * 
27792  * @constructor
27793  * Create a new TabPane
27794  * @param {Object} config The config object
27795  */
27796
27797 Roo.bootstrap.dash.TabPane = function(config){
27798     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27799     
27800     this.addEvents({
27801         // raw events
27802         /**
27803          * @event activate
27804          * When a pane is activated
27805          * @param {Roo.bootstrap.dash.TabPane} pane
27806          */
27807         "activate" : true
27808          
27809     });
27810 };
27811
27812 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27813     
27814     active : false,
27815     title : '',
27816     
27817     // the tabBox that this is attached to.
27818     tab : false,
27819      
27820     getAutoCreate : function() 
27821     {
27822         var cfg = {
27823             tag: 'div',
27824             cls: 'tab-pane'
27825         };
27826         
27827         if(this.active){
27828             cfg.cls += ' active';
27829         }
27830         
27831         return cfg;
27832     },
27833     initEvents  : function()
27834     {
27835         //Roo.log('trigger add pane handler');
27836         this.parent().fireEvent('addpane', this)
27837     },
27838     
27839      /**
27840      * Updates the tab title 
27841      * @param {String} html to set the title to.
27842      */
27843     setTitle: function(str)
27844     {
27845         if (!this.tab) {
27846             return;
27847         }
27848         this.title = str;
27849         this.tab.select('a', true).first().dom.innerHTML = str;
27850         
27851     }
27852     
27853     
27854     
27855 });
27856
27857  
27858
27859
27860  /*
27861  * - LGPL
27862  *
27863  * menu
27864  * 
27865  */
27866 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27867
27868 /**
27869  * @class Roo.bootstrap.menu.Menu
27870  * @extends Roo.bootstrap.Component
27871  * Bootstrap Menu class - container for Menu
27872  * @cfg {String} html Text of the menu
27873  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27874  * @cfg {String} icon Font awesome icon
27875  * @cfg {String} pos Menu align to (top | bottom) default bottom
27876  * 
27877  * 
27878  * @constructor
27879  * Create a new Menu
27880  * @param {Object} config The config object
27881  */
27882
27883
27884 Roo.bootstrap.menu.Menu = function(config){
27885     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27886     
27887     this.addEvents({
27888         /**
27889          * @event beforeshow
27890          * Fires before this menu is displayed
27891          * @param {Roo.bootstrap.menu.Menu} this
27892          */
27893         beforeshow : true,
27894         /**
27895          * @event beforehide
27896          * Fires before this menu is hidden
27897          * @param {Roo.bootstrap.menu.Menu} this
27898          */
27899         beforehide : true,
27900         /**
27901          * @event show
27902          * Fires after this menu is displayed
27903          * @param {Roo.bootstrap.menu.Menu} this
27904          */
27905         show : true,
27906         /**
27907          * @event hide
27908          * Fires after this menu is hidden
27909          * @param {Roo.bootstrap.menu.Menu} this
27910          */
27911         hide : true,
27912         /**
27913          * @event click
27914          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27915          * @param {Roo.bootstrap.menu.Menu} this
27916          * @param {Roo.EventObject} e
27917          */
27918         click : true
27919     });
27920     
27921 };
27922
27923 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27924     
27925     submenu : false,
27926     html : '',
27927     weight : 'default',
27928     icon : false,
27929     pos : 'bottom',
27930     
27931     
27932     getChildContainer : function() {
27933         if(this.isSubMenu){
27934             return this.el;
27935         }
27936         
27937         return this.el.select('ul.dropdown-menu', true).first();  
27938     },
27939     
27940     getAutoCreate : function()
27941     {
27942         var text = [
27943             {
27944                 tag : 'span',
27945                 cls : 'roo-menu-text',
27946                 html : this.html
27947             }
27948         ];
27949         
27950         if(this.icon){
27951             text.unshift({
27952                 tag : 'i',
27953                 cls : 'fa ' + this.icon
27954             })
27955         }
27956         
27957         
27958         var cfg = {
27959             tag : 'div',
27960             cls : 'btn-group',
27961             cn : [
27962                 {
27963                     tag : 'button',
27964                     cls : 'dropdown-button btn btn-' + this.weight,
27965                     cn : text
27966                 },
27967                 {
27968                     tag : 'button',
27969                     cls : 'dropdown-toggle btn btn-' + this.weight,
27970                     cn : [
27971                         {
27972                             tag : 'span',
27973                             cls : 'caret'
27974                         }
27975                     ]
27976                 },
27977                 {
27978                     tag : 'ul',
27979                     cls : 'dropdown-menu'
27980                 }
27981             ]
27982             
27983         };
27984         
27985         if(this.pos == 'top'){
27986             cfg.cls += ' dropup';
27987         }
27988         
27989         if(this.isSubMenu){
27990             cfg = {
27991                 tag : 'ul',
27992                 cls : 'dropdown-menu'
27993             }
27994         }
27995         
27996         return cfg;
27997     },
27998     
27999     onRender : function(ct, position)
28000     {
28001         this.isSubMenu = ct.hasClass('dropdown-submenu');
28002         
28003         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28004     },
28005     
28006     initEvents : function() 
28007     {
28008         if(this.isSubMenu){
28009             return;
28010         }
28011         
28012         this.hidden = true;
28013         
28014         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28015         this.triggerEl.on('click', this.onTriggerPress, this);
28016         
28017         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28018         this.buttonEl.on('click', this.onClick, this);
28019         
28020     },
28021     
28022     list : function()
28023     {
28024         if(this.isSubMenu){
28025             return this.el;
28026         }
28027         
28028         return this.el.select('ul.dropdown-menu', true).first();
28029     },
28030     
28031     onClick : function(e)
28032     {
28033         this.fireEvent("click", this, e);
28034     },
28035     
28036     onTriggerPress  : function(e)
28037     {   
28038         if (this.isVisible()) {
28039             this.hide();
28040         } else {
28041             this.show();
28042         }
28043     },
28044     
28045     isVisible : function(){
28046         return !this.hidden;
28047     },
28048     
28049     show : function()
28050     {
28051         this.fireEvent("beforeshow", this);
28052         
28053         this.hidden = false;
28054         this.el.addClass('open');
28055         
28056         Roo.get(document).on("mouseup", this.onMouseUp, this);
28057         
28058         this.fireEvent("show", this);
28059         
28060         
28061     },
28062     
28063     hide : function()
28064     {
28065         this.fireEvent("beforehide", this);
28066         
28067         this.hidden = true;
28068         this.el.removeClass('open');
28069         
28070         Roo.get(document).un("mouseup", this.onMouseUp);
28071         
28072         this.fireEvent("hide", this);
28073     },
28074     
28075     onMouseUp : function()
28076     {
28077         this.hide();
28078     }
28079     
28080 });
28081
28082  
28083  /*
28084  * - LGPL
28085  *
28086  * menu item
28087  * 
28088  */
28089 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28090
28091 /**
28092  * @class Roo.bootstrap.menu.Item
28093  * @extends Roo.bootstrap.Component
28094  * Bootstrap MenuItem class
28095  * @cfg {Boolean} submenu (true | false) default false
28096  * @cfg {String} html text of the item
28097  * @cfg {String} href the link
28098  * @cfg {Boolean} disable (true | false) default false
28099  * @cfg {Boolean} preventDefault (true | false) default true
28100  * @cfg {String} icon Font awesome icon
28101  * @cfg {String} pos Submenu align to (left | right) default right 
28102  * 
28103  * 
28104  * @constructor
28105  * Create a new Item
28106  * @param {Object} config The config object
28107  */
28108
28109
28110 Roo.bootstrap.menu.Item = function(config){
28111     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28112     this.addEvents({
28113         /**
28114          * @event mouseover
28115          * Fires when the mouse is hovering over this menu
28116          * @param {Roo.bootstrap.menu.Item} this
28117          * @param {Roo.EventObject} e
28118          */
28119         mouseover : true,
28120         /**
28121          * @event mouseout
28122          * Fires when the mouse exits this menu
28123          * @param {Roo.bootstrap.menu.Item} this
28124          * @param {Roo.EventObject} e
28125          */
28126         mouseout : true,
28127         // raw events
28128         /**
28129          * @event click
28130          * The raw click event for the entire grid.
28131          * @param {Roo.EventObject} e
28132          */
28133         click : true
28134     });
28135 };
28136
28137 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28138     
28139     submenu : false,
28140     href : '',
28141     html : '',
28142     preventDefault: true,
28143     disable : false,
28144     icon : false,
28145     pos : 'right',
28146     
28147     getAutoCreate : function()
28148     {
28149         var text = [
28150             {
28151                 tag : 'span',
28152                 cls : 'roo-menu-item-text',
28153                 html : this.html
28154             }
28155         ];
28156         
28157         if(this.icon){
28158             text.unshift({
28159                 tag : 'i',
28160                 cls : 'fa ' + this.icon
28161             })
28162         }
28163         
28164         var cfg = {
28165             tag : 'li',
28166             cn : [
28167                 {
28168                     tag : 'a',
28169                     href : this.href || '#',
28170                     cn : text
28171                 }
28172             ]
28173         };
28174         
28175         if(this.disable){
28176             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28177         }
28178         
28179         if(this.submenu){
28180             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28181             
28182             if(this.pos == 'left'){
28183                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28184             }
28185         }
28186         
28187         return cfg;
28188     },
28189     
28190     initEvents : function() 
28191     {
28192         this.el.on('mouseover', this.onMouseOver, this);
28193         this.el.on('mouseout', this.onMouseOut, this);
28194         
28195         this.el.select('a', true).first().on('click', this.onClick, this);
28196         
28197     },
28198     
28199     onClick : function(e)
28200     {
28201         if(this.preventDefault){
28202             e.preventDefault();
28203         }
28204         
28205         this.fireEvent("click", this, e);
28206     },
28207     
28208     onMouseOver : function(e)
28209     {
28210         if(this.submenu && this.pos == 'left'){
28211             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28212         }
28213         
28214         this.fireEvent("mouseover", this, e);
28215     },
28216     
28217     onMouseOut : function(e)
28218     {
28219         this.fireEvent("mouseout", this, e);
28220     }
28221 });
28222
28223  
28224
28225  /*
28226  * - LGPL
28227  *
28228  * menu separator
28229  * 
28230  */
28231 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28232
28233 /**
28234  * @class Roo.bootstrap.menu.Separator
28235  * @extends Roo.bootstrap.Component
28236  * Bootstrap Separator class
28237  * 
28238  * @constructor
28239  * Create a new Separator
28240  * @param {Object} config The config object
28241  */
28242
28243
28244 Roo.bootstrap.menu.Separator = function(config){
28245     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28246 };
28247
28248 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28249     
28250     getAutoCreate : function(){
28251         var cfg = {
28252             tag : 'li',
28253             cls: 'divider'
28254         };
28255         
28256         return cfg;
28257     }
28258    
28259 });
28260
28261  
28262
28263  /*
28264  * - LGPL
28265  *
28266  * Tooltip
28267  * 
28268  */
28269
28270 /**
28271  * @class Roo.bootstrap.Tooltip
28272  * Bootstrap Tooltip class
28273  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28274  * to determine which dom element triggers the tooltip.
28275  * 
28276  * It needs to add support for additional attributes like tooltip-position
28277  * 
28278  * @constructor
28279  * Create a new Toolti
28280  * @param {Object} config The config object
28281  */
28282
28283 Roo.bootstrap.Tooltip = function(config){
28284     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28285     
28286     this.alignment = Roo.bootstrap.Tooltip.alignment;
28287     
28288     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28289         this.alignment = config.alignment;
28290     }
28291     
28292 };
28293
28294 Roo.apply(Roo.bootstrap.Tooltip, {
28295     /**
28296      * @function init initialize tooltip monitoring.
28297      * @static
28298      */
28299     currentEl : false,
28300     currentTip : false,
28301     currentRegion : false,
28302     
28303     //  init : delay?
28304     
28305     init : function()
28306     {
28307         Roo.get(document).on('mouseover', this.enter ,this);
28308         Roo.get(document).on('mouseout', this.leave, this);
28309          
28310         
28311         this.currentTip = new Roo.bootstrap.Tooltip();
28312     },
28313     
28314     enter : function(ev)
28315     {
28316         var dom = ev.getTarget();
28317         
28318         //Roo.log(['enter',dom]);
28319         var el = Roo.fly(dom);
28320         if (this.currentEl) {
28321             //Roo.log(dom);
28322             //Roo.log(this.currentEl);
28323             //Roo.log(this.currentEl.contains(dom));
28324             if (this.currentEl == el) {
28325                 return;
28326             }
28327             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28328                 return;
28329             }
28330
28331         }
28332         
28333         if (this.currentTip.el) {
28334             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28335         }    
28336         //Roo.log(ev);
28337         
28338         if(!el || el.dom == document){
28339             return;
28340         }
28341         
28342         var bindEl = el;
28343         
28344         // you can not look for children, as if el is the body.. then everythign is the child..
28345         if (!el.attr('tooltip')) { //
28346             if (!el.select("[tooltip]").elements.length) {
28347                 return;
28348             }
28349             // is the mouse over this child...?
28350             bindEl = el.select("[tooltip]").first();
28351             var xy = ev.getXY();
28352             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28353                 //Roo.log("not in region.");
28354                 return;
28355             }
28356             //Roo.log("child element over..");
28357             
28358         }
28359         this.currentEl = bindEl;
28360         this.currentTip.bind(bindEl);
28361         this.currentRegion = Roo.lib.Region.getRegion(dom);
28362         this.currentTip.enter();
28363         
28364     },
28365     leave : function(ev)
28366     {
28367         var dom = ev.getTarget();
28368         //Roo.log(['leave',dom]);
28369         if (!this.currentEl) {
28370             return;
28371         }
28372         
28373         
28374         if (dom != this.currentEl.dom) {
28375             return;
28376         }
28377         var xy = ev.getXY();
28378         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28379             return;
28380         }
28381         // only activate leave if mouse cursor is outside... bounding box..
28382         
28383         
28384         
28385         
28386         if (this.currentTip) {
28387             this.currentTip.leave();
28388         }
28389         //Roo.log('clear currentEl');
28390         this.currentEl = false;
28391         
28392         
28393     },
28394     alignment : {
28395         'left' : ['r-l', [-2,0], 'right'],
28396         'right' : ['l-r', [2,0], 'left'],
28397         'bottom' : ['t-b', [0,2], 'top'],
28398         'top' : [ 'b-t', [0,-2], 'bottom']
28399     }
28400     
28401 });
28402
28403
28404 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28405     
28406     
28407     bindEl : false,
28408     
28409     delay : null, // can be { show : 300 , hide: 500}
28410     
28411     timeout : null,
28412     
28413     hoverState : null, //???
28414     
28415     placement : 'bottom', 
28416     
28417     alignment : false,
28418     
28419     getAutoCreate : function(){
28420     
28421         var cfg = {
28422            cls : 'tooltip',   
28423            role : 'tooltip',
28424            cn : [
28425                 {
28426                     cls : 'tooltip-arrow arrow'
28427                 },
28428                 {
28429                     cls : 'tooltip-inner'
28430                 }
28431            ]
28432         };
28433         
28434         return cfg;
28435     },
28436     bind : function(el)
28437     {
28438         this.bindEl = el;
28439     },
28440     
28441     initEvents : function()
28442     {
28443         this.arrowEl = this.el.select('.arrow', true).first();
28444         this.innerEl = this.el.select('.tooltip-inner', true).first();
28445     },
28446     
28447     enter : function () {
28448        
28449         if (this.timeout != null) {
28450             clearTimeout(this.timeout);
28451         }
28452         
28453         this.hoverState = 'in';
28454          //Roo.log("enter - show");
28455         if (!this.delay || !this.delay.show) {
28456             this.show();
28457             return;
28458         }
28459         var _t = this;
28460         this.timeout = setTimeout(function () {
28461             if (_t.hoverState == 'in') {
28462                 _t.show();
28463             }
28464         }, this.delay.show);
28465     },
28466     leave : function()
28467     {
28468         clearTimeout(this.timeout);
28469     
28470         this.hoverState = 'out';
28471          if (!this.delay || !this.delay.hide) {
28472             this.hide();
28473             return;
28474         }
28475        
28476         var _t = this;
28477         this.timeout = setTimeout(function () {
28478             //Roo.log("leave - timeout");
28479             
28480             if (_t.hoverState == 'out') {
28481                 _t.hide();
28482                 Roo.bootstrap.Tooltip.currentEl = false;
28483             }
28484         }, delay);
28485     },
28486     
28487     show : function (msg)
28488     {
28489         if (!this.el) {
28490             this.render(document.body);
28491         }
28492         // set content.
28493         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28494         
28495         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28496         
28497         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28498         
28499         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28500                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28501         
28502         var placement = typeof this.placement == 'function' ?
28503             this.placement.call(this, this.el, on_el) :
28504             this.placement;
28505             
28506         var autoToken = /\s?auto?\s?/i;
28507         var autoPlace = autoToken.test(placement);
28508         if (autoPlace) {
28509             placement = placement.replace(autoToken, '') || 'top';
28510         }
28511         
28512         //this.el.detach()
28513         //this.el.setXY([0,0]);
28514         this.el.show();
28515         //this.el.dom.style.display='block';
28516         
28517         //this.el.appendTo(on_el);
28518         
28519         var p = this.getPosition();
28520         var box = this.el.getBox();
28521         
28522         if (autoPlace) {
28523             // fixme..
28524         }
28525         
28526         var align = this.alignment[placement];
28527         
28528         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28529         
28530         if(placement == 'top' || placement == 'bottom'){
28531             if(xy[0] < 0){
28532                 placement = 'right';
28533             }
28534             
28535             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28536                 placement = 'left';
28537             }
28538             
28539             var scroll = Roo.select('body', true).first().getScroll();
28540             
28541             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28542                 placement = 'top';
28543             }
28544             
28545             align = this.alignment[placement];
28546             
28547             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28548             
28549         }
28550         
28551         this.el.alignTo(this.bindEl, align[0],align[1]);
28552         //var arrow = this.el.select('.arrow',true).first();
28553         //arrow.set(align[2], 
28554         
28555         this.el.addClass(placement);
28556         this.el.addClass("bs-tooltip-"+ placement);
28557         
28558         this.el.addClass('in fade show');
28559         
28560         this.hoverState = null;
28561         
28562         if (this.el.hasClass('fade')) {
28563             // fade it?
28564         }
28565         
28566         
28567         
28568         
28569         
28570     },
28571     hide : function()
28572     {
28573          
28574         if (!this.el) {
28575             return;
28576         }
28577         //this.el.setXY([0,0]);
28578         this.el.removeClass(['show', 'in']);
28579         //this.el.hide();
28580         
28581     }
28582     
28583 });
28584  
28585
28586  /*
28587  * - LGPL
28588  *
28589  * Location Picker
28590  * 
28591  */
28592
28593 /**
28594  * @class Roo.bootstrap.LocationPicker
28595  * @extends Roo.bootstrap.Component
28596  * Bootstrap LocationPicker class
28597  * @cfg {Number} latitude Position when init default 0
28598  * @cfg {Number} longitude Position when init default 0
28599  * @cfg {Number} zoom default 15
28600  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28601  * @cfg {Boolean} mapTypeControl default false
28602  * @cfg {Boolean} disableDoubleClickZoom default false
28603  * @cfg {Boolean} scrollwheel default true
28604  * @cfg {Boolean} streetViewControl default false
28605  * @cfg {Number} radius default 0
28606  * @cfg {String} locationName
28607  * @cfg {Boolean} draggable default true
28608  * @cfg {Boolean} enableAutocomplete default false
28609  * @cfg {Boolean} enableReverseGeocode default true
28610  * @cfg {String} markerTitle
28611  * 
28612  * @constructor
28613  * Create a new LocationPicker
28614  * @param {Object} config The config object
28615  */
28616
28617
28618 Roo.bootstrap.LocationPicker = function(config){
28619     
28620     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28621     
28622     this.addEvents({
28623         /**
28624          * @event initial
28625          * Fires when the picker initialized.
28626          * @param {Roo.bootstrap.LocationPicker} this
28627          * @param {Google Location} location
28628          */
28629         initial : true,
28630         /**
28631          * @event positionchanged
28632          * Fires when the picker position changed.
28633          * @param {Roo.bootstrap.LocationPicker} this
28634          * @param {Google Location} location
28635          */
28636         positionchanged : true,
28637         /**
28638          * @event resize
28639          * Fires when the map resize.
28640          * @param {Roo.bootstrap.LocationPicker} this
28641          */
28642         resize : true,
28643         /**
28644          * @event show
28645          * Fires when the map show.
28646          * @param {Roo.bootstrap.LocationPicker} this
28647          */
28648         show : true,
28649         /**
28650          * @event hide
28651          * Fires when the map hide.
28652          * @param {Roo.bootstrap.LocationPicker} this
28653          */
28654         hide : true,
28655         /**
28656          * @event mapClick
28657          * Fires when click the map.
28658          * @param {Roo.bootstrap.LocationPicker} this
28659          * @param {Map event} e
28660          */
28661         mapClick : true,
28662         /**
28663          * @event mapRightClick
28664          * Fires when right click the map.
28665          * @param {Roo.bootstrap.LocationPicker} this
28666          * @param {Map event} e
28667          */
28668         mapRightClick : true,
28669         /**
28670          * @event markerClick
28671          * Fires when click the marker.
28672          * @param {Roo.bootstrap.LocationPicker} this
28673          * @param {Map event} e
28674          */
28675         markerClick : true,
28676         /**
28677          * @event markerRightClick
28678          * Fires when right click the marker.
28679          * @param {Roo.bootstrap.LocationPicker} this
28680          * @param {Map event} e
28681          */
28682         markerRightClick : true,
28683         /**
28684          * @event OverlayViewDraw
28685          * Fires when OverlayView Draw
28686          * @param {Roo.bootstrap.LocationPicker} this
28687          */
28688         OverlayViewDraw : true,
28689         /**
28690          * @event OverlayViewOnAdd
28691          * Fires when OverlayView Draw
28692          * @param {Roo.bootstrap.LocationPicker} this
28693          */
28694         OverlayViewOnAdd : true,
28695         /**
28696          * @event OverlayViewOnRemove
28697          * Fires when OverlayView Draw
28698          * @param {Roo.bootstrap.LocationPicker} this
28699          */
28700         OverlayViewOnRemove : true,
28701         /**
28702          * @event OverlayViewShow
28703          * Fires when OverlayView Draw
28704          * @param {Roo.bootstrap.LocationPicker} this
28705          * @param {Pixel} cpx
28706          */
28707         OverlayViewShow : true,
28708         /**
28709          * @event OverlayViewHide
28710          * Fires when OverlayView Draw
28711          * @param {Roo.bootstrap.LocationPicker} this
28712          */
28713         OverlayViewHide : true,
28714         /**
28715          * @event loadexception
28716          * Fires when load google lib failed.
28717          * @param {Roo.bootstrap.LocationPicker} this
28718          */
28719         loadexception : true
28720     });
28721         
28722 };
28723
28724 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28725     
28726     gMapContext: false,
28727     
28728     latitude: 0,
28729     longitude: 0,
28730     zoom: 15,
28731     mapTypeId: false,
28732     mapTypeControl: false,
28733     disableDoubleClickZoom: false,
28734     scrollwheel: true,
28735     streetViewControl: false,
28736     radius: 0,
28737     locationName: '',
28738     draggable: true,
28739     enableAutocomplete: false,
28740     enableReverseGeocode: true,
28741     markerTitle: '',
28742     
28743     getAutoCreate: function()
28744     {
28745
28746         var cfg = {
28747             tag: 'div',
28748             cls: 'roo-location-picker'
28749         };
28750         
28751         return cfg
28752     },
28753     
28754     initEvents: function(ct, position)
28755     {       
28756         if(!this.el.getWidth() || this.isApplied()){
28757             return;
28758         }
28759         
28760         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28761         
28762         this.initial();
28763     },
28764     
28765     initial: function()
28766     {
28767         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28768             this.fireEvent('loadexception', this);
28769             return;
28770         }
28771         
28772         if(!this.mapTypeId){
28773             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28774         }
28775         
28776         this.gMapContext = this.GMapContext();
28777         
28778         this.initOverlayView();
28779         
28780         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28781         
28782         var _this = this;
28783                 
28784         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28785             _this.setPosition(_this.gMapContext.marker.position);
28786         });
28787         
28788         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28789             _this.fireEvent('mapClick', this, event);
28790             
28791         });
28792
28793         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28794             _this.fireEvent('mapRightClick', this, event);
28795             
28796         });
28797         
28798         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28799             _this.fireEvent('markerClick', this, event);
28800             
28801         });
28802
28803         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28804             _this.fireEvent('markerRightClick', this, event);
28805             
28806         });
28807         
28808         this.setPosition(this.gMapContext.location);
28809         
28810         this.fireEvent('initial', this, this.gMapContext.location);
28811     },
28812     
28813     initOverlayView: function()
28814     {
28815         var _this = this;
28816         
28817         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28818             
28819             draw: function()
28820             {
28821                 _this.fireEvent('OverlayViewDraw', _this);
28822             },
28823             
28824             onAdd: function()
28825             {
28826                 _this.fireEvent('OverlayViewOnAdd', _this);
28827             },
28828             
28829             onRemove: function()
28830             {
28831                 _this.fireEvent('OverlayViewOnRemove', _this);
28832             },
28833             
28834             show: function(cpx)
28835             {
28836                 _this.fireEvent('OverlayViewShow', _this, cpx);
28837             },
28838             
28839             hide: function()
28840             {
28841                 _this.fireEvent('OverlayViewHide', _this);
28842             }
28843             
28844         });
28845     },
28846     
28847     fromLatLngToContainerPixel: function(event)
28848     {
28849         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28850     },
28851     
28852     isApplied: function() 
28853     {
28854         return this.getGmapContext() == false ? false : true;
28855     },
28856     
28857     getGmapContext: function() 
28858     {
28859         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28860     },
28861     
28862     GMapContext: function() 
28863     {
28864         var position = new google.maps.LatLng(this.latitude, this.longitude);
28865         
28866         var _map = new google.maps.Map(this.el.dom, {
28867             center: position,
28868             zoom: this.zoom,
28869             mapTypeId: this.mapTypeId,
28870             mapTypeControl: this.mapTypeControl,
28871             disableDoubleClickZoom: this.disableDoubleClickZoom,
28872             scrollwheel: this.scrollwheel,
28873             streetViewControl: this.streetViewControl,
28874             locationName: this.locationName,
28875             draggable: this.draggable,
28876             enableAutocomplete: this.enableAutocomplete,
28877             enableReverseGeocode: this.enableReverseGeocode
28878         });
28879         
28880         var _marker = new google.maps.Marker({
28881             position: position,
28882             map: _map,
28883             title: this.markerTitle,
28884             draggable: this.draggable
28885         });
28886         
28887         return {
28888             map: _map,
28889             marker: _marker,
28890             circle: null,
28891             location: position,
28892             radius: this.radius,
28893             locationName: this.locationName,
28894             addressComponents: {
28895                 formatted_address: null,
28896                 addressLine1: null,
28897                 addressLine2: null,
28898                 streetName: null,
28899                 streetNumber: null,
28900                 city: null,
28901                 district: null,
28902                 state: null,
28903                 stateOrProvince: null
28904             },
28905             settings: this,
28906             domContainer: this.el.dom,
28907             geodecoder: new google.maps.Geocoder()
28908         };
28909     },
28910     
28911     drawCircle: function(center, radius, options) 
28912     {
28913         if (this.gMapContext.circle != null) {
28914             this.gMapContext.circle.setMap(null);
28915         }
28916         if (radius > 0) {
28917             radius *= 1;
28918             options = Roo.apply({}, options, {
28919                 strokeColor: "#0000FF",
28920                 strokeOpacity: .35,
28921                 strokeWeight: 2,
28922                 fillColor: "#0000FF",
28923                 fillOpacity: .2
28924             });
28925             
28926             options.map = this.gMapContext.map;
28927             options.radius = radius;
28928             options.center = center;
28929             this.gMapContext.circle = new google.maps.Circle(options);
28930             return this.gMapContext.circle;
28931         }
28932         
28933         return null;
28934     },
28935     
28936     setPosition: function(location) 
28937     {
28938         this.gMapContext.location = location;
28939         this.gMapContext.marker.setPosition(location);
28940         this.gMapContext.map.panTo(location);
28941         this.drawCircle(location, this.gMapContext.radius, {});
28942         
28943         var _this = this;
28944         
28945         if (this.gMapContext.settings.enableReverseGeocode) {
28946             this.gMapContext.geodecoder.geocode({
28947                 latLng: this.gMapContext.location
28948             }, function(results, status) {
28949                 
28950                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28951                     _this.gMapContext.locationName = results[0].formatted_address;
28952                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28953                     
28954                     _this.fireEvent('positionchanged', this, location);
28955                 }
28956             });
28957             
28958             return;
28959         }
28960         
28961         this.fireEvent('positionchanged', this, location);
28962     },
28963     
28964     resize: function()
28965     {
28966         google.maps.event.trigger(this.gMapContext.map, "resize");
28967         
28968         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28969         
28970         this.fireEvent('resize', this);
28971     },
28972     
28973     setPositionByLatLng: function(latitude, longitude)
28974     {
28975         this.setPosition(new google.maps.LatLng(latitude, longitude));
28976     },
28977     
28978     getCurrentPosition: function() 
28979     {
28980         return {
28981             latitude: this.gMapContext.location.lat(),
28982             longitude: this.gMapContext.location.lng()
28983         };
28984     },
28985     
28986     getAddressName: function() 
28987     {
28988         return this.gMapContext.locationName;
28989     },
28990     
28991     getAddressComponents: function() 
28992     {
28993         return this.gMapContext.addressComponents;
28994     },
28995     
28996     address_component_from_google_geocode: function(address_components) 
28997     {
28998         var result = {};
28999         
29000         for (var i = 0; i < address_components.length; i++) {
29001             var component = address_components[i];
29002             if (component.types.indexOf("postal_code") >= 0) {
29003                 result.postalCode = component.short_name;
29004             } else if (component.types.indexOf("street_number") >= 0) {
29005                 result.streetNumber = component.short_name;
29006             } else if (component.types.indexOf("route") >= 0) {
29007                 result.streetName = component.short_name;
29008             } else if (component.types.indexOf("neighborhood") >= 0) {
29009                 result.city = component.short_name;
29010             } else if (component.types.indexOf("locality") >= 0) {
29011                 result.city = component.short_name;
29012             } else if (component.types.indexOf("sublocality") >= 0) {
29013                 result.district = component.short_name;
29014             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29015                 result.stateOrProvince = component.short_name;
29016             } else if (component.types.indexOf("country") >= 0) {
29017                 result.country = component.short_name;
29018             }
29019         }
29020         
29021         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29022         result.addressLine2 = "";
29023         return result;
29024     },
29025     
29026     setZoomLevel: function(zoom)
29027     {
29028         this.gMapContext.map.setZoom(zoom);
29029     },
29030     
29031     show: function()
29032     {
29033         if(!this.el){
29034             return;
29035         }
29036         
29037         this.el.show();
29038         
29039         this.resize();
29040         
29041         this.fireEvent('show', this);
29042     },
29043     
29044     hide: function()
29045     {
29046         if(!this.el){
29047             return;
29048         }
29049         
29050         this.el.hide();
29051         
29052         this.fireEvent('hide', this);
29053     }
29054     
29055 });
29056
29057 Roo.apply(Roo.bootstrap.LocationPicker, {
29058     
29059     OverlayView : function(map, options)
29060     {
29061         options = options || {};
29062         
29063         this.setMap(map);
29064     }
29065     
29066     
29067 });/**
29068  * @class Roo.bootstrap.Alert
29069  * @extends Roo.bootstrap.Component
29070  * Bootstrap Alert class - shows an alert area box
29071  * eg
29072  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29073   Enter a valid email address
29074 </div>
29075  * @licence LGPL
29076  * @cfg {String} title The title of alert
29077  * @cfg {String} html The content of alert
29078  * @cfg {String} weight (  success | info | warning | danger )
29079  * @cfg {String} faicon font-awesomeicon
29080  * 
29081  * @constructor
29082  * Create a new alert
29083  * @param {Object} config The config object
29084  */
29085
29086
29087 Roo.bootstrap.Alert = function(config){
29088     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29089     
29090 };
29091
29092 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29093     
29094     title: '',
29095     html: '',
29096     weight: false,
29097     faicon: false,
29098     
29099     getAutoCreate : function()
29100     {
29101         
29102         var cfg = {
29103             tag : 'div',
29104             cls : 'alert',
29105             cn : [
29106                 {
29107                     tag : 'i',
29108                     cls : 'roo-alert-icon'
29109                     
29110                 },
29111                 {
29112                     tag : 'b',
29113                     cls : 'roo-alert-title',
29114                     html : this.title
29115                 },
29116                 {
29117                     tag : 'span',
29118                     cls : 'roo-alert-text',
29119                     html : this.html
29120                 }
29121             ]
29122         };
29123         
29124         if(this.faicon){
29125             cfg.cn[0].cls += ' fa ' + this.faicon;
29126         }
29127         
29128         if(this.weight){
29129             cfg.cls += ' alert-' + this.weight;
29130         }
29131         
29132         return cfg;
29133     },
29134     
29135     initEvents: function() 
29136     {
29137         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29138     },
29139     
29140     setTitle : function(str)
29141     {
29142         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29143     },
29144     
29145     setText : function(str)
29146     {
29147         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29148     },
29149     
29150     setWeight : function(weight)
29151     {
29152         if(this.weight){
29153             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29154         }
29155         
29156         this.weight = weight;
29157         
29158         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29159     },
29160     
29161     setIcon : function(icon)
29162     {
29163         if(this.faicon){
29164             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29165         }
29166         
29167         this.faicon = icon;
29168         
29169         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29170     },
29171     
29172     hide: function() 
29173     {
29174         this.el.hide();   
29175     },
29176     
29177     show: function() 
29178     {  
29179         this.el.show();   
29180     }
29181     
29182 });
29183
29184  
29185 /*
29186 * Licence: LGPL
29187 */
29188
29189 /**
29190  * @class Roo.bootstrap.UploadCropbox
29191  * @extends Roo.bootstrap.Component
29192  * Bootstrap UploadCropbox class
29193  * @cfg {String} emptyText show when image has been loaded
29194  * @cfg {String} rotateNotify show when image too small to rotate
29195  * @cfg {Number} errorTimeout default 3000
29196  * @cfg {Number} minWidth default 300
29197  * @cfg {Number} minHeight default 300
29198  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29199  * @cfg {Boolean} isDocument (true|false) default false
29200  * @cfg {String} url action url
29201  * @cfg {String} paramName default 'imageUpload'
29202  * @cfg {String} method default POST
29203  * @cfg {Boolean} loadMask (true|false) default true
29204  * @cfg {Boolean} loadingText default 'Loading...'
29205  * 
29206  * @constructor
29207  * Create a new UploadCropbox
29208  * @param {Object} config The config object
29209  */
29210
29211 Roo.bootstrap.UploadCropbox = function(config){
29212     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29213     
29214     this.addEvents({
29215         /**
29216          * @event beforeselectfile
29217          * Fire before select file
29218          * @param {Roo.bootstrap.UploadCropbox} this
29219          */
29220         "beforeselectfile" : true,
29221         /**
29222          * @event initial
29223          * Fire after initEvent
29224          * @param {Roo.bootstrap.UploadCropbox} this
29225          */
29226         "initial" : true,
29227         /**
29228          * @event crop
29229          * Fire after initEvent
29230          * @param {Roo.bootstrap.UploadCropbox} this
29231          * @param {String} data
29232          */
29233         "crop" : true,
29234         /**
29235          * @event prepare
29236          * Fire when preparing the file data
29237          * @param {Roo.bootstrap.UploadCropbox} this
29238          * @param {Object} file
29239          */
29240         "prepare" : true,
29241         /**
29242          * @event exception
29243          * Fire when get exception
29244          * @param {Roo.bootstrap.UploadCropbox} this
29245          * @param {XMLHttpRequest} xhr
29246          */
29247         "exception" : true,
29248         /**
29249          * @event beforeloadcanvas
29250          * Fire before load the canvas
29251          * @param {Roo.bootstrap.UploadCropbox} this
29252          * @param {String} src
29253          */
29254         "beforeloadcanvas" : true,
29255         /**
29256          * @event trash
29257          * Fire when trash image
29258          * @param {Roo.bootstrap.UploadCropbox} this
29259          */
29260         "trash" : true,
29261         /**
29262          * @event download
29263          * Fire when download the image
29264          * @param {Roo.bootstrap.UploadCropbox} this
29265          */
29266         "download" : true,
29267         /**
29268          * @event footerbuttonclick
29269          * Fire when footerbuttonclick
29270          * @param {Roo.bootstrap.UploadCropbox} this
29271          * @param {String} type
29272          */
29273         "footerbuttonclick" : true,
29274         /**
29275          * @event resize
29276          * Fire when resize
29277          * @param {Roo.bootstrap.UploadCropbox} this
29278          */
29279         "resize" : true,
29280         /**
29281          * @event rotate
29282          * Fire when rotate the image
29283          * @param {Roo.bootstrap.UploadCropbox} this
29284          * @param {String} pos
29285          */
29286         "rotate" : true,
29287         /**
29288          * @event inspect
29289          * Fire when inspect the file
29290          * @param {Roo.bootstrap.UploadCropbox} this
29291          * @param {Object} file
29292          */
29293         "inspect" : true,
29294         /**
29295          * @event upload
29296          * Fire when xhr upload the file
29297          * @param {Roo.bootstrap.UploadCropbox} this
29298          * @param {Object} data
29299          */
29300         "upload" : true,
29301         /**
29302          * @event arrange
29303          * Fire when arrange the file data
29304          * @param {Roo.bootstrap.UploadCropbox} this
29305          * @param {Object} formData
29306          */
29307         "arrange" : true
29308     });
29309     
29310     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29311 };
29312
29313 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29314     
29315     emptyText : 'Click to upload image',
29316     rotateNotify : 'Image is too small to rotate',
29317     errorTimeout : 3000,
29318     scale : 0,
29319     baseScale : 1,
29320     rotate : 0,
29321     dragable : false,
29322     pinching : false,
29323     mouseX : 0,
29324     mouseY : 0,
29325     cropData : false,
29326     minWidth : 300,
29327     minHeight : 300,
29328     file : false,
29329     exif : {},
29330     baseRotate : 1,
29331     cropType : 'image/jpeg',
29332     buttons : false,
29333     canvasLoaded : false,
29334     isDocument : false,
29335     method : 'POST',
29336     paramName : 'imageUpload',
29337     loadMask : true,
29338     loadingText : 'Loading...',
29339     maskEl : false,
29340     
29341     getAutoCreate : function()
29342     {
29343         var cfg = {
29344             tag : 'div',
29345             cls : 'roo-upload-cropbox',
29346             cn : [
29347                 {
29348                     tag : 'input',
29349                     cls : 'roo-upload-cropbox-selector',
29350                     type : 'file'
29351                 },
29352                 {
29353                     tag : 'div',
29354                     cls : 'roo-upload-cropbox-body',
29355                     style : 'cursor:pointer',
29356                     cn : [
29357                         {
29358                             tag : 'div',
29359                             cls : 'roo-upload-cropbox-preview'
29360                         },
29361                         {
29362                             tag : 'div',
29363                             cls : 'roo-upload-cropbox-thumb'
29364                         },
29365                         {
29366                             tag : 'div',
29367                             cls : 'roo-upload-cropbox-empty-notify',
29368                             html : this.emptyText
29369                         },
29370                         {
29371                             tag : 'div',
29372                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29373                             html : this.rotateNotify
29374                         }
29375                     ]
29376                 },
29377                 {
29378                     tag : 'div',
29379                     cls : 'roo-upload-cropbox-footer',
29380                     cn : {
29381                         tag : 'div',
29382                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29383                         cn : []
29384                     }
29385                 }
29386             ]
29387         };
29388         
29389         return cfg;
29390     },
29391     
29392     onRender : function(ct, position)
29393     {
29394         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29395         
29396         if (this.buttons.length) {
29397             
29398             Roo.each(this.buttons, function(bb) {
29399                 
29400                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29401                 
29402                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29403                 
29404             }, this);
29405         }
29406         
29407         if(this.loadMask){
29408             this.maskEl = this.el;
29409         }
29410     },
29411     
29412     initEvents : function()
29413     {
29414         this.urlAPI = (window.createObjectURL && window) || 
29415                                 (window.URL && URL.revokeObjectURL && URL) || 
29416                                 (window.webkitURL && webkitURL);
29417                         
29418         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29419         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29420         
29421         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29422         this.selectorEl.hide();
29423         
29424         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29425         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29426         
29427         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29428         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29429         this.thumbEl.hide();
29430         
29431         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29432         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29433         
29434         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29435         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29436         this.errorEl.hide();
29437         
29438         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29439         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29440         this.footerEl.hide();
29441         
29442         this.setThumbBoxSize();
29443         
29444         this.bind();
29445         
29446         this.resize();
29447         
29448         this.fireEvent('initial', this);
29449     },
29450
29451     bind : function()
29452     {
29453         var _this = this;
29454         
29455         window.addEventListener("resize", function() { _this.resize(); } );
29456         
29457         this.bodyEl.on('click', this.beforeSelectFile, this);
29458         
29459         if(Roo.isTouch){
29460             this.bodyEl.on('touchstart', this.onTouchStart, this);
29461             this.bodyEl.on('touchmove', this.onTouchMove, this);
29462             this.bodyEl.on('touchend', this.onTouchEnd, this);
29463         }
29464         
29465         if(!Roo.isTouch){
29466             this.bodyEl.on('mousedown', this.onMouseDown, this);
29467             this.bodyEl.on('mousemove', this.onMouseMove, this);
29468             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29469             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29470             Roo.get(document).on('mouseup', this.onMouseUp, this);
29471         }
29472         
29473         this.selectorEl.on('change', this.onFileSelected, this);
29474     },
29475     
29476     reset : function()
29477     {    
29478         this.scale = 0;
29479         this.baseScale = 1;
29480         this.rotate = 0;
29481         this.baseRotate = 1;
29482         this.dragable = false;
29483         this.pinching = false;
29484         this.mouseX = 0;
29485         this.mouseY = 0;
29486         this.cropData = false;
29487         this.notifyEl.dom.innerHTML = this.emptyText;
29488         
29489         this.selectorEl.dom.value = '';
29490         
29491     },
29492     
29493     resize : function()
29494     {
29495         if(this.fireEvent('resize', this) != false){
29496             this.setThumbBoxPosition();
29497             this.setCanvasPosition();
29498         }
29499     },
29500     
29501     onFooterButtonClick : function(e, el, o, type)
29502     {
29503         switch (type) {
29504             case 'rotate-left' :
29505                 this.onRotateLeft(e);
29506                 break;
29507             case 'rotate-right' :
29508                 this.onRotateRight(e);
29509                 break;
29510             case 'picture' :
29511                 this.beforeSelectFile(e);
29512                 break;
29513             case 'trash' :
29514                 this.trash(e);
29515                 break;
29516             case 'crop' :
29517                 this.crop(e);
29518                 break;
29519             case 'download' :
29520                 this.download(e);
29521                 break;
29522             default :
29523                 break;
29524         }
29525         
29526         this.fireEvent('footerbuttonclick', this, type);
29527     },
29528     
29529     beforeSelectFile : function(e)
29530     {
29531         e.preventDefault();
29532         
29533         if(this.fireEvent('beforeselectfile', this) != false){
29534             this.selectorEl.dom.click();
29535         }
29536     },
29537     
29538     onFileSelected : function(e)
29539     {
29540         e.preventDefault();
29541         
29542         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29543             return;
29544         }
29545         
29546         var file = this.selectorEl.dom.files[0];
29547         
29548         if(this.fireEvent('inspect', this, file) != false){
29549             this.prepare(file);
29550         }
29551         
29552     },
29553     
29554     trash : function(e)
29555     {
29556         this.fireEvent('trash', this);
29557     },
29558     
29559     download : function(e)
29560     {
29561         this.fireEvent('download', this);
29562     },
29563     
29564     loadCanvas : function(src)
29565     {   
29566         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29567             
29568             this.reset();
29569             
29570             this.imageEl = document.createElement('img');
29571             
29572             var _this = this;
29573             
29574             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29575             
29576             this.imageEl.src = src;
29577         }
29578     },
29579     
29580     onLoadCanvas : function()
29581     {   
29582         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29583         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29584         
29585         this.bodyEl.un('click', this.beforeSelectFile, this);
29586         
29587         this.notifyEl.hide();
29588         this.thumbEl.show();
29589         this.footerEl.show();
29590         
29591         this.baseRotateLevel();
29592         
29593         if(this.isDocument){
29594             this.setThumbBoxSize();
29595         }
29596         
29597         this.setThumbBoxPosition();
29598         
29599         this.baseScaleLevel();
29600         
29601         this.draw();
29602         
29603         this.resize();
29604         
29605         this.canvasLoaded = true;
29606         
29607         if(this.loadMask){
29608             this.maskEl.unmask();
29609         }
29610         
29611     },
29612     
29613     setCanvasPosition : function()
29614     {   
29615         if(!this.canvasEl){
29616             return;
29617         }
29618         
29619         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29620         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29621         
29622         this.previewEl.setLeft(pw);
29623         this.previewEl.setTop(ph);
29624         
29625     },
29626     
29627     onMouseDown : function(e)
29628     {   
29629         e.stopEvent();
29630         
29631         this.dragable = true;
29632         this.pinching = false;
29633         
29634         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29635             this.dragable = false;
29636             return;
29637         }
29638         
29639         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29640         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29641         
29642     },
29643     
29644     onMouseMove : function(e)
29645     {   
29646         e.stopEvent();
29647         
29648         if(!this.canvasLoaded){
29649             return;
29650         }
29651         
29652         if (!this.dragable){
29653             return;
29654         }
29655         
29656         var minX = Math.ceil(this.thumbEl.getLeft(true));
29657         var minY = Math.ceil(this.thumbEl.getTop(true));
29658         
29659         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29660         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29661         
29662         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29663         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29664         
29665         x = x - this.mouseX;
29666         y = y - this.mouseY;
29667         
29668         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29669         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29670         
29671         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29672         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29673         
29674         this.previewEl.setLeft(bgX);
29675         this.previewEl.setTop(bgY);
29676         
29677         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29678         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29679     },
29680     
29681     onMouseUp : function(e)
29682     {   
29683         e.stopEvent();
29684         
29685         this.dragable = false;
29686     },
29687     
29688     onMouseWheel : function(e)
29689     {   
29690         e.stopEvent();
29691         
29692         this.startScale = this.scale;
29693         
29694         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29695         
29696         if(!this.zoomable()){
29697             this.scale = this.startScale;
29698             return;
29699         }
29700         
29701         this.draw();
29702         
29703         return;
29704     },
29705     
29706     zoomable : function()
29707     {
29708         var minScale = this.thumbEl.getWidth() / this.minWidth;
29709         
29710         if(this.minWidth < this.minHeight){
29711             minScale = this.thumbEl.getHeight() / this.minHeight;
29712         }
29713         
29714         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29715         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29716         
29717         if(
29718                 this.isDocument &&
29719                 (this.rotate == 0 || this.rotate == 180) && 
29720                 (
29721                     width > this.imageEl.OriginWidth || 
29722                     height > this.imageEl.OriginHeight ||
29723                     (width < this.minWidth && height < this.minHeight)
29724                 )
29725         ){
29726             return false;
29727         }
29728         
29729         if(
29730                 this.isDocument &&
29731                 (this.rotate == 90 || this.rotate == 270) && 
29732                 (
29733                     width > this.imageEl.OriginWidth || 
29734                     height > this.imageEl.OriginHeight ||
29735                     (width < this.minHeight && height < this.minWidth)
29736                 )
29737         ){
29738             return false;
29739         }
29740         
29741         if(
29742                 !this.isDocument &&
29743                 (this.rotate == 0 || this.rotate == 180) && 
29744                 (
29745                     width < this.minWidth || 
29746                     width > this.imageEl.OriginWidth || 
29747                     height < this.minHeight || 
29748                     height > this.imageEl.OriginHeight
29749                 )
29750         ){
29751             return false;
29752         }
29753         
29754         if(
29755                 !this.isDocument &&
29756                 (this.rotate == 90 || this.rotate == 270) && 
29757                 (
29758                     width < this.minHeight || 
29759                     width > this.imageEl.OriginWidth || 
29760                     height < this.minWidth || 
29761                     height > this.imageEl.OriginHeight
29762                 )
29763         ){
29764             return false;
29765         }
29766         
29767         return true;
29768         
29769     },
29770     
29771     onRotateLeft : function(e)
29772     {   
29773         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29774             
29775             var minScale = this.thumbEl.getWidth() / this.minWidth;
29776             
29777             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29778             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29779             
29780             this.startScale = this.scale;
29781             
29782             while (this.getScaleLevel() < minScale){
29783             
29784                 this.scale = this.scale + 1;
29785                 
29786                 if(!this.zoomable()){
29787                     break;
29788                 }
29789                 
29790                 if(
29791                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29792                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29793                 ){
29794                     continue;
29795                 }
29796                 
29797                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29798
29799                 this.draw();
29800                 
29801                 return;
29802             }
29803             
29804             this.scale = this.startScale;
29805             
29806             this.onRotateFail();
29807             
29808             return false;
29809         }
29810         
29811         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29812
29813         if(this.isDocument){
29814             this.setThumbBoxSize();
29815             this.setThumbBoxPosition();
29816             this.setCanvasPosition();
29817         }
29818         
29819         this.draw();
29820         
29821         this.fireEvent('rotate', this, 'left');
29822         
29823     },
29824     
29825     onRotateRight : function(e)
29826     {
29827         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29828             
29829             var minScale = this.thumbEl.getWidth() / this.minWidth;
29830         
29831             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29832             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29833             
29834             this.startScale = this.scale;
29835             
29836             while (this.getScaleLevel() < minScale){
29837             
29838                 this.scale = this.scale + 1;
29839                 
29840                 if(!this.zoomable()){
29841                     break;
29842                 }
29843                 
29844                 if(
29845                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29846                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29847                 ){
29848                     continue;
29849                 }
29850                 
29851                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29852
29853                 this.draw();
29854                 
29855                 return;
29856             }
29857             
29858             this.scale = this.startScale;
29859             
29860             this.onRotateFail();
29861             
29862             return false;
29863         }
29864         
29865         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29866
29867         if(this.isDocument){
29868             this.setThumbBoxSize();
29869             this.setThumbBoxPosition();
29870             this.setCanvasPosition();
29871         }
29872         
29873         this.draw();
29874         
29875         this.fireEvent('rotate', this, 'right');
29876     },
29877     
29878     onRotateFail : function()
29879     {
29880         this.errorEl.show(true);
29881         
29882         var _this = this;
29883         
29884         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29885     },
29886     
29887     draw : function()
29888     {
29889         this.previewEl.dom.innerHTML = '';
29890         
29891         var canvasEl = document.createElement("canvas");
29892         
29893         var contextEl = canvasEl.getContext("2d");
29894         
29895         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29896         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29897         var center = this.imageEl.OriginWidth / 2;
29898         
29899         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29900             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29901             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29902             center = this.imageEl.OriginHeight / 2;
29903         }
29904         
29905         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29906         
29907         contextEl.translate(center, center);
29908         contextEl.rotate(this.rotate * Math.PI / 180);
29909
29910         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29911         
29912         this.canvasEl = document.createElement("canvas");
29913         
29914         this.contextEl = this.canvasEl.getContext("2d");
29915         
29916         switch (this.rotate) {
29917             case 0 :
29918                 
29919                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29920                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29921                 
29922                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29923                 
29924                 break;
29925             case 90 : 
29926                 
29927                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29928                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29929                 
29930                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29931                     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);
29932                     break;
29933                 }
29934                 
29935                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29936                 
29937                 break;
29938             case 180 :
29939                 
29940                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29941                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29942                 
29943                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29944                     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);
29945                     break;
29946                 }
29947                 
29948                 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);
29949                 
29950                 break;
29951             case 270 :
29952                 
29953                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29954                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29955         
29956                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29957                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29958                     break;
29959                 }
29960                 
29961                 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);
29962                 
29963                 break;
29964             default : 
29965                 break;
29966         }
29967         
29968         this.previewEl.appendChild(this.canvasEl);
29969         
29970         this.setCanvasPosition();
29971     },
29972     
29973     crop : function()
29974     {
29975         if(!this.canvasLoaded){
29976             return;
29977         }
29978         
29979         var imageCanvas = document.createElement("canvas");
29980         
29981         var imageContext = imageCanvas.getContext("2d");
29982         
29983         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29984         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29985         
29986         var center = imageCanvas.width / 2;
29987         
29988         imageContext.translate(center, center);
29989         
29990         imageContext.rotate(this.rotate * Math.PI / 180);
29991         
29992         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29993         
29994         var canvas = document.createElement("canvas");
29995         
29996         var context = canvas.getContext("2d");
29997                 
29998         canvas.width = this.minWidth;
29999         canvas.height = this.minHeight;
30000
30001         switch (this.rotate) {
30002             case 0 :
30003                 
30004                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30005                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30006                 
30007                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30008                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30009                 
30010                 var targetWidth = this.minWidth - 2 * x;
30011                 var targetHeight = this.minHeight - 2 * y;
30012                 
30013                 var scale = 1;
30014                 
30015                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30016                     scale = targetWidth / width;
30017                 }
30018                 
30019                 if(x > 0 && y == 0){
30020                     scale = targetHeight / height;
30021                 }
30022                 
30023                 if(x > 0 && y > 0){
30024                     scale = targetWidth / width;
30025                     
30026                     if(width < height){
30027                         scale = targetHeight / height;
30028                     }
30029                 }
30030                 
30031                 context.scale(scale, scale);
30032                 
30033                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30034                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30035
30036                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30037                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30038
30039                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30040                 
30041                 break;
30042             case 90 : 
30043                 
30044                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30045                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30046                 
30047                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30048                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30049                 
30050                 var targetWidth = this.minWidth - 2 * x;
30051                 var targetHeight = this.minHeight - 2 * y;
30052                 
30053                 var scale = 1;
30054                 
30055                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30056                     scale = targetWidth / width;
30057                 }
30058                 
30059                 if(x > 0 && y == 0){
30060                     scale = targetHeight / height;
30061                 }
30062                 
30063                 if(x > 0 && y > 0){
30064                     scale = targetWidth / width;
30065                     
30066                     if(width < height){
30067                         scale = targetHeight / height;
30068                     }
30069                 }
30070                 
30071                 context.scale(scale, scale);
30072                 
30073                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30074                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30075
30076                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30077                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30078                 
30079                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30080                 
30081                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30082                 
30083                 break;
30084             case 180 :
30085                 
30086                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30087                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30088                 
30089                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30090                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30091                 
30092                 var targetWidth = this.minWidth - 2 * x;
30093                 var targetHeight = this.minHeight - 2 * y;
30094                 
30095                 var scale = 1;
30096                 
30097                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30098                     scale = targetWidth / width;
30099                 }
30100                 
30101                 if(x > 0 && y == 0){
30102                     scale = targetHeight / height;
30103                 }
30104                 
30105                 if(x > 0 && y > 0){
30106                     scale = targetWidth / width;
30107                     
30108                     if(width < height){
30109                         scale = targetHeight / height;
30110                     }
30111                 }
30112                 
30113                 context.scale(scale, scale);
30114                 
30115                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30116                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30117
30118                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30119                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30120
30121                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30122                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30123                 
30124                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30125                 
30126                 break;
30127             case 270 :
30128                 
30129                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30130                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30131                 
30132                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30133                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30134                 
30135                 var targetWidth = this.minWidth - 2 * x;
30136                 var targetHeight = this.minHeight - 2 * y;
30137                 
30138                 var scale = 1;
30139                 
30140                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30141                     scale = targetWidth / width;
30142                 }
30143                 
30144                 if(x > 0 && y == 0){
30145                     scale = targetHeight / height;
30146                 }
30147                 
30148                 if(x > 0 && y > 0){
30149                     scale = targetWidth / width;
30150                     
30151                     if(width < height){
30152                         scale = targetHeight / height;
30153                     }
30154                 }
30155                 
30156                 context.scale(scale, scale);
30157                 
30158                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30159                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30160
30161                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30162                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30163                 
30164                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30165                 
30166                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30167                 
30168                 break;
30169             default : 
30170                 break;
30171         }
30172         
30173         this.cropData = canvas.toDataURL(this.cropType);
30174         
30175         if(this.fireEvent('crop', this, this.cropData) !== false){
30176             this.process(this.file, this.cropData);
30177         }
30178         
30179         return;
30180         
30181     },
30182     
30183     setThumbBoxSize : function()
30184     {
30185         var width, height;
30186         
30187         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30188             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30189             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30190             
30191             this.minWidth = width;
30192             this.minHeight = height;
30193             
30194             if(this.rotate == 90 || this.rotate == 270){
30195                 this.minWidth = height;
30196                 this.minHeight = width;
30197             }
30198         }
30199         
30200         height = 300;
30201         width = Math.ceil(this.minWidth * height / this.minHeight);
30202         
30203         if(this.minWidth > this.minHeight){
30204             width = 300;
30205             height = Math.ceil(this.minHeight * width / this.minWidth);
30206         }
30207         
30208         this.thumbEl.setStyle({
30209             width : width + 'px',
30210             height : height + 'px'
30211         });
30212
30213         return;
30214             
30215     },
30216     
30217     setThumbBoxPosition : function()
30218     {
30219         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30220         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30221         
30222         this.thumbEl.setLeft(x);
30223         this.thumbEl.setTop(y);
30224         
30225     },
30226     
30227     baseRotateLevel : function()
30228     {
30229         this.baseRotate = 1;
30230         
30231         if(
30232                 typeof(this.exif) != 'undefined' &&
30233                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30234                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30235         ){
30236             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30237         }
30238         
30239         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30240         
30241     },
30242     
30243     baseScaleLevel : function()
30244     {
30245         var width, height;
30246         
30247         if(this.isDocument){
30248             
30249             if(this.baseRotate == 6 || this.baseRotate == 8){
30250             
30251                 height = this.thumbEl.getHeight();
30252                 this.baseScale = height / this.imageEl.OriginWidth;
30253
30254                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30255                     width = this.thumbEl.getWidth();
30256                     this.baseScale = width / this.imageEl.OriginHeight;
30257                 }
30258
30259                 return;
30260             }
30261
30262             height = this.thumbEl.getHeight();
30263             this.baseScale = height / this.imageEl.OriginHeight;
30264
30265             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30266                 width = this.thumbEl.getWidth();
30267                 this.baseScale = width / this.imageEl.OriginWidth;
30268             }
30269
30270             return;
30271         }
30272         
30273         if(this.baseRotate == 6 || this.baseRotate == 8){
30274             
30275             width = this.thumbEl.getHeight();
30276             this.baseScale = width / this.imageEl.OriginHeight;
30277             
30278             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30279                 height = this.thumbEl.getWidth();
30280                 this.baseScale = height / this.imageEl.OriginHeight;
30281             }
30282             
30283             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30284                 height = this.thumbEl.getWidth();
30285                 this.baseScale = height / this.imageEl.OriginHeight;
30286                 
30287                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30288                     width = this.thumbEl.getHeight();
30289                     this.baseScale = width / this.imageEl.OriginWidth;
30290                 }
30291             }
30292             
30293             return;
30294         }
30295         
30296         width = this.thumbEl.getWidth();
30297         this.baseScale = width / this.imageEl.OriginWidth;
30298         
30299         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30300             height = this.thumbEl.getHeight();
30301             this.baseScale = height / this.imageEl.OriginHeight;
30302         }
30303         
30304         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30305             
30306             height = this.thumbEl.getHeight();
30307             this.baseScale = height / this.imageEl.OriginHeight;
30308             
30309             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30310                 width = this.thumbEl.getWidth();
30311                 this.baseScale = width / this.imageEl.OriginWidth;
30312             }
30313             
30314         }
30315         
30316         return;
30317     },
30318     
30319     getScaleLevel : function()
30320     {
30321         return this.baseScale * Math.pow(1.1, this.scale);
30322     },
30323     
30324     onTouchStart : function(e)
30325     {
30326         if(!this.canvasLoaded){
30327             this.beforeSelectFile(e);
30328             return;
30329         }
30330         
30331         var touches = e.browserEvent.touches;
30332         
30333         if(!touches){
30334             return;
30335         }
30336         
30337         if(touches.length == 1){
30338             this.onMouseDown(e);
30339             return;
30340         }
30341         
30342         if(touches.length != 2){
30343             return;
30344         }
30345         
30346         var coords = [];
30347         
30348         for(var i = 0, finger; finger = touches[i]; i++){
30349             coords.push(finger.pageX, finger.pageY);
30350         }
30351         
30352         var x = Math.pow(coords[0] - coords[2], 2);
30353         var y = Math.pow(coords[1] - coords[3], 2);
30354         
30355         this.startDistance = Math.sqrt(x + y);
30356         
30357         this.startScale = this.scale;
30358         
30359         this.pinching = true;
30360         this.dragable = false;
30361         
30362     },
30363     
30364     onTouchMove : function(e)
30365     {
30366         if(!this.pinching && !this.dragable){
30367             return;
30368         }
30369         
30370         var touches = e.browserEvent.touches;
30371         
30372         if(!touches){
30373             return;
30374         }
30375         
30376         if(this.dragable){
30377             this.onMouseMove(e);
30378             return;
30379         }
30380         
30381         var coords = [];
30382         
30383         for(var i = 0, finger; finger = touches[i]; i++){
30384             coords.push(finger.pageX, finger.pageY);
30385         }
30386         
30387         var x = Math.pow(coords[0] - coords[2], 2);
30388         var y = Math.pow(coords[1] - coords[3], 2);
30389         
30390         this.endDistance = Math.sqrt(x + y);
30391         
30392         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30393         
30394         if(!this.zoomable()){
30395             this.scale = this.startScale;
30396             return;
30397         }
30398         
30399         this.draw();
30400         
30401     },
30402     
30403     onTouchEnd : function(e)
30404     {
30405         this.pinching = false;
30406         this.dragable = false;
30407         
30408     },
30409     
30410     process : function(file, crop)
30411     {
30412         if(this.loadMask){
30413             this.maskEl.mask(this.loadingText);
30414         }
30415         
30416         this.xhr = new XMLHttpRequest();
30417         
30418         file.xhr = this.xhr;
30419
30420         this.xhr.open(this.method, this.url, true);
30421         
30422         var headers = {
30423             "Accept": "application/json",
30424             "Cache-Control": "no-cache",
30425             "X-Requested-With": "XMLHttpRequest"
30426         };
30427         
30428         for (var headerName in headers) {
30429             var headerValue = headers[headerName];
30430             if (headerValue) {
30431                 this.xhr.setRequestHeader(headerName, headerValue);
30432             }
30433         }
30434         
30435         var _this = this;
30436         
30437         this.xhr.onload = function()
30438         {
30439             _this.xhrOnLoad(_this.xhr);
30440         }
30441         
30442         this.xhr.onerror = function()
30443         {
30444             _this.xhrOnError(_this.xhr);
30445         }
30446         
30447         var formData = new FormData();
30448
30449         formData.append('returnHTML', 'NO');
30450         
30451         if(crop){
30452             formData.append('crop', crop);
30453         }
30454         
30455         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30456             formData.append(this.paramName, file, file.name);
30457         }
30458         
30459         if(typeof(file.filename) != 'undefined'){
30460             formData.append('filename', file.filename);
30461         }
30462         
30463         if(typeof(file.mimetype) != 'undefined'){
30464             formData.append('mimetype', file.mimetype);
30465         }
30466         
30467         if(this.fireEvent('arrange', this, formData) != false){
30468             this.xhr.send(formData);
30469         };
30470     },
30471     
30472     xhrOnLoad : function(xhr)
30473     {
30474         if(this.loadMask){
30475             this.maskEl.unmask();
30476         }
30477         
30478         if (xhr.readyState !== 4) {
30479             this.fireEvent('exception', this, xhr);
30480             return;
30481         }
30482
30483         var response = Roo.decode(xhr.responseText);
30484         
30485         if(!response.success){
30486             this.fireEvent('exception', this, xhr);
30487             return;
30488         }
30489         
30490         var response = Roo.decode(xhr.responseText);
30491         
30492         this.fireEvent('upload', this, response);
30493         
30494     },
30495     
30496     xhrOnError : function()
30497     {
30498         if(this.loadMask){
30499             this.maskEl.unmask();
30500         }
30501         
30502         Roo.log('xhr on error');
30503         
30504         var response = Roo.decode(xhr.responseText);
30505           
30506         Roo.log(response);
30507         
30508     },
30509     
30510     prepare : function(file)
30511     {   
30512         if(this.loadMask){
30513             this.maskEl.mask(this.loadingText);
30514         }
30515         
30516         this.file = false;
30517         this.exif = {};
30518         
30519         if(typeof(file) === 'string'){
30520             this.loadCanvas(file);
30521             return;
30522         }
30523         
30524         if(!file || !this.urlAPI){
30525             return;
30526         }
30527         
30528         this.file = file;
30529         this.cropType = file.type;
30530         
30531         var _this = this;
30532         
30533         if(this.fireEvent('prepare', this, this.file) != false){
30534             
30535             var reader = new FileReader();
30536             
30537             reader.onload = function (e) {
30538                 if (e.target.error) {
30539                     Roo.log(e.target.error);
30540                     return;
30541                 }
30542                 
30543                 var buffer = e.target.result,
30544                     dataView = new DataView(buffer),
30545                     offset = 2,
30546                     maxOffset = dataView.byteLength - 4,
30547                     markerBytes,
30548                     markerLength;
30549                 
30550                 if (dataView.getUint16(0) === 0xffd8) {
30551                     while (offset < maxOffset) {
30552                         markerBytes = dataView.getUint16(offset);
30553                         
30554                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30555                             markerLength = dataView.getUint16(offset + 2) + 2;
30556                             if (offset + markerLength > dataView.byteLength) {
30557                                 Roo.log('Invalid meta data: Invalid segment size.');
30558                                 break;
30559                             }
30560                             
30561                             if(markerBytes == 0xffe1){
30562                                 _this.parseExifData(
30563                                     dataView,
30564                                     offset,
30565                                     markerLength
30566                                 );
30567                             }
30568                             
30569                             offset += markerLength;
30570                             
30571                             continue;
30572                         }
30573                         
30574                         break;
30575                     }
30576                     
30577                 }
30578                 
30579                 var url = _this.urlAPI.createObjectURL(_this.file);
30580                 
30581                 _this.loadCanvas(url);
30582                 
30583                 return;
30584             }
30585             
30586             reader.readAsArrayBuffer(this.file);
30587             
30588         }
30589         
30590     },
30591     
30592     parseExifData : function(dataView, offset, length)
30593     {
30594         var tiffOffset = offset + 10,
30595             littleEndian,
30596             dirOffset;
30597     
30598         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30599             // No Exif data, might be XMP data instead
30600             return;
30601         }
30602         
30603         // Check for the ASCII code for "Exif" (0x45786966):
30604         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30605             // No Exif data, might be XMP data instead
30606             return;
30607         }
30608         if (tiffOffset + 8 > dataView.byteLength) {
30609             Roo.log('Invalid Exif data: Invalid segment size.');
30610             return;
30611         }
30612         // Check for the two null bytes:
30613         if (dataView.getUint16(offset + 8) !== 0x0000) {
30614             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30615             return;
30616         }
30617         // Check the byte alignment:
30618         switch (dataView.getUint16(tiffOffset)) {
30619         case 0x4949:
30620             littleEndian = true;
30621             break;
30622         case 0x4D4D:
30623             littleEndian = false;
30624             break;
30625         default:
30626             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30627             return;
30628         }
30629         // Check for the TIFF tag marker (0x002A):
30630         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30631             Roo.log('Invalid Exif data: Missing TIFF marker.');
30632             return;
30633         }
30634         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30635         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30636         
30637         this.parseExifTags(
30638             dataView,
30639             tiffOffset,
30640             tiffOffset + dirOffset,
30641             littleEndian
30642         );
30643     },
30644     
30645     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30646     {
30647         var tagsNumber,
30648             dirEndOffset,
30649             i;
30650         if (dirOffset + 6 > dataView.byteLength) {
30651             Roo.log('Invalid Exif data: Invalid directory offset.');
30652             return;
30653         }
30654         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30655         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30656         if (dirEndOffset + 4 > dataView.byteLength) {
30657             Roo.log('Invalid Exif data: Invalid directory size.');
30658             return;
30659         }
30660         for (i = 0; i < tagsNumber; i += 1) {
30661             this.parseExifTag(
30662                 dataView,
30663                 tiffOffset,
30664                 dirOffset + 2 + 12 * i, // tag offset
30665                 littleEndian
30666             );
30667         }
30668         // Return the offset to the next directory:
30669         return dataView.getUint32(dirEndOffset, littleEndian);
30670     },
30671     
30672     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30673     {
30674         var tag = dataView.getUint16(offset, littleEndian);
30675         
30676         this.exif[tag] = this.getExifValue(
30677             dataView,
30678             tiffOffset,
30679             offset,
30680             dataView.getUint16(offset + 2, littleEndian), // tag type
30681             dataView.getUint32(offset + 4, littleEndian), // tag length
30682             littleEndian
30683         );
30684     },
30685     
30686     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30687     {
30688         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30689             tagSize,
30690             dataOffset,
30691             values,
30692             i,
30693             str,
30694             c;
30695     
30696         if (!tagType) {
30697             Roo.log('Invalid Exif data: Invalid tag type.');
30698             return;
30699         }
30700         
30701         tagSize = tagType.size * length;
30702         // Determine if the value is contained in the dataOffset bytes,
30703         // or if the value at the dataOffset is a pointer to the actual data:
30704         dataOffset = tagSize > 4 ?
30705                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30706         if (dataOffset + tagSize > dataView.byteLength) {
30707             Roo.log('Invalid Exif data: Invalid data offset.');
30708             return;
30709         }
30710         if (length === 1) {
30711             return tagType.getValue(dataView, dataOffset, littleEndian);
30712         }
30713         values = [];
30714         for (i = 0; i < length; i += 1) {
30715             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30716         }
30717         
30718         if (tagType.ascii) {
30719             str = '';
30720             // Concatenate the chars:
30721             for (i = 0; i < values.length; i += 1) {
30722                 c = values[i];
30723                 // Ignore the terminating NULL byte(s):
30724                 if (c === '\u0000') {
30725                     break;
30726                 }
30727                 str += c;
30728             }
30729             return str;
30730         }
30731         return values;
30732     }
30733     
30734 });
30735
30736 Roo.apply(Roo.bootstrap.UploadCropbox, {
30737     tags : {
30738         'Orientation': 0x0112
30739     },
30740     
30741     Orientation: {
30742             1: 0, //'top-left',
30743 //            2: 'top-right',
30744             3: 180, //'bottom-right',
30745 //            4: 'bottom-left',
30746 //            5: 'left-top',
30747             6: 90, //'right-top',
30748 //            7: 'right-bottom',
30749             8: 270 //'left-bottom'
30750     },
30751     
30752     exifTagTypes : {
30753         // byte, 8-bit unsigned int:
30754         1: {
30755             getValue: function (dataView, dataOffset) {
30756                 return dataView.getUint8(dataOffset);
30757             },
30758             size: 1
30759         },
30760         // ascii, 8-bit byte:
30761         2: {
30762             getValue: function (dataView, dataOffset) {
30763                 return String.fromCharCode(dataView.getUint8(dataOffset));
30764             },
30765             size: 1,
30766             ascii: true
30767         },
30768         // short, 16 bit int:
30769         3: {
30770             getValue: function (dataView, dataOffset, littleEndian) {
30771                 return dataView.getUint16(dataOffset, littleEndian);
30772             },
30773             size: 2
30774         },
30775         // long, 32 bit int:
30776         4: {
30777             getValue: function (dataView, dataOffset, littleEndian) {
30778                 return dataView.getUint32(dataOffset, littleEndian);
30779             },
30780             size: 4
30781         },
30782         // rational = two long values, first is numerator, second is denominator:
30783         5: {
30784             getValue: function (dataView, dataOffset, littleEndian) {
30785                 return dataView.getUint32(dataOffset, littleEndian) /
30786                     dataView.getUint32(dataOffset + 4, littleEndian);
30787             },
30788             size: 8
30789         },
30790         // slong, 32 bit signed int:
30791         9: {
30792             getValue: function (dataView, dataOffset, littleEndian) {
30793                 return dataView.getInt32(dataOffset, littleEndian);
30794             },
30795             size: 4
30796         },
30797         // srational, two slongs, first is numerator, second is denominator:
30798         10: {
30799             getValue: function (dataView, dataOffset, littleEndian) {
30800                 return dataView.getInt32(dataOffset, littleEndian) /
30801                     dataView.getInt32(dataOffset + 4, littleEndian);
30802             },
30803             size: 8
30804         }
30805     },
30806     
30807     footer : {
30808         STANDARD : [
30809             {
30810                 tag : 'div',
30811                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30812                 action : 'rotate-left',
30813                 cn : [
30814                     {
30815                         tag : 'button',
30816                         cls : 'btn btn-default',
30817                         html : '<i class="fa fa-undo"></i>'
30818                     }
30819                 ]
30820             },
30821             {
30822                 tag : 'div',
30823                 cls : 'btn-group roo-upload-cropbox-picture',
30824                 action : 'picture',
30825                 cn : [
30826                     {
30827                         tag : 'button',
30828                         cls : 'btn btn-default',
30829                         html : '<i class="fa fa-picture-o"></i>'
30830                     }
30831                 ]
30832             },
30833             {
30834                 tag : 'div',
30835                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30836                 action : 'rotate-right',
30837                 cn : [
30838                     {
30839                         tag : 'button',
30840                         cls : 'btn btn-default',
30841                         html : '<i class="fa fa-repeat"></i>'
30842                     }
30843                 ]
30844             }
30845         ],
30846         DOCUMENT : [
30847             {
30848                 tag : 'div',
30849                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30850                 action : 'rotate-left',
30851                 cn : [
30852                     {
30853                         tag : 'button',
30854                         cls : 'btn btn-default',
30855                         html : '<i class="fa fa-undo"></i>'
30856                     }
30857                 ]
30858             },
30859             {
30860                 tag : 'div',
30861                 cls : 'btn-group roo-upload-cropbox-download',
30862                 action : 'download',
30863                 cn : [
30864                     {
30865                         tag : 'button',
30866                         cls : 'btn btn-default',
30867                         html : '<i class="fa fa-download"></i>'
30868                     }
30869                 ]
30870             },
30871             {
30872                 tag : 'div',
30873                 cls : 'btn-group roo-upload-cropbox-crop',
30874                 action : 'crop',
30875                 cn : [
30876                     {
30877                         tag : 'button',
30878                         cls : 'btn btn-default',
30879                         html : '<i class="fa fa-crop"></i>'
30880                     }
30881                 ]
30882             },
30883             {
30884                 tag : 'div',
30885                 cls : 'btn-group roo-upload-cropbox-trash',
30886                 action : 'trash',
30887                 cn : [
30888                     {
30889                         tag : 'button',
30890                         cls : 'btn btn-default',
30891                         html : '<i class="fa fa-trash"></i>'
30892                     }
30893                 ]
30894             },
30895             {
30896                 tag : 'div',
30897                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30898                 action : 'rotate-right',
30899                 cn : [
30900                     {
30901                         tag : 'button',
30902                         cls : 'btn btn-default',
30903                         html : '<i class="fa fa-repeat"></i>'
30904                     }
30905                 ]
30906             }
30907         ],
30908         ROTATOR : [
30909             {
30910                 tag : 'div',
30911                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30912                 action : 'rotate-left',
30913                 cn : [
30914                     {
30915                         tag : 'button',
30916                         cls : 'btn btn-default',
30917                         html : '<i class="fa fa-undo"></i>'
30918                     }
30919                 ]
30920             },
30921             {
30922                 tag : 'div',
30923                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30924                 action : 'rotate-right',
30925                 cn : [
30926                     {
30927                         tag : 'button',
30928                         cls : 'btn btn-default',
30929                         html : '<i class="fa fa-repeat"></i>'
30930                     }
30931                 ]
30932             }
30933         ]
30934     }
30935 });
30936
30937 /*
30938 * Licence: LGPL
30939 */
30940
30941 /**
30942  * @class Roo.bootstrap.DocumentManager
30943  * @extends Roo.bootstrap.Component
30944  * Bootstrap DocumentManager class
30945  * @cfg {String} paramName default 'imageUpload'
30946  * @cfg {String} toolTipName default 'filename'
30947  * @cfg {String} method default POST
30948  * @cfg {String} url action url
30949  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30950  * @cfg {Boolean} multiple multiple upload default true
30951  * @cfg {Number} thumbSize default 300
30952  * @cfg {String} fieldLabel
30953  * @cfg {Number} labelWidth default 4
30954  * @cfg {String} labelAlign (left|top) default left
30955  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30956 * @cfg {Number} labellg set the width of label (1-12)
30957  * @cfg {Number} labelmd set the width of label (1-12)
30958  * @cfg {Number} labelsm set the width of label (1-12)
30959  * @cfg {Number} labelxs set the width of label (1-12)
30960  * 
30961  * @constructor
30962  * Create a new DocumentManager
30963  * @param {Object} config The config object
30964  */
30965
30966 Roo.bootstrap.DocumentManager = function(config){
30967     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30968     
30969     this.files = [];
30970     this.delegates = [];
30971     
30972     this.addEvents({
30973         /**
30974          * @event initial
30975          * Fire when initial the DocumentManager
30976          * @param {Roo.bootstrap.DocumentManager} this
30977          */
30978         "initial" : true,
30979         /**
30980          * @event inspect
30981          * inspect selected file
30982          * @param {Roo.bootstrap.DocumentManager} this
30983          * @param {File} file
30984          */
30985         "inspect" : true,
30986         /**
30987          * @event exception
30988          * Fire when xhr load exception
30989          * @param {Roo.bootstrap.DocumentManager} this
30990          * @param {XMLHttpRequest} xhr
30991          */
30992         "exception" : true,
30993         /**
30994          * @event afterupload
30995          * Fire when xhr load exception
30996          * @param {Roo.bootstrap.DocumentManager} this
30997          * @param {XMLHttpRequest} xhr
30998          */
30999         "afterupload" : true,
31000         /**
31001          * @event prepare
31002          * prepare the form data
31003          * @param {Roo.bootstrap.DocumentManager} this
31004          * @param {Object} formData
31005          */
31006         "prepare" : true,
31007         /**
31008          * @event remove
31009          * Fire when remove the file
31010          * @param {Roo.bootstrap.DocumentManager} this
31011          * @param {Object} file
31012          */
31013         "remove" : true,
31014         /**
31015          * @event refresh
31016          * Fire after refresh the file
31017          * @param {Roo.bootstrap.DocumentManager} this
31018          */
31019         "refresh" : true,
31020         /**
31021          * @event click
31022          * Fire after click the image
31023          * @param {Roo.bootstrap.DocumentManager} this
31024          * @param {Object} file
31025          */
31026         "click" : true,
31027         /**
31028          * @event edit
31029          * Fire when upload a image and editable set to true
31030          * @param {Roo.bootstrap.DocumentManager} this
31031          * @param {Object} file
31032          */
31033         "edit" : true,
31034         /**
31035          * @event beforeselectfile
31036          * Fire before select file
31037          * @param {Roo.bootstrap.DocumentManager} this
31038          */
31039         "beforeselectfile" : true,
31040         /**
31041          * @event process
31042          * Fire before process file
31043          * @param {Roo.bootstrap.DocumentManager} this
31044          * @param {Object} file
31045          */
31046         "process" : true,
31047         /**
31048          * @event previewrendered
31049          * Fire when preview rendered
31050          * @param {Roo.bootstrap.DocumentManager} this
31051          * @param {Object} file
31052          */
31053         "previewrendered" : true,
31054         /**
31055          */
31056         "previewResize" : true
31057         
31058     });
31059 };
31060
31061 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31062     
31063     boxes : 0,
31064     inputName : '',
31065     thumbSize : 300,
31066     multiple : true,
31067     files : false,
31068     method : 'POST',
31069     url : '',
31070     paramName : 'imageUpload',
31071     toolTipName : 'filename',
31072     fieldLabel : '',
31073     labelWidth : 4,
31074     labelAlign : 'left',
31075     editable : true,
31076     delegates : false,
31077     xhr : false, 
31078     
31079     labellg : 0,
31080     labelmd : 0,
31081     labelsm : 0,
31082     labelxs : 0,
31083     
31084     getAutoCreate : function()
31085     {   
31086         var managerWidget = {
31087             tag : 'div',
31088             cls : 'roo-document-manager',
31089             cn : [
31090                 {
31091                     tag : 'input',
31092                     cls : 'roo-document-manager-selector',
31093                     type : 'file'
31094                 },
31095                 {
31096                     tag : 'div',
31097                     cls : 'roo-document-manager-uploader',
31098                     cn : [
31099                         {
31100                             tag : 'div',
31101                             cls : 'roo-document-manager-upload-btn',
31102                             html : '<i class="fa fa-plus"></i>'
31103                         }
31104                     ]
31105                     
31106                 }
31107             ]
31108         };
31109         
31110         var content = [
31111             {
31112                 tag : 'div',
31113                 cls : 'column col-md-12',
31114                 cn : managerWidget
31115             }
31116         ];
31117         
31118         if(this.fieldLabel.length){
31119             
31120             content = [
31121                 {
31122                     tag : 'div',
31123                     cls : 'column col-md-12',
31124                     html : this.fieldLabel
31125                 },
31126                 {
31127                     tag : 'div',
31128                     cls : 'column col-md-12',
31129                     cn : managerWidget
31130                 }
31131             ];
31132
31133             if(this.labelAlign == 'left'){
31134                 content = [
31135                     {
31136                         tag : 'div',
31137                         cls : 'column',
31138                         html : this.fieldLabel
31139                     },
31140                     {
31141                         tag : 'div',
31142                         cls : 'column',
31143                         cn : managerWidget
31144                     }
31145                 ];
31146                 
31147                 if(this.labelWidth > 12){
31148                     content[0].style = "width: " + this.labelWidth + 'px';
31149                 }
31150
31151                 if(this.labelWidth < 13 && this.labelmd == 0){
31152                     this.labelmd = this.labelWidth;
31153                 }
31154
31155                 if(this.labellg > 0){
31156                     content[0].cls += ' col-lg-' + this.labellg;
31157                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31158                 }
31159
31160                 if(this.labelmd > 0){
31161                     content[0].cls += ' col-md-' + this.labelmd;
31162                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31163                 }
31164
31165                 if(this.labelsm > 0){
31166                     content[0].cls += ' col-sm-' + this.labelsm;
31167                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31168                 }
31169
31170                 if(this.labelxs > 0){
31171                     content[0].cls += ' col-xs-' + this.labelxs;
31172                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31173                 }
31174                 
31175             }
31176         }
31177         
31178         var cfg = {
31179             tag : 'div',
31180             cls : 'row clearfix',
31181             cn : content
31182         };
31183         
31184         return cfg;
31185         
31186     },
31187     
31188     initEvents : function()
31189     {
31190         this.managerEl = this.el.select('.roo-document-manager', true).first();
31191         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31192         
31193         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31194         this.selectorEl.hide();
31195         
31196         if(this.multiple){
31197             this.selectorEl.attr('multiple', 'multiple');
31198         }
31199         
31200         this.selectorEl.on('change', this.onFileSelected, this);
31201         
31202         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31203         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31204         
31205         this.uploader.on('click', this.onUploaderClick, this);
31206         
31207         this.renderProgressDialog();
31208         
31209         var _this = this;
31210         
31211         window.addEventListener("resize", function() { _this.refresh(); } );
31212         
31213         this.fireEvent('initial', this);
31214     },
31215     
31216     renderProgressDialog : function()
31217     {
31218         var _this = this;
31219         
31220         this.progressDialog = new Roo.bootstrap.Modal({
31221             cls : 'roo-document-manager-progress-dialog',
31222             allow_close : false,
31223             animate : false,
31224             title : '',
31225             buttons : [
31226                 {
31227                     name  :'cancel',
31228                     weight : 'danger',
31229                     html : 'Cancel'
31230                 }
31231             ], 
31232             listeners : { 
31233                 btnclick : function() {
31234                     _this.uploadCancel();
31235                     this.hide();
31236                 }
31237             }
31238         });
31239          
31240         this.progressDialog.render(Roo.get(document.body));
31241          
31242         this.progress = new Roo.bootstrap.Progress({
31243             cls : 'roo-document-manager-progress',
31244             active : true,
31245             striped : true
31246         });
31247         
31248         this.progress.render(this.progressDialog.getChildContainer());
31249         
31250         this.progressBar = new Roo.bootstrap.ProgressBar({
31251             cls : 'roo-document-manager-progress-bar',
31252             aria_valuenow : 0,
31253             aria_valuemin : 0,
31254             aria_valuemax : 12,
31255             panel : 'success'
31256         });
31257         
31258         this.progressBar.render(this.progress.getChildContainer());
31259     },
31260     
31261     onUploaderClick : function(e)
31262     {
31263         e.preventDefault();
31264      
31265         if(this.fireEvent('beforeselectfile', this) != false){
31266             this.selectorEl.dom.click();
31267         }
31268         
31269     },
31270     
31271     onFileSelected : function(e)
31272     {
31273         e.preventDefault();
31274         
31275         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31276             return;
31277         }
31278         
31279         Roo.each(this.selectorEl.dom.files, function(file){
31280             if(this.fireEvent('inspect', this, file) != false){
31281                 this.files.push(file);
31282             }
31283         }, this);
31284         
31285         this.queue();
31286         
31287     },
31288     
31289     queue : function()
31290     {
31291         this.selectorEl.dom.value = '';
31292         
31293         if(!this.files || !this.files.length){
31294             return;
31295         }
31296         
31297         if(this.boxes > 0 && this.files.length > this.boxes){
31298             this.files = this.files.slice(0, this.boxes);
31299         }
31300         
31301         this.uploader.show();
31302         
31303         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31304             this.uploader.hide();
31305         }
31306         
31307         var _this = this;
31308         
31309         var files = [];
31310         
31311         var docs = [];
31312         
31313         Roo.each(this.files, function(file){
31314             
31315             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31316                 var f = this.renderPreview(file);
31317                 files.push(f);
31318                 return;
31319             }
31320             
31321             if(file.type.indexOf('image') != -1){
31322                 this.delegates.push(
31323                     (function(){
31324                         _this.process(file);
31325                     }).createDelegate(this)
31326                 );
31327         
31328                 return;
31329             }
31330             
31331             docs.push(
31332                 (function(){
31333                     _this.process(file);
31334                 }).createDelegate(this)
31335             );
31336             
31337         }, this);
31338         
31339         this.files = files;
31340         
31341         this.delegates = this.delegates.concat(docs);
31342         
31343         if(!this.delegates.length){
31344             this.refresh();
31345             return;
31346         }
31347         
31348         this.progressBar.aria_valuemax = this.delegates.length;
31349         
31350         this.arrange();
31351         
31352         return;
31353     },
31354     
31355     arrange : function()
31356     {
31357         if(!this.delegates.length){
31358             this.progressDialog.hide();
31359             this.refresh();
31360             return;
31361         }
31362         
31363         var delegate = this.delegates.shift();
31364         
31365         this.progressDialog.show();
31366         
31367         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31368         
31369         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31370         
31371         delegate();
31372     },
31373     
31374     refresh : function()
31375     {
31376         this.uploader.show();
31377         
31378         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31379             this.uploader.hide();
31380         }
31381         
31382         Roo.isTouch ? this.closable(false) : this.closable(true);
31383         
31384         this.fireEvent('refresh', this);
31385     },
31386     
31387     onRemove : function(e, el, o)
31388     {
31389         e.preventDefault();
31390         
31391         this.fireEvent('remove', this, o);
31392         
31393     },
31394     
31395     remove : function(o)
31396     {
31397         var files = [];
31398         
31399         Roo.each(this.files, function(file){
31400             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31401                 files.push(file);
31402                 return;
31403             }
31404
31405             o.target.remove();
31406
31407         }, this);
31408         
31409         this.files = files;
31410         
31411         this.refresh();
31412     },
31413     
31414     clear : function()
31415     {
31416         Roo.each(this.files, function(file){
31417             if(!file.target){
31418                 return;
31419             }
31420             
31421             file.target.remove();
31422
31423         }, this);
31424         
31425         this.files = [];
31426         
31427         this.refresh();
31428     },
31429     
31430     onClick : function(e, el, o)
31431     {
31432         e.preventDefault();
31433         
31434         this.fireEvent('click', this, o);
31435         
31436     },
31437     
31438     closable : function(closable)
31439     {
31440         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31441             
31442             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31443             
31444             if(closable){
31445                 el.show();
31446                 return;
31447             }
31448             
31449             el.hide();
31450             
31451         }, this);
31452     },
31453     
31454     xhrOnLoad : function(xhr)
31455     {
31456         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31457             el.remove();
31458         }, this);
31459         
31460         if (xhr.readyState !== 4) {
31461             this.arrange();
31462             this.fireEvent('exception', this, xhr);
31463             return;
31464         }
31465
31466         var response = Roo.decode(xhr.responseText);
31467         
31468         if(!response.success){
31469             this.arrange();
31470             this.fireEvent('exception', this, xhr);
31471             return;
31472         }
31473         
31474         var file = this.renderPreview(response.data);
31475         
31476         this.files.push(file);
31477         
31478         this.arrange();
31479         
31480         this.fireEvent('afterupload', this, xhr);
31481         
31482     },
31483     
31484     xhrOnError : function(xhr)
31485     {
31486         Roo.log('xhr on error');
31487         
31488         var response = Roo.decode(xhr.responseText);
31489           
31490         Roo.log(response);
31491         
31492         this.arrange();
31493     },
31494     
31495     process : function(file)
31496     {
31497         if(this.fireEvent('process', this, file) !== false){
31498             if(this.editable && file.type.indexOf('image') != -1){
31499                 this.fireEvent('edit', this, file);
31500                 return;
31501             }
31502
31503             this.uploadStart(file, false);
31504
31505             return;
31506         }
31507         
31508     },
31509     
31510     uploadStart : function(file, crop)
31511     {
31512         this.xhr = new XMLHttpRequest();
31513         
31514         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31515             this.arrange();
31516             return;
31517         }
31518         
31519         file.xhr = this.xhr;
31520             
31521         this.managerEl.createChild({
31522             tag : 'div',
31523             cls : 'roo-document-manager-loading',
31524             cn : [
31525                 {
31526                     tag : 'div',
31527                     tooltip : file.name,
31528                     cls : 'roo-document-manager-thumb',
31529                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31530                 }
31531             ]
31532
31533         });
31534
31535         this.xhr.open(this.method, this.url, true);
31536         
31537         var headers = {
31538             "Accept": "application/json",
31539             "Cache-Control": "no-cache",
31540             "X-Requested-With": "XMLHttpRequest"
31541         };
31542         
31543         for (var headerName in headers) {
31544             var headerValue = headers[headerName];
31545             if (headerValue) {
31546                 this.xhr.setRequestHeader(headerName, headerValue);
31547             }
31548         }
31549         
31550         var _this = this;
31551         
31552         this.xhr.onload = function()
31553         {
31554             _this.xhrOnLoad(_this.xhr);
31555         }
31556         
31557         this.xhr.onerror = function()
31558         {
31559             _this.xhrOnError(_this.xhr);
31560         }
31561         
31562         var formData = new FormData();
31563
31564         formData.append('returnHTML', 'NO');
31565         
31566         if(crop){
31567             formData.append('crop', crop);
31568         }
31569         
31570         formData.append(this.paramName, file, file.name);
31571         
31572         var options = {
31573             file : file, 
31574             manually : false
31575         };
31576         
31577         if(this.fireEvent('prepare', this, formData, options) != false){
31578             
31579             if(options.manually){
31580                 return;
31581             }
31582             
31583             this.xhr.send(formData);
31584             return;
31585         };
31586         
31587         this.uploadCancel();
31588     },
31589     
31590     uploadCancel : function()
31591     {
31592         if (this.xhr) {
31593             this.xhr.abort();
31594         }
31595         
31596         this.delegates = [];
31597         
31598         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31599             el.remove();
31600         }, this);
31601         
31602         this.arrange();
31603     },
31604     
31605     renderPreview : function(file)
31606     {
31607         if(typeof(file.target) != 'undefined' && file.target){
31608             return file;
31609         }
31610         
31611         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31612         
31613         var previewEl = this.managerEl.createChild({
31614             tag : 'div',
31615             cls : 'roo-document-manager-preview',
31616             cn : [
31617                 {
31618                     tag : 'div',
31619                     tooltip : file[this.toolTipName],
31620                     cls : 'roo-document-manager-thumb',
31621                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31622                 },
31623                 {
31624                     tag : 'button',
31625                     cls : 'close',
31626                     html : '<i class="fa fa-times-circle"></i>'
31627                 }
31628             ]
31629         });
31630
31631         var close = previewEl.select('button.close', true).first();
31632
31633         close.on('click', this.onRemove, this, file);
31634
31635         file.target = previewEl;
31636
31637         var image = previewEl.select('img', true).first();
31638         
31639         var _this = this;
31640         
31641         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31642         
31643         image.on('click', this.onClick, this, file);
31644         
31645         this.fireEvent('previewrendered', this, file);
31646         
31647         return file;
31648         
31649     },
31650     
31651     onPreviewLoad : function(file, image)
31652     {
31653         if(typeof(file.target) == 'undefined' || !file.target){
31654             return;
31655         }
31656         
31657         var width = image.dom.naturalWidth || image.dom.width;
31658         var height = image.dom.naturalHeight || image.dom.height;
31659         
31660         if(!this.previewResize) {
31661             return;
31662         }
31663         
31664         if(width > height){
31665             file.target.addClass('wide');
31666             return;
31667         }
31668         
31669         file.target.addClass('tall');
31670         return;
31671         
31672     },
31673     
31674     uploadFromSource : function(file, crop)
31675     {
31676         this.xhr = new XMLHttpRequest();
31677         
31678         this.managerEl.createChild({
31679             tag : 'div',
31680             cls : 'roo-document-manager-loading',
31681             cn : [
31682                 {
31683                     tag : 'div',
31684                     tooltip : file.name,
31685                     cls : 'roo-document-manager-thumb',
31686                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31687                 }
31688             ]
31689
31690         });
31691
31692         this.xhr.open(this.method, this.url, true);
31693         
31694         var headers = {
31695             "Accept": "application/json",
31696             "Cache-Control": "no-cache",
31697             "X-Requested-With": "XMLHttpRequest"
31698         };
31699         
31700         for (var headerName in headers) {
31701             var headerValue = headers[headerName];
31702             if (headerValue) {
31703                 this.xhr.setRequestHeader(headerName, headerValue);
31704             }
31705         }
31706         
31707         var _this = this;
31708         
31709         this.xhr.onload = function()
31710         {
31711             _this.xhrOnLoad(_this.xhr);
31712         }
31713         
31714         this.xhr.onerror = function()
31715         {
31716             _this.xhrOnError(_this.xhr);
31717         }
31718         
31719         var formData = new FormData();
31720
31721         formData.append('returnHTML', 'NO');
31722         
31723         formData.append('crop', crop);
31724         
31725         if(typeof(file.filename) != 'undefined'){
31726             formData.append('filename', file.filename);
31727         }
31728         
31729         if(typeof(file.mimetype) != 'undefined'){
31730             formData.append('mimetype', file.mimetype);
31731         }
31732         
31733         Roo.log(formData);
31734         
31735         if(this.fireEvent('prepare', this, formData) != false){
31736             this.xhr.send(formData);
31737         };
31738     }
31739 });
31740
31741 /*
31742 * Licence: LGPL
31743 */
31744
31745 /**
31746  * @class Roo.bootstrap.DocumentViewer
31747  * @extends Roo.bootstrap.Component
31748  * Bootstrap DocumentViewer class
31749  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31750  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31751  * 
31752  * @constructor
31753  * Create a new DocumentViewer
31754  * @param {Object} config The config object
31755  */
31756
31757 Roo.bootstrap.DocumentViewer = function(config){
31758     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31759     
31760     this.addEvents({
31761         /**
31762          * @event initial
31763          * Fire after initEvent
31764          * @param {Roo.bootstrap.DocumentViewer} this
31765          */
31766         "initial" : true,
31767         /**
31768          * @event click
31769          * Fire after click
31770          * @param {Roo.bootstrap.DocumentViewer} this
31771          */
31772         "click" : true,
31773         /**
31774          * @event download
31775          * Fire after download button
31776          * @param {Roo.bootstrap.DocumentViewer} this
31777          */
31778         "download" : true,
31779         /**
31780          * @event trash
31781          * Fire after trash button
31782          * @param {Roo.bootstrap.DocumentViewer} this
31783          */
31784         "trash" : true
31785         
31786     });
31787 };
31788
31789 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31790     
31791     showDownload : true,
31792     
31793     showTrash : true,
31794     
31795     getAutoCreate : function()
31796     {
31797         var cfg = {
31798             tag : 'div',
31799             cls : 'roo-document-viewer',
31800             cn : [
31801                 {
31802                     tag : 'div',
31803                     cls : 'roo-document-viewer-body',
31804                     cn : [
31805                         {
31806                             tag : 'div',
31807                             cls : 'roo-document-viewer-thumb',
31808                             cn : [
31809                                 {
31810                                     tag : 'img',
31811                                     cls : 'roo-document-viewer-image'
31812                                 }
31813                             ]
31814                         }
31815                     ]
31816                 },
31817                 {
31818                     tag : 'div',
31819                     cls : 'roo-document-viewer-footer',
31820                     cn : {
31821                         tag : 'div',
31822                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31823                         cn : [
31824                             {
31825                                 tag : 'div',
31826                                 cls : 'btn-group roo-document-viewer-download',
31827                                 cn : [
31828                                     {
31829                                         tag : 'button',
31830                                         cls : 'btn btn-default',
31831                                         html : '<i class="fa fa-download"></i>'
31832                                     }
31833                                 ]
31834                             },
31835                             {
31836                                 tag : 'div',
31837                                 cls : 'btn-group roo-document-viewer-trash',
31838                                 cn : [
31839                                     {
31840                                         tag : 'button',
31841                                         cls : 'btn btn-default',
31842                                         html : '<i class="fa fa-trash"></i>'
31843                                     }
31844                                 ]
31845                             }
31846                         ]
31847                     }
31848                 }
31849             ]
31850         };
31851         
31852         return cfg;
31853     },
31854     
31855     initEvents : function()
31856     {
31857         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31858         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31859         
31860         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31861         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31862         
31863         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31864         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31865         
31866         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31867         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31868         
31869         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31870         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31871         
31872         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31873         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31874         
31875         this.bodyEl.on('click', this.onClick, this);
31876         this.downloadBtn.on('click', this.onDownload, this);
31877         this.trashBtn.on('click', this.onTrash, this);
31878         
31879         this.downloadBtn.hide();
31880         this.trashBtn.hide();
31881         
31882         if(this.showDownload){
31883             this.downloadBtn.show();
31884         }
31885         
31886         if(this.showTrash){
31887             this.trashBtn.show();
31888         }
31889         
31890         if(!this.showDownload && !this.showTrash) {
31891             this.footerEl.hide();
31892         }
31893         
31894     },
31895     
31896     initial : function()
31897     {
31898         this.fireEvent('initial', this);
31899         
31900     },
31901     
31902     onClick : function(e)
31903     {
31904         e.preventDefault();
31905         
31906         this.fireEvent('click', this);
31907     },
31908     
31909     onDownload : function(e)
31910     {
31911         e.preventDefault();
31912         
31913         this.fireEvent('download', this);
31914     },
31915     
31916     onTrash : function(e)
31917     {
31918         e.preventDefault();
31919         
31920         this.fireEvent('trash', this);
31921     }
31922     
31923 });
31924 /*
31925  * - LGPL
31926  *
31927  * nav progress bar
31928  * 
31929  */
31930
31931 /**
31932  * @class Roo.bootstrap.NavProgressBar
31933  * @extends Roo.bootstrap.Component
31934  * Bootstrap NavProgressBar class
31935  * 
31936  * @constructor
31937  * Create a new nav progress bar
31938  * @param {Object} config The config object
31939  */
31940
31941 Roo.bootstrap.NavProgressBar = function(config){
31942     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31943
31944     this.bullets = this.bullets || [];
31945    
31946 //    Roo.bootstrap.NavProgressBar.register(this);
31947      this.addEvents({
31948         /**
31949              * @event changed
31950              * Fires when the active item changes
31951              * @param {Roo.bootstrap.NavProgressBar} this
31952              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31953              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31954          */
31955         'changed': true
31956      });
31957     
31958 };
31959
31960 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31961     
31962     bullets : [],
31963     barItems : [],
31964     
31965     getAutoCreate : function()
31966     {
31967         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31968         
31969         cfg = {
31970             tag : 'div',
31971             cls : 'roo-navigation-bar-group',
31972             cn : [
31973                 {
31974                     tag : 'div',
31975                     cls : 'roo-navigation-top-bar'
31976                 },
31977                 {
31978                     tag : 'div',
31979                     cls : 'roo-navigation-bullets-bar',
31980                     cn : [
31981                         {
31982                             tag : 'ul',
31983                             cls : 'roo-navigation-bar'
31984                         }
31985                     ]
31986                 },
31987                 
31988                 {
31989                     tag : 'div',
31990                     cls : 'roo-navigation-bottom-bar'
31991                 }
31992             ]
31993             
31994         };
31995         
31996         return cfg;
31997         
31998     },
31999     
32000     initEvents: function() 
32001     {
32002         
32003     },
32004     
32005     onRender : function(ct, position) 
32006     {
32007         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32008         
32009         if(this.bullets.length){
32010             Roo.each(this.bullets, function(b){
32011                this.addItem(b);
32012             }, this);
32013         }
32014         
32015         this.format();
32016         
32017     },
32018     
32019     addItem : function(cfg)
32020     {
32021         var item = new Roo.bootstrap.NavProgressItem(cfg);
32022         
32023         item.parentId = this.id;
32024         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32025         
32026         if(cfg.html){
32027             var top = new Roo.bootstrap.Element({
32028                 tag : 'div',
32029                 cls : 'roo-navigation-bar-text'
32030             });
32031             
32032             var bottom = new Roo.bootstrap.Element({
32033                 tag : 'div',
32034                 cls : 'roo-navigation-bar-text'
32035             });
32036             
32037             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32038             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32039             
32040             var topText = new Roo.bootstrap.Element({
32041                 tag : 'span',
32042                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32043             });
32044             
32045             var bottomText = new Roo.bootstrap.Element({
32046                 tag : 'span',
32047                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32048             });
32049             
32050             topText.onRender(top.el, null);
32051             bottomText.onRender(bottom.el, null);
32052             
32053             item.topEl = top;
32054             item.bottomEl = bottom;
32055         }
32056         
32057         this.barItems.push(item);
32058         
32059         return item;
32060     },
32061     
32062     getActive : function()
32063     {
32064         var active = false;
32065         
32066         Roo.each(this.barItems, function(v){
32067             
32068             if (!v.isActive()) {
32069                 return;
32070             }
32071             
32072             active = v;
32073             return false;
32074             
32075         });
32076         
32077         return active;
32078     },
32079     
32080     setActiveItem : function(item)
32081     {
32082         var prev = false;
32083         
32084         Roo.each(this.barItems, function(v){
32085             if (v.rid == item.rid) {
32086                 return ;
32087             }
32088             
32089             if (v.isActive()) {
32090                 v.setActive(false);
32091                 prev = v;
32092             }
32093         });
32094
32095         item.setActive(true);
32096         
32097         this.fireEvent('changed', this, item, prev);
32098     },
32099     
32100     getBarItem: function(rid)
32101     {
32102         var ret = false;
32103         
32104         Roo.each(this.barItems, function(e) {
32105             if (e.rid != rid) {
32106                 return;
32107             }
32108             
32109             ret =  e;
32110             return false;
32111         });
32112         
32113         return ret;
32114     },
32115     
32116     indexOfItem : function(item)
32117     {
32118         var index = false;
32119         
32120         Roo.each(this.barItems, function(v, i){
32121             
32122             if (v.rid != item.rid) {
32123                 return;
32124             }
32125             
32126             index = i;
32127             return false
32128         });
32129         
32130         return index;
32131     },
32132     
32133     setActiveNext : function()
32134     {
32135         var i = this.indexOfItem(this.getActive());
32136         
32137         if (i > this.barItems.length) {
32138             return;
32139         }
32140         
32141         this.setActiveItem(this.barItems[i+1]);
32142     },
32143     
32144     setActivePrev : function()
32145     {
32146         var i = this.indexOfItem(this.getActive());
32147         
32148         if (i  < 1) {
32149             return;
32150         }
32151         
32152         this.setActiveItem(this.barItems[i-1]);
32153     },
32154     
32155     format : function()
32156     {
32157         if(!this.barItems.length){
32158             return;
32159         }
32160      
32161         var width = 100 / this.barItems.length;
32162         
32163         Roo.each(this.barItems, function(i){
32164             i.el.setStyle('width', width + '%');
32165             i.topEl.el.setStyle('width', width + '%');
32166             i.bottomEl.el.setStyle('width', width + '%');
32167         }, this);
32168         
32169     }
32170     
32171 });
32172 /*
32173  * - LGPL
32174  *
32175  * Nav Progress Item
32176  * 
32177  */
32178
32179 /**
32180  * @class Roo.bootstrap.NavProgressItem
32181  * @extends Roo.bootstrap.Component
32182  * Bootstrap NavProgressItem class
32183  * @cfg {String} rid the reference id
32184  * @cfg {Boolean} active (true|false) Is item active default false
32185  * @cfg {Boolean} disabled (true|false) Is item active default false
32186  * @cfg {String} html
32187  * @cfg {String} position (top|bottom) text position default bottom
32188  * @cfg {String} icon show icon instead of number
32189  * 
32190  * @constructor
32191  * Create a new NavProgressItem
32192  * @param {Object} config The config object
32193  */
32194 Roo.bootstrap.NavProgressItem = function(config){
32195     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32196     this.addEvents({
32197         // raw events
32198         /**
32199          * @event click
32200          * The raw click event for the entire grid.
32201          * @param {Roo.bootstrap.NavProgressItem} this
32202          * @param {Roo.EventObject} e
32203          */
32204         "click" : true
32205     });
32206    
32207 };
32208
32209 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32210     
32211     rid : '',
32212     active : false,
32213     disabled : false,
32214     html : '',
32215     position : 'bottom',
32216     icon : false,
32217     
32218     getAutoCreate : function()
32219     {
32220         var iconCls = 'roo-navigation-bar-item-icon';
32221         
32222         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32223         
32224         var cfg = {
32225             tag: 'li',
32226             cls: 'roo-navigation-bar-item',
32227             cn : [
32228                 {
32229                     tag : 'i',
32230                     cls : iconCls
32231                 }
32232             ]
32233         };
32234         
32235         if(this.active){
32236             cfg.cls += ' active';
32237         }
32238         if(this.disabled){
32239             cfg.cls += ' disabled';
32240         }
32241         
32242         return cfg;
32243     },
32244     
32245     disable : function()
32246     {
32247         this.setDisabled(true);
32248     },
32249     
32250     enable : function()
32251     {
32252         this.setDisabled(false);
32253     },
32254     
32255     initEvents: function() 
32256     {
32257         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32258         
32259         this.iconEl.on('click', this.onClick, this);
32260     },
32261     
32262     onClick : function(e)
32263     {
32264         e.preventDefault();
32265         
32266         if(this.disabled){
32267             return;
32268         }
32269         
32270         if(this.fireEvent('click', this, e) === false){
32271             return;
32272         };
32273         
32274         this.parent().setActiveItem(this);
32275     },
32276     
32277     isActive: function () 
32278     {
32279         return this.active;
32280     },
32281     
32282     setActive : function(state)
32283     {
32284         if(this.active == state){
32285             return;
32286         }
32287         
32288         this.active = state;
32289         
32290         if (state) {
32291             this.el.addClass('active');
32292             return;
32293         }
32294         
32295         this.el.removeClass('active');
32296         
32297         return;
32298     },
32299     
32300     setDisabled : function(state)
32301     {
32302         if(this.disabled == state){
32303             return;
32304         }
32305         
32306         this.disabled = state;
32307         
32308         if (state) {
32309             this.el.addClass('disabled');
32310             return;
32311         }
32312         
32313         this.el.removeClass('disabled');
32314     },
32315     
32316     tooltipEl : function()
32317     {
32318         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32319     }
32320 });
32321  
32322
32323  /*
32324  * - LGPL
32325  *
32326  * FieldLabel
32327  * 
32328  */
32329
32330 /**
32331  * @class Roo.bootstrap.FieldLabel
32332  * @extends Roo.bootstrap.Component
32333  * Bootstrap FieldLabel class
32334  * @cfg {String} html contents of the element
32335  * @cfg {String} tag tag of the element default label
32336  * @cfg {String} cls class of the element
32337  * @cfg {String} target label target 
32338  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32339  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32340  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32341  * @cfg {String} iconTooltip default "This field is required"
32342  * @cfg {String} indicatorpos (left|right) default left
32343  * 
32344  * @constructor
32345  * Create a new FieldLabel
32346  * @param {Object} config The config object
32347  */
32348
32349 Roo.bootstrap.FieldLabel = function(config){
32350     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32351     
32352     this.addEvents({
32353             /**
32354              * @event invalid
32355              * Fires after the field has been marked as invalid.
32356              * @param {Roo.form.FieldLabel} this
32357              * @param {String} msg The validation message
32358              */
32359             invalid : true,
32360             /**
32361              * @event valid
32362              * Fires after the field has been validated with no errors.
32363              * @param {Roo.form.FieldLabel} this
32364              */
32365             valid : true
32366         });
32367 };
32368
32369 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32370     
32371     tag: 'label',
32372     cls: '',
32373     html: '',
32374     target: '',
32375     allowBlank : true,
32376     invalidClass : 'has-warning',
32377     validClass : 'has-success',
32378     iconTooltip : 'This field is required',
32379     indicatorpos : 'left',
32380     
32381     getAutoCreate : function(){
32382         
32383         var cls = "";
32384         if (!this.allowBlank) {
32385             cls  = "visible";
32386         }
32387         
32388         var cfg = {
32389             tag : this.tag,
32390             cls : 'roo-bootstrap-field-label ' + this.cls,
32391             for : this.target,
32392             cn : [
32393                 {
32394                     tag : 'i',
32395                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32396                     tooltip : this.iconTooltip
32397                 },
32398                 {
32399                     tag : 'span',
32400                     html : this.html
32401                 }
32402             ] 
32403         };
32404         
32405         if(this.indicatorpos == 'right'){
32406             var cfg = {
32407                 tag : this.tag,
32408                 cls : 'roo-bootstrap-field-label ' + this.cls,
32409                 for : this.target,
32410                 cn : [
32411                     {
32412                         tag : 'span',
32413                         html : this.html
32414                     },
32415                     {
32416                         tag : 'i',
32417                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32418                         tooltip : this.iconTooltip
32419                     }
32420                 ] 
32421             };
32422         }
32423         
32424         return cfg;
32425     },
32426     
32427     initEvents: function() 
32428     {
32429         Roo.bootstrap.Element.superclass.initEvents.call(this);
32430         
32431         this.indicator = this.indicatorEl();
32432         
32433         if(this.indicator){
32434             this.indicator.removeClass('visible');
32435             this.indicator.addClass('invisible');
32436         }
32437         
32438         Roo.bootstrap.FieldLabel.register(this);
32439     },
32440     
32441     indicatorEl : function()
32442     {
32443         var indicator = this.el.select('i.roo-required-indicator',true).first();
32444         
32445         if(!indicator){
32446             return false;
32447         }
32448         
32449         return indicator;
32450         
32451     },
32452     
32453     /**
32454      * Mark this field as valid
32455      */
32456     markValid : function()
32457     {
32458         if(this.indicator){
32459             this.indicator.removeClass('visible');
32460             this.indicator.addClass('invisible');
32461         }
32462         if (Roo.bootstrap.version == 3) {
32463             this.el.removeClass(this.invalidClass);
32464             this.el.addClass(this.validClass);
32465         } else {
32466             this.el.removeClass('is-invalid');
32467             this.el.addClass('is-valid');
32468         }
32469         
32470         
32471         this.fireEvent('valid', this);
32472     },
32473     
32474     /**
32475      * Mark this field as invalid
32476      * @param {String} msg The validation message
32477      */
32478     markInvalid : function(msg)
32479     {
32480         if(this.indicator){
32481             this.indicator.removeClass('invisible');
32482             this.indicator.addClass('visible');
32483         }
32484           if (Roo.bootstrap.version == 3) {
32485             this.el.removeClass(this.validClass);
32486             this.el.addClass(this.invalidClass);
32487         } else {
32488             this.el.removeClass('is-valid');
32489             this.el.addClass('is-invalid');
32490         }
32491         
32492         
32493         this.fireEvent('invalid', this, msg);
32494     }
32495     
32496    
32497 });
32498
32499 Roo.apply(Roo.bootstrap.FieldLabel, {
32500     
32501     groups: {},
32502     
32503      /**
32504     * register a FieldLabel Group
32505     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32506     */
32507     register : function(label)
32508     {
32509         if(this.groups.hasOwnProperty(label.target)){
32510             return;
32511         }
32512      
32513         this.groups[label.target] = label;
32514         
32515     },
32516     /**
32517     * fetch a FieldLabel Group based on the target
32518     * @param {string} target
32519     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32520     */
32521     get: function(target) {
32522         if (typeof(this.groups[target]) == 'undefined') {
32523             return false;
32524         }
32525         
32526         return this.groups[target] ;
32527     }
32528 });
32529
32530  
32531
32532  /*
32533  * - LGPL
32534  *
32535  * page DateSplitField.
32536  * 
32537  */
32538
32539
32540 /**
32541  * @class Roo.bootstrap.DateSplitField
32542  * @extends Roo.bootstrap.Component
32543  * Bootstrap DateSplitField class
32544  * @cfg {string} fieldLabel - the label associated
32545  * @cfg {Number} labelWidth set the width of label (0-12)
32546  * @cfg {String} labelAlign (top|left)
32547  * @cfg {Boolean} dayAllowBlank (true|false) default false
32548  * @cfg {Boolean} monthAllowBlank (true|false) default false
32549  * @cfg {Boolean} yearAllowBlank (true|false) default false
32550  * @cfg {string} dayPlaceholder 
32551  * @cfg {string} monthPlaceholder
32552  * @cfg {string} yearPlaceholder
32553  * @cfg {string} dayFormat default 'd'
32554  * @cfg {string} monthFormat default 'm'
32555  * @cfg {string} yearFormat default 'Y'
32556  * @cfg {Number} labellg set the width of label (1-12)
32557  * @cfg {Number} labelmd set the width of label (1-12)
32558  * @cfg {Number} labelsm set the width of label (1-12)
32559  * @cfg {Number} labelxs set the width of label (1-12)
32560
32561  *     
32562  * @constructor
32563  * Create a new DateSplitField
32564  * @param {Object} config The config object
32565  */
32566
32567 Roo.bootstrap.DateSplitField = function(config){
32568     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32569     
32570     this.addEvents({
32571         // raw events
32572          /**
32573          * @event years
32574          * getting the data of years
32575          * @param {Roo.bootstrap.DateSplitField} this
32576          * @param {Object} years
32577          */
32578         "years" : true,
32579         /**
32580          * @event days
32581          * getting the data of days
32582          * @param {Roo.bootstrap.DateSplitField} this
32583          * @param {Object} days
32584          */
32585         "days" : true,
32586         /**
32587          * @event invalid
32588          * Fires after the field has been marked as invalid.
32589          * @param {Roo.form.Field} this
32590          * @param {String} msg The validation message
32591          */
32592         invalid : true,
32593        /**
32594          * @event valid
32595          * Fires after the field has been validated with no errors.
32596          * @param {Roo.form.Field} this
32597          */
32598         valid : true
32599     });
32600 };
32601
32602 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32603     
32604     fieldLabel : '',
32605     labelAlign : 'top',
32606     labelWidth : 3,
32607     dayAllowBlank : false,
32608     monthAllowBlank : false,
32609     yearAllowBlank : false,
32610     dayPlaceholder : '',
32611     monthPlaceholder : '',
32612     yearPlaceholder : '',
32613     dayFormat : 'd',
32614     monthFormat : 'm',
32615     yearFormat : 'Y',
32616     isFormField : true,
32617     labellg : 0,
32618     labelmd : 0,
32619     labelsm : 0,
32620     labelxs : 0,
32621     
32622     getAutoCreate : function()
32623     {
32624         var cfg = {
32625             tag : 'div',
32626             cls : 'row roo-date-split-field-group',
32627             cn : [
32628                 {
32629                     tag : 'input',
32630                     type : 'hidden',
32631                     cls : 'form-hidden-field roo-date-split-field-group-value',
32632                     name : this.name
32633                 }
32634             ]
32635         };
32636         
32637         var labelCls = 'col-md-12';
32638         var contentCls = 'col-md-4';
32639         
32640         if(this.fieldLabel){
32641             
32642             var label = {
32643                 tag : 'div',
32644                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32645                 cn : [
32646                     {
32647                         tag : 'label',
32648                         html : this.fieldLabel
32649                     }
32650                 ]
32651             };
32652             
32653             if(this.labelAlign == 'left'){
32654             
32655                 if(this.labelWidth > 12){
32656                     label.style = "width: " + this.labelWidth + 'px';
32657                 }
32658
32659                 if(this.labelWidth < 13 && this.labelmd == 0){
32660                     this.labelmd = this.labelWidth;
32661                 }
32662
32663                 if(this.labellg > 0){
32664                     labelCls = ' col-lg-' + this.labellg;
32665                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32666                 }
32667
32668                 if(this.labelmd > 0){
32669                     labelCls = ' col-md-' + this.labelmd;
32670                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32671                 }
32672
32673                 if(this.labelsm > 0){
32674                     labelCls = ' col-sm-' + this.labelsm;
32675                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32676                 }
32677
32678                 if(this.labelxs > 0){
32679                     labelCls = ' col-xs-' + this.labelxs;
32680                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32681                 }
32682             }
32683             
32684             label.cls += ' ' + labelCls;
32685             
32686             cfg.cn.push(label);
32687         }
32688         
32689         Roo.each(['day', 'month', 'year'], function(t){
32690             cfg.cn.push({
32691                 tag : 'div',
32692                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32693             });
32694         }, this);
32695         
32696         return cfg;
32697     },
32698     
32699     inputEl: function ()
32700     {
32701         return this.el.select('.roo-date-split-field-group-value', true).first();
32702     },
32703     
32704     onRender : function(ct, position) 
32705     {
32706         var _this = this;
32707         
32708         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32709         
32710         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32711         
32712         this.dayField = new Roo.bootstrap.ComboBox({
32713             allowBlank : this.dayAllowBlank,
32714             alwaysQuery : true,
32715             displayField : 'value',
32716             editable : false,
32717             fieldLabel : '',
32718             forceSelection : true,
32719             mode : 'local',
32720             placeholder : this.dayPlaceholder,
32721             selectOnFocus : true,
32722             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32723             triggerAction : 'all',
32724             typeAhead : true,
32725             valueField : 'value',
32726             store : new Roo.data.SimpleStore({
32727                 data : (function() {    
32728                     var days = [];
32729                     _this.fireEvent('days', _this, days);
32730                     return days;
32731                 })(),
32732                 fields : [ 'value' ]
32733             }),
32734             listeners : {
32735                 select : function (_self, record, index)
32736                 {
32737                     _this.setValue(_this.getValue());
32738                 }
32739             }
32740         });
32741
32742         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32743         
32744         this.monthField = new Roo.bootstrap.MonthField({
32745             after : '<i class=\"fa fa-calendar\"></i>',
32746             allowBlank : this.monthAllowBlank,
32747             placeholder : this.monthPlaceholder,
32748             readOnly : true,
32749             listeners : {
32750                 render : function (_self)
32751                 {
32752                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32753                         e.preventDefault();
32754                         _self.focus();
32755                     });
32756                 },
32757                 select : function (_self, oldvalue, newvalue)
32758                 {
32759                     _this.setValue(_this.getValue());
32760                 }
32761             }
32762         });
32763         
32764         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32765         
32766         this.yearField = new Roo.bootstrap.ComboBox({
32767             allowBlank : this.yearAllowBlank,
32768             alwaysQuery : true,
32769             displayField : 'value',
32770             editable : false,
32771             fieldLabel : '',
32772             forceSelection : true,
32773             mode : 'local',
32774             placeholder : this.yearPlaceholder,
32775             selectOnFocus : true,
32776             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32777             triggerAction : 'all',
32778             typeAhead : true,
32779             valueField : 'value',
32780             store : new Roo.data.SimpleStore({
32781                 data : (function() {
32782                     var years = [];
32783                     _this.fireEvent('years', _this, years);
32784                     return years;
32785                 })(),
32786                 fields : [ 'value' ]
32787             }),
32788             listeners : {
32789                 select : function (_self, record, index)
32790                 {
32791                     _this.setValue(_this.getValue());
32792                 }
32793             }
32794         });
32795
32796         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32797     },
32798     
32799     setValue : function(v, format)
32800     {
32801         this.inputEl.dom.value = v;
32802         
32803         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32804         
32805         var d = Date.parseDate(v, f);
32806         
32807         if(!d){
32808             this.validate();
32809             return;
32810         }
32811         
32812         this.setDay(d.format(this.dayFormat));
32813         this.setMonth(d.format(this.monthFormat));
32814         this.setYear(d.format(this.yearFormat));
32815         
32816         this.validate();
32817         
32818         return;
32819     },
32820     
32821     setDay : function(v)
32822     {
32823         this.dayField.setValue(v);
32824         this.inputEl.dom.value = this.getValue();
32825         this.validate();
32826         return;
32827     },
32828     
32829     setMonth : function(v)
32830     {
32831         this.monthField.setValue(v, true);
32832         this.inputEl.dom.value = this.getValue();
32833         this.validate();
32834         return;
32835     },
32836     
32837     setYear : function(v)
32838     {
32839         this.yearField.setValue(v);
32840         this.inputEl.dom.value = this.getValue();
32841         this.validate();
32842         return;
32843     },
32844     
32845     getDay : function()
32846     {
32847         return this.dayField.getValue();
32848     },
32849     
32850     getMonth : function()
32851     {
32852         return this.monthField.getValue();
32853     },
32854     
32855     getYear : function()
32856     {
32857         return this.yearField.getValue();
32858     },
32859     
32860     getValue : function()
32861     {
32862         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32863         
32864         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32865         
32866         return date;
32867     },
32868     
32869     reset : function()
32870     {
32871         this.setDay('');
32872         this.setMonth('');
32873         this.setYear('');
32874         this.inputEl.dom.value = '';
32875         this.validate();
32876         return;
32877     },
32878     
32879     validate : function()
32880     {
32881         var d = this.dayField.validate();
32882         var m = this.monthField.validate();
32883         var y = this.yearField.validate();
32884         
32885         var valid = true;
32886         
32887         if(
32888                 (!this.dayAllowBlank && !d) ||
32889                 (!this.monthAllowBlank && !m) ||
32890                 (!this.yearAllowBlank && !y)
32891         ){
32892             valid = false;
32893         }
32894         
32895         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32896             return valid;
32897         }
32898         
32899         if(valid){
32900             this.markValid();
32901             return valid;
32902         }
32903         
32904         this.markInvalid();
32905         
32906         return valid;
32907     },
32908     
32909     markValid : function()
32910     {
32911         
32912         var label = this.el.select('label', true).first();
32913         var icon = this.el.select('i.fa-star', true).first();
32914
32915         if(label && icon){
32916             icon.remove();
32917         }
32918         
32919         this.fireEvent('valid', this);
32920     },
32921     
32922      /**
32923      * Mark this field as invalid
32924      * @param {String} msg The validation message
32925      */
32926     markInvalid : function(msg)
32927     {
32928         
32929         var label = this.el.select('label', true).first();
32930         var icon = this.el.select('i.fa-star', true).first();
32931
32932         if(label && !icon){
32933             this.el.select('.roo-date-split-field-label', true).createChild({
32934                 tag : 'i',
32935                 cls : 'text-danger fa fa-lg fa-star',
32936                 tooltip : 'This field is required',
32937                 style : 'margin-right:5px;'
32938             }, label, true);
32939         }
32940         
32941         this.fireEvent('invalid', this, msg);
32942     },
32943     
32944     clearInvalid : function()
32945     {
32946         var label = this.el.select('label', true).first();
32947         var icon = this.el.select('i.fa-star', true).first();
32948
32949         if(label && icon){
32950             icon.remove();
32951         }
32952         
32953         this.fireEvent('valid', this);
32954     },
32955     
32956     getName: function()
32957     {
32958         return this.name;
32959     }
32960     
32961 });
32962
32963  /**
32964  *
32965  * This is based on 
32966  * http://masonry.desandro.com
32967  *
32968  * The idea is to render all the bricks based on vertical width...
32969  *
32970  * The original code extends 'outlayer' - we might need to use that....
32971  * 
32972  */
32973
32974
32975 /**
32976  * @class Roo.bootstrap.LayoutMasonry
32977  * @extends Roo.bootstrap.Component
32978  * Bootstrap Layout Masonry class
32979  * 
32980  * @constructor
32981  * Create a new Element
32982  * @param {Object} config The config object
32983  */
32984
32985 Roo.bootstrap.LayoutMasonry = function(config){
32986     
32987     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32988     
32989     this.bricks = [];
32990     
32991     Roo.bootstrap.LayoutMasonry.register(this);
32992     
32993     this.addEvents({
32994         // raw events
32995         /**
32996          * @event layout
32997          * Fire after layout the items
32998          * @param {Roo.bootstrap.LayoutMasonry} this
32999          * @param {Roo.EventObject} e
33000          */
33001         "layout" : true
33002     });
33003     
33004 };
33005
33006 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33007     
33008     /**
33009      * @cfg {Boolean} isLayoutInstant = no animation?
33010      */   
33011     isLayoutInstant : false, // needed?
33012    
33013     /**
33014      * @cfg {Number} boxWidth  width of the columns
33015      */   
33016     boxWidth : 450,
33017     
33018       /**
33019      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33020      */   
33021     boxHeight : 0,
33022     
33023     /**
33024      * @cfg {Number} padWidth padding below box..
33025      */   
33026     padWidth : 10, 
33027     
33028     /**
33029      * @cfg {Number} gutter gutter width..
33030      */   
33031     gutter : 10,
33032     
33033      /**
33034      * @cfg {Number} maxCols maximum number of columns
33035      */   
33036     
33037     maxCols: 0,
33038     
33039     /**
33040      * @cfg {Boolean} isAutoInitial defalut true
33041      */   
33042     isAutoInitial : true, 
33043     
33044     containerWidth: 0,
33045     
33046     /**
33047      * @cfg {Boolean} isHorizontal defalut false
33048      */   
33049     isHorizontal : false, 
33050
33051     currentSize : null,
33052     
33053     tag: 'div',
33054     
33055     cls: '',
33056     
33057     bricks: null, //CompositeElement
33058     
33059     cols : 1,
33060     
33061     _isLayoutInited : false,
33062     
33063 //    isAlternative : false, // only use for vertical layout...
33064     
33065     /**
33066      * @cfg {Number} alternativePadWidth padding below box..
33067      */   
33068     alternativePadWidth : 50,
33069     
33070     selectedBrick : [],
33071     
33072     getAutoCreate : function(){
33073         
33074         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33075         
33076         var cfg = {
33077             tag: this.tag,
33078             cls: 'blog-masonary-wrapper ' + this.cls,
33079             cn : {
33080                 cls : 'mas-boxes masonary'
33081             }
33082         };
33083         
33084         return cfg;
33085     },
33086     
33087     getChildContainer: function( )
33088     {
33089         if (this.boxesEl) {
33090             return this.boxesEl;
33091         }
33092         
33093         this.boxesEl = this.el.select('.mas-boxes').first();
33094         
33095         return this.boxesEl;
33096     },
33097     
33098     
33099     initEvents : function()
33100     {
33101         var _this = this;
33102         
33103         if(this.isAutoInitial){
33104             Roo.log('hook children rendered');
33105             this.on('childrenrendered', function() {
33106                 Roo.log('children rendered');
33107                 _this.initial();
33108             } ,this);
33109         }
33110     },
33111     
33112     initial : function()
33113     {
33114         this.selectedBrick = [];
33115         
33116         this.currentSize = this.el.getBox(true);
33117         
33118         Roo.EventManager.onWindowResize(this.resize, this); 
33119
33120         if(!this.isAutoInitial){
33121             this.layout();
33122             return;
33123         }
33124         
33125         this.layout();
33126         
33127         return;
33128         //this.layout.defer(500,this);
33129         
33130     },
33131     
33132     resize : function()
33133     {
33134         var cs = this.el.getBox(true);
33135         
33136         if (
33137                 this.currentSize.width == cs.width && 
33138                 this.currentSize.x == cs.x && 
33139                 this.currentSize.height == cs.height && 
33140                 this.currentSize.y == cs.y 
33141         ) {
33142             Roo.log("no change in with or X or Y");
33143             return;
33144         }
33145         
33146         this.currentSize = cs;
33147         
33148         this.layout();
33149         
33150     },
33151     
33152     layout : function()
33153     {   
33154         this._resetLayout();
33155         
33156         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33157         
33158         this.layoutItems( isInstant );
33159       
33160         this._isLayoutInited = true;
33161         
33162         this.fireEvent('layout', this);
33163         
33164     },
33165     
33166     _resetLayout : function()
33167     {
33168         if(this.isHorizontal){
33169             this.horizontalMeasureColumns();
33170             return;
33171         }
33172         
33173         this.verticalMeasureColumns();
33174         
33175     },
33176     
33177     verticalMeasureColumns : function()
33178     {
33179         this.getContainerWidth();
33180         
33181 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33182 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33183 //            return;
33184 //        }
33185         
33186         var boxWidth = this.boxWidth + this.padWidth;
33187         
33188         if(this.containerWidth < this.boxWidth){
33189             boxWidth = this.containerWidth
33190         }
33191         
33192         var containerWidth = this.containerWidth;
33193         
33194         var cols = Math.floor(containerWidth / boxWidth);
33195         
33196         this.cols = Math.max( cols, 1 );
33197         
33198         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33199         
33200         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33201         
33202         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33203         
33204         this.colWidth = boxWidth + avail - this.padWidth;
33205         
33206         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33207         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33208     },
33209     
33210     horizontalMeasureColumns : function()
33211     {
33212         this.getContainerWidth();
33213         
33214         var boxWidth = this.boxWidth;
33215         
33216         if(this.containerWidth < boxWidth){
33217             boxWidth = this.containerWidth;
33218         }
33219         
33220         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33221         
33222         this.el.setHeight(boxWidth);
33223         
33224     },
33225     
33226     getContainerWidth : function()
33227     {
33228         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33229     },
33230     
33231     layoutItems : function( isInstant )
33232     {
33233         Roo.log(this.bricks);
33234         
33235         var items = Roo.apply([], this.bricks);
33236         
33237         if(this.isHorizontal){
33238             this._horizontalLayoutItems( items , isInstant );
33239             return;
33240         }
33241         
33242 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33243 //            this._verticalAlternativeLayoutItems( items , isInstant );
33244 //            return;
33245 //        }
33246         
33247         this._verticalLayoutItems( items , isInstant );
33248         
33249     },
33250     
33251     _verticalLayoutItems : function ( items , isInstant)
33252     {
33253         if ( !items || !items.length ) {
33254             return;
33255         }
33256         
33257         var standard = [
33258             ['xs', 'xs', 'xs', 'tall'],
33259             ['xs', 'xs', 'tall'],
33260             ['xs', 'xs', 'sm'],
33261             ['xs', 'xs', 'xs'],
33262             ['xs', 'tall'],
33263             ['xs', 'sm'],
33264             ['xs', 'xs'],
33265             ['xs'],
33266             
33267             ['sm', 'xs', 'xs'],
33268             ['sm', 'xs'],
33269             ['sm'],
33270             
33271             ['tall', 'xs', 'xs', 'xs'],
33272             ['tall', 'xs', 'xs'],
33273             ['tall', 'xs'],
33274             ['tall']
33275             
33276         ];
33277         
33278         var queue = [];
33279         
33280         var boxes = [];
33281         
33282         var box = [];
33283         
33284         Roo.each(items, function(item, k){
33285             
33286             switch (item.size) {
33287                 // these layouts take up a full box,
33288                 case 'md' :
33289                 case 'md-left' :
33290                 case 'md-right' :
33291                 case 'wide' :
33292                     
33293                     if(box.length){
33294                         boxes.push(box);
33295                         box = [];
33296                     }
33297                     
33298                     boxes.push([item]);
33299                     
33300                     break;
33301                     
33302                 case 'xs' :
33303                 case 'sm' :
33304                 case 'tall' :
33305                     
33306                     box.push(item);
33307                     
33308                     break;
33309                 default :
33310                     break;
33311                     
33312             }
33313             
33314         }, this);
33315         
33316         if(box.length){
33317             boxes.push(box);
33318             box = [];
33319         }
33320         
33321         var filterPattern = function(box, length)
33322         {
33323             if(!box.length){
33324                 return;
33325             }
33326             
33327             var match = false;
33328             
33329             var pattern = box.slice(0, length);
33330             
33331             var format = [];
33332             
33333             Roo.each(pattern, function(i){
33334                 format.push(i.size);
33335             }, this);
33336             
33337             Roo.each(standard, function(s){
33338                 
33339                 if(String(s) != String(format)){
33340                     return;
33341                 }
33342                 
33343                 match = true;
33344                 return false;
33345                 
33346             }, this);
33347             
33348             if(!match && length == 1){
33349                 return;
33350             }
33351             
33352             if(!match){
33353                 filterPattern(box, length - 1);
33354                 return;
33355             }
33356                 
33357             queue.push(pattern);
33358
33359             box = box.slice(length, box.length);
33360
33361             filterPattern(box, 4);
33362
33363             return;
33364             
33365         }
33366         
33367         Roo.each(boxes, function(box, k){
33368             
33369             if(!box.length){
33370                 return;
33371             }
33372             
33373             if(box.length == 1){
33374                 queue.push(box);
33375                 return;
33376             }
33377             
33378             filterPattern(box, 4);
33379             
33380         }, this);
33381         
33382         this._processVerticalLayoutQueue( queue, isInstant );
33383         
33384     },
33385     
33386 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33387 //    {
33388 //        if ( !items || !items.length ) {
33389 //            return;
33390 //        }
33391 //
33392 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33393 //        
33394 //    },
33395     
33396     _horizontalLayoutItems : function ( items , isInstant)
33397     {
33398         if ( !items || !items.length || items.length < 3) {
33399             return;
33400         }
33401         
33402         items.reverse();
33403         
33404         var eItems = items.slice(0, 3);
33405         
33406         items = items.slice(3, items.length);
33407         
33408         var standard = [
33409             ['xs', 'xs', 'xs', 'wide'],
33410             ['xs', 'xs', 'wide'],
33411             ['xs', 'xs', 'sm'],
33412             ['xs', 'xs', 'xs'],
33413             ['xs', 'wide'],
33414             ['xs', 'sm'],
33415             ['xs', 'xs'],
33416             ['xs'],
33417             
33418             ['sm', 'xs', 'xs'],
33419             ['sm', 'xs'],
33420             ['sm'],
33421             
33422             ['wide', 'xs', 'xs', 'xs'],
33423             ['wide', 'xs', 'xs'],
33424             ['wide', 'xs'],
33425             ['wide'],
33426             
33427             ['wide-thin']
33428         ];
33429         
33430         var queue = [];
33431         
33432         var boxes = [];
33433         
33434         var box = [];
33435         
33436         Roo.each(items, function(item, k){
33437             
33438             switch (item.size) {
33439                 case 'md' :
33440                 case 'md-left' :
33441                 case 'md-right' :
33442                 case 'tall' :
33443                     
33444                     if(box.length){
33445                         boxes.push(box);
33446                         box = [];
33447                     }
33448                     
33449                     boxes.push([item]);
33450                     
33451                     break;
33452                     
33453                 case 'xs' :
33454                 case 'sm' :
33455                 case 'wide' :
33456                 case 'wide-thin' :
33457                     
33458                     box.push(item);
33459                     
33460                     break;
33461                 default :
33462                     break;
33463                     
33464             }
33465             
33466         }, this);
33467         
33468         if(box.length){
33469             boxes.push(box);
33470             box = [];
33471         }
33472         
33473         var filterPattern = function(box, length)
33474         {
33475             if(!box.length){
33476                 return;
33477             }
33478             
33479             var match = false;
33480             
33481             var pattern = box.slice(0, length);
33482             
33483             var format = [];
33484             
33485             Roo.each(pattern, function(i){
33486                 format.push(i.size);
33487             }, this);
33488             
33489             Roo.each(standard, function(s){
33490                 
33491                 if(String(s) != String(format)){
33492                     return;
33493                 }
33494                 
33495                 match = true;
33496                 return false;
33497                 
33498             }, this);
33499             
33500             if(!match && length == 1){
33501                 return;
33502             }
33503             
33504             if(!match){
33505                 filterPattern(box, length - 1);
33506                 return;
33507             }
33508                 
33509             queue.push(pattern);
33510
33511             box = box.slice(length, box.length);
33512
33513             filterPattern(box, 4);
33514
33515             return;
33516             
33517         }
33518         
33519         Roo.each(boxes, function(box, k){
33520             
33521             if(!box.length){
33522                 return;
33523             }
33524             
33525             if(box.length == 1){
33526                 queue.push(box);
33527                 return;
33528             }
33529             
33530             filterPattern(box, 4);
33531             
33532         }, this);
33533         
33534         
33535         var prune = [];
33536         
33537         var pos = this.el.getBox(true);
33538         
33539         var minX = pos.x;
33540         
33541         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33542         
33543         var hit_end = false;
33544         
33545         Roo.each(queue, function(box){
33546             
33547             if(hit_end){
33548                 
33549                 Roo.each(box, function(b){
33550                 
33551                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33552                     b.el.hide();
33553
33554                 }, this);
33555
33556                 return;
33557             }
33558             
33559             var mx = 0;
33560             
33561             Roo.each(box, function(b){
33562                 
33563                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33564                 b.el.show();
33565
33566                 mx = Math.max(mx, b.x);
33567                 
33568             }, this);
33569             
33570             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33571             
33572             if(maxX < minX){
33573                 
33574                 Roo.each(box, function(b){
33575                 
33576                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33577                     b.el.hide();
33578                     
33579                 }, this);
33580                 
33581                 hit_end = true;
33582                 
33583                 return;
33584             }
33585             
33586             prune.push(box);
33587             
33588         }, this);
33589         
33590         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33591     },
33592     
33593     /** Sets position of item in DOM
33594     * @param {Element} item
33595     * @param {Number} x - horizontal position
33596     * @param {Number} y - vertical position
33597     * @param {Boolean} isInstant - disables transitions
33598     */
33599     _processVerticalLayoutQueue : function( queue, isInstant )
33600     {
33601         var pos = this.el.getBox(true);
33602         var x = pos.x;
33603         var y = pos.y;
33604         var maxY = [];
33605         
33606         for (var i = 0; i < this.cols; i++){
33607             maxY[i] = pos.y;
33608         }
33609         
33610         Roo.each(queue, function(box, k){
33611             
33612             var col = k % this.cols;
33613             
33614             Roo.each(box, function(b,kk){
33615                 
33616                 b.el.position('absolute');
33617                 
33618                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33619                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33620                 
33621                 if(b.size == 'md-left' || b.size == 'md-right'){
33622                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33623                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33624                 }
33625                 
33626                 b.el.setWidth(width);
33627                 b.el.setHeight(height);
33628                 // iframe?
33629                 b.el.select('iframe',true).setSize(width,height);
33630                 
33631             }, this);
33632             
33633             for (var i = 0; i < this.cols; i++){
33634                 
33635                 if(maxY[i] < maxY[col]){
33636                     col = i;
33637                     continue;
33638                 }
33639                 
33640                 col = Math.min(col, i);
33641                 
33642             }
33643             
33644             x = pos.x + col * (this.colWidth + this.padWidth);
33645             
33646             y = maxY[col];
33647             
33648             var positions = [];
33649             
33650             switch (box.length){
33651                 case 1 :
33652                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33653                     break;
33654                 case 2 :
33655                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33656                     break;
33657                 case 3 :
33658                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33659                     break;
33660                 case 4 :
33661                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33662                     break;
33663                 default :
33664                     break;
33665             }
33666             
33667             Roo.each(box, function(b,kk){
33668                 
33669                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33670                 
33671                 var sz = b.el.getSize();
33672                 
33673                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33674                 
33675             }, this);
33676             
33677         }, this);
33678         
33679         var mY = 0;
33680         
33681         for (var i = 0; i < this.cols; i++){
33682             mY = Math.max(mY, maxY[i]);
33683         }
33684         
33685         this.el.setHeight(mY - pos.y);
33686         
33687     },
33688     
33689 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33690 //    {
33691 //        var pos = this.el.getBox(true);
33692 //        var x = pos.x;
33693 //        var y = pos.y;
33694 //        var maxX = pos.right;
33695 //        
33696 //        var maxHeight = 0;
33697 //        
33698 //        Roo.each(items, function(item, k){
33699 //            
33700 //            var c = k % 2;
33701 //            
33702 //            item.el.position('absolute');
33703 //                
33704 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33705 //
33706 //            item.el.setWidth(width);
33707 //
33708 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33709 //
33710 //            item.el.setHeight(height);
33711 //            
33712 //            if(c == 0){
33713 //                item.el.setXY([x, y], isInstant ? false : true);
33714 //            } else {
33715 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33716 //            }
33717 //            
33718 //            y = y + height + this.alternativePadWidth;
33719 //            
33720 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33721 //            
33722 //        }, this);
33723 //        
33724 //        this.el.setHeight(maxHeight);
33725 //        
33726 //    },
33727     
33728     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33729     {
33730         var pos = this.el.getBox(true);
33731         
33732         var minX = pos.x;
33733         var minY = pos.y;
33734         
33735         var maxX = pos.right;
33736         
33737         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33738         
33739         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33740         
33741         Roo.each(queue, function(box, k){
33742             
33743             Roo.each(box, function(b, kk){
33744                 
33745                 b.el.position('absolute');
33746                 
33747                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33748                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33749                 
33750                 if(b.size == 'md-left' || b.size == 'md-right'){
33751                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33752                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33753                 }
33754                 
33755                 b.el.setWidth(width);
33756                 b.el.setHeight(height);
33757                 
33758             }, this);
33759             
33760             if(!box.length){
33761                 return;
33762             }
33763             
33764             var positions = [];
33765             
33766             switch (box.length){
33767                 case 1 :
33768                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33769                     break;
33770                 case 2 :
33771                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33772                     break;
33773                 case 3 :
33774                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33775                     break;
33776                 case 4 :
33777                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33778                     break;
33779                 default :
33780                     break;
33781             }
33782             
33783             Roo.each(box, function(b,kk){
33784                 
33785                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33786                 
33787                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33788                 
33789             }, this);
33790             
33791         }, this);
33792         
33793     },
33794     
33795     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33796     {
33797         Roo.each(eItems, function(b,k){
33798             
33799             b.size = (k == 0) ? 'sm' : 'xs';
33800             b.x = (k == 0) ? 2 : 1;
33801             b.y = (k == 0) ? 2 : 1;
33802             
33803             b.el.position('absolute');
33804             
33805             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33806                 
33807             b.el.setWidth(width);
33808             
33809             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33810             
33811             b.el.setHeight(height);
33812             
33813         }, this);
33814
33815         var positions = [];
33816         
33817         positions.push({
33818             x : maxX - this.unitWidth * 2 - this.gutter,
33819             y : minY
33820         });
33821         
33822         positions.push({
33823             x : maxX - this.unitWidth,
33824             y : minY + (this.unitWidth + this.gutter) * 2
33825         });
33826         
33827         positions.push({
33828             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33829             y : minY
33830         });
33831         
33832         Roo.each(eItems, function(b,k){
33833             
33834             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33835
33836         }, this);
33837         
33838     },
33839     
33840     getVerticalOneBoxColPositions : function(x, y, box)
33841     {
33842         var pos = [];
33843         
33844         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33845         
33846         if(box[0].size == 'md-left'){
33847             rand = 0;
33848         }
33849         
33850         if(box[0].size == 'md-right'){
33851             rand = 1;
33852         }
33853         
33854         pos.push({
33855             x : x + (this.unitWidth + this.gutter) * rand,
33856             y : y
33857         });
33858         
33859         return pos;
33860     },
33861     
33862     getVerticalTwoBoxColPositions : function(x, y, box)
33863     {
33864         var pos = [];
33865         
33866         if(box[0].size == 'xs'){
33867             
33868             pos.push({
33869                 x : x,
33870                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33871             });
33872
33873             pos.push({
33874                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33875                 y : y
33876             });
33877             
33878             return pos;
33879             
33880         }
33881         
33882         pos.push({
33883             x : x,
33884             y : y
33885         });
33886
33887         pos.push({
33888             x : x + (this.unitWidth + this.gutter) * 2,
33889             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33890         });
33891         
33892         return pos;
33893         
33894     },
33895     
33896     getVerticalThreeBoxColPositions : function(x, y, box)
33897     {
33898         var pos = [];
33899         
33900         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33901             
33902             pos.push({
33903                 x : x,
33904                 y : y
33905             });
33906
33907             pos.push({
33908                 x : x + (this.unitWidth + this.gutter) * 1,
33909                 y : y
33910             });
33911             
33912             pos.push({
33913                 x : x + (this.unitWidth + this.gutter) * 2,
33914                 y : y
33915             });
33916             
33917             return pos;
33918             
33919         }
33920         
33921         if(box[0].size == 'xs' && box[1].size == 'xs'){
33922             
33923             pos.push({
33924                 x : x,
33925                 y : y
33926             });
33927
33928             pos.push({
33929                 x : x,
33930                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33931             });
33932             
33933             pos.push({
33934                 x : x + (this.unitWidth + this.gutter) * 1,
33935                 y : y
33936             });
33937             
33938             return pos;
33939             
33940         }
33941         
33942         pos.push({
33943             x : x,
33944             y : y
33945         });
33946
33947         pos.push({
33948             x : x + (this.unitWidth + this.gutter) * 2,
33949             y : y
33950         });
33951
33952         pos.push({
33953             x : x + (this.unitWidth + this.gutter) * 2,
33954             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33955         });
33956             
33957         return pos;
33958         
33959     },
33960     
33961     getVerticalFourBoxColPositions : function(x, y, box)
33962     {
33963         var pos = [];
33964         
33965         if(box[0].size == 'xs'){
33966             
33967             pos.push({
33968                 x : x,
33969                 y : y
33970             });
33971
33972             pos.push({
33973                 x : x,
33974                 y : y + (this.unitHeight + this.gutter) * 1
33975             });
33976             
33977             pos.push({
33978                 x : x,
33979                 y : y + (this.unitHeight + this.gutter) * 2
33980             });
33981             
33982             pos.push({
33983                 x : x + (this.unitWidth + this.gutter) * 1,
33984                 y : y
33985             });
33986             
33987             return pos;
33988             
33989         }
33990         
33991         pos.push({
33992             x : x,
33993             y : y
33994         });
33995
33996         pos.push({
33997             x : x + (this.unitWidth + this.gutter) * 2,
33998             y : y
33999         });
34000
34001         pos.push({
34002             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34003             y : y + (this.unitHeight + this.gutter) * 1
34004         });
34005
34006         pos.push({
34007             x : x + (this.unitWidth + this.gutter) * 2,
34008             y : y + (this.unitWidth + this.gutter) * 2
34009         });
34010
34011         return pos;
34012         
34013     },
34014     
34015     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34016     {
34017         var pos = [];
34018         
34019         if(box[0].size == 'md-left'){
34020             pos.push({
34021                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34022                 y : minY
34023             });
34024             
34025             return pos;
34026         }
34027         
34028         if(box[0].size == 'md-right'){
34029             pos.push({
34030                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34031                 y : minY + (this.unitWidth + this.gutter) * 1
34032             });
34033             
34034             return pos;
34035         }
34036         
34037         var rand = Math.floor(Math.random() * (4 - box[0].y));
34038         
34039         pos.push({
34040             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34041             y : minY + (this.unitWidth + this.gutter) * rand
34042         });
34043         
34044         return pos;
34045         
34046     },
34047     
34048     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34049     {
34050         var pos = [];
34051         
34052         if(box[0].size == 'xs'){
34053             
34054             pos.push({
34055                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34056                 y : minY
34057             });
34058
34059             pos.push({
34060                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34061                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34062             });
34063             
34064             return pos;
34065             
34066         }
34067         
34068         pos.push({
34069             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34070             y : minY
34071         });
34072
34073         pos.push({
34074             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34075             y : minY + (this.unitWidth + this.gutter) * 2
34076         });
34077         
34078         return pos;
34079         
34080     },
34081     
34082     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34083     {
34084         var pos = [];
34085         
34086         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34087             
34088             pos.push({
34089                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34090                 y : minY
34091             });
34092
34093             pos.push({
34094                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34095                 y : minY + (this.unitWidth + this.gutter) * 1
34096             });
34097             
34098             pos.push({
34099                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34100                 y : minY + (this.unitWidth + this.gutter) * 2
34101             });
34102             
34103             return pos;
34104             
34105         }
34106         
34107         if(box[0].size == 'xs' && box[1].size == 'xs'){
34108             
34109             pos.push({
34110                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34111                 y : minY
34112             });
34113
34114             pos.push({
34115                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34116                 y : minY
34117             });
34118             
34119             pos.push({
34120                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34121                 y : minY + (this.unitWidth + this.gutter) * 1
34122             });
34123             
34124             return pos;
34125             
34126         }
34127         
34128         pos.push({
34129             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34130             y : minY
34131         });
34132
34133         pos.push({
34134             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34135             y : minY + (this.unitWidth + this.gutter) * 2
34136         });
34137
34138         pos.push({
34139             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34140             y : minY + (this.unitWidth + this.gutter) * 2
34141         });
34142             
34143         return pos;
34144         
34145     },
34146     
34147     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34148     {
34149         var pos = [];
34150         
34151         if(box[0].size == 'xs'){
34152             
34153             pos.push({
34154                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34155                 y : minY
34156             });
34157
34158             pos.push({
34159                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34160                 y : minY
34161             });
34162             
34163             pos.push({
34164                 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),
34165                 y : minY
34166             });
34167             
34168             pos.push({
34169                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34170                 y : minY + (this.unitWidth + this.gutter) * 1
34171             });
34172             
34173             return pos;
34174             
34175         }
34176         
34177         pos.push({
34178             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34179             y : minY
34180         });
34181         
34182         pos.push({
34183             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34184             y : minY + (this.unitWidth + this.gutter) * 2
34185         });
34186         
34187         pos.push({
34188             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34189             y : minY + (this.unitWidth + this.gutter) * 2
34190         });
34191         
34192         pos.push({
34193             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),
34194             y : minY + (this.unitWidth + this.gutter) * 2
34195         });
34196
34197         return pos;
34198         
34199     },
34200     
34201     /**
34202     * remove a Masonry Brick
34203     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34204     */
34205     removeBrick : function(brick_id)
34206     {
34207         if (!brick_id) {
34208             return;
34209         }
34210         
34211         for (var i = 0; i<this.bricks.length; i++) {
34212             if (this.bricks[i].id == brick_id) {
34213                 this.bricks.splice(i,1);
34214                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34215                 this.initial();
34216             }
34217         }
34218     },
34219     
34220     /**
34221     * adds a Masonry Brick
34222     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34223     */
34224     addBrick : function(cfg)
34225     {
34226         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34227         //this.register(cn);
34228         cn.parentId = this.id;
34229         cn.render(this.el);
34230         return cn;
34231     },
34232     
34233     /**
34234     * register a Masonry Brick
34235     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34236     */
34237     
34238     register : function(brick)
34239     {
34240         this.bricks.push(brick);
34241         brick.masonryId = this.id;
34242     },
34243     
34244     /**
34245     * clear all the Masonry Brick
34246     */
34247     clearAll : function()
34248     {
34249         this.bricks = [];
34250         //this.getChildContainer().dom.innerHTML = "";
34251         this.el.dom.innerHTML = '';
34252     },
34253     
34254     getSelected : function()
34255     {
34256         if (!this.selectedBrick) {
34257             return false;
34258         }
34259         
34260         return this.selectedBrick;
34261     }
34262 });
34263
34264 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34265     
34266     groups: {},
34267      /**
34268     * register a Masonry Layout
34269     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34270     */
34271     
34272     register : function(layout)
34273     {
34274         this.groups[layout.id] = layout;
34275     },
34276     /**
34277     * fetch a  Masonry Layout based on the masonry layout ID
34278     * @param {string} the masonry layout to add
34279     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34280     */
34281     
34282     get: function(layout_id) {
34283         if (typeof(this.groups[layout_id]) == 'undefined') {
34284             return false;
34285         }
34286         return this.groups[layout_id] ;
34287     }
34288     
34289     
34290     
34291 });
34292
34293  
34294
34295  /**
34296  *
34297  * This is based on 
34298  * http://masonry.desandro.com
34299  *
34300  * The idea is to render all the bricks based on vertical width...
34301  *
34302  * The original code extends 'outlayer' - we might need to use that....
34303  * 
34304  */
34305
34306
34307 /**
34308  * @class Roo.bootstrap.LayoutMasonryAuto
34309  * @extends Roo.bootstrap.Component
34310  * Bootstrap Layout Masonry class
34311  * 
34312  * @constructor
34313  * Create a new Element
34314  * @param {Object} config The config object
34315  */
34316
34317 Roo.bootstrap.LayoutMasonryAuto = function(config){
34318     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34319 };
34320
34321 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34322     
34323       /**
34324      * @cfg {Boolean} isFitWidth  - resize the width..
34325      */   
34326     isFitWidth : false,  // options..
34327     /**
34328      * @cfg {Boolean} isOriginLeft = left align?
34329      */   
34330     isOriginLeft : true,
34331     /**
34332      * @cfg {Boolean} isOriginTop = top align?
34333      */   
34334     isOriginTop : false,
34335     /**
34336      * @cfg {Boolean} isLayoutInstant = no animation?
34337      */   
34338     isLayoutInstant : false, // needed?
34339     /**
34340      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34341      */   
34342     isResizingContainer : true,
34343     /**
34344      * @cfg {Number} columnWidth  width of the columns 
34345      */   
34346     
34347     columnWidth : 0,
34348     
34349     /**
34350      * @cfg {Number} maxCols maximum number of columns
34351      */   
34352     
34353     maxCols: 0,
34354     /**
34355      * @cfg {Number} padHeight padding below box..
34356      */   
34357     
34358     padHeight : 10, 
34359     
34360     /**
34361      * @cfg {Boolean} isAutoInitial defalut true
34362      */   
34363     
34364     isAutoInitial : true, 
34365     
34366     // private?
34367     gutter : 0,
34368     
34369     containerWidth: 0,
34370     initialColumnWidth : 0,
34371     currentSize : null,
34372     
34373     colYs : null, // array.
34374     maxY : 0,
34375     padWidth: 10,
34376     
34377     
34378     tag: 'div',
34379     cls: '',
34380     bricks: null, //CompositeElement
34381     cols : 0, // array?
34382     // element : null, // wrapped now this.el
34383     _isLayoutInited : null, 
34384     
34385     
34386     getAutoCreate : function(){
34387         
34388         var cfg = {
34389             tag: this.tag,
34390             cls: 'blog-masonary-wrapper ' + this.cls,
34391             cn : {
34392                 cls : 'mas-boxes masonary'
34393             }
34394         };
34395         
34396         return cfg;
34397     },
34398     
34399     getChildContainer: function( )
34400     {
34401         if (this.boxesEl) {
34402             return this.boxesEl;
34403         }
34404         
34405         this.boxesEl = this.el.select('.mas-boxes').first();
34406         
34407         return this.boxesEl;
34408     },
34409     
34410     
34411     initEvents : function()
34412     {
34413         var _this = this;
34414         
34415         if(this.isAutoInitial){
34416             Roo.log('hook children rendered');
34417             this.on('childrenrendered', function() {
34418                 Roo.log('children rendered');
34419                 _this.initial();
34420             } ,this);
34421         }
34422         
34423     },
34424     
34425     initial : function()
34426     {
34427         this.reloadItems();
34428
34429         this.currentSize = this.el.getBox(true);
34430
34431         /// was window resize... - let's see if this works..
34432         Roo.EventManager.onWindowResize(this.resize, this); 
34433
34434         if(!this.isAutoInitial){
34435             this.layout();
34436             return;
34437         }
34438         
34439         this.layout.defer(500,this);
34440     },
34441     
34442     reloadItems: function()
34443     {
34444         this.bricks = this.el.select('.masonry-brick', true);
34445         
34446         this.bricks.each(function(b) {
34447             //Roo.log(b.getSize());
34448             if (!b.attr('originalwidth')) {
34449                 b.attr('originalwidth',  b.getSize().width);
34450             }
34451             
34452         });
34453         
34454         Roo.log(this.bricks.elements.length);
34455     },
34456     
34457     resize : function()
34458     {
34459         Roo.log('resize');
34460         var cs = this.el.getBox(true);
34461         
34462         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34463             Roo.log("no change in with or X");
34464             return;
34465         }
34466         this.currentSize = cs;
34467         this.layout();
34468     },
34469     
34470     layout : function()
34471     {
34472          Roo.log('layout');
34473         this._resetLayout();
34474         //this._manageStamps();
34475       
34476         // don't animate first layout
34477         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34478         this.layoutItems( isInstant );
34479       
34480         // flag for initalized
34481         this._isLayoutInited = true;
34482     },
34483     
34484     layoutItems : function( isInstant )
34485     {
34486         //var items = this._getItemsForLayout( this.items );
34487         // original code supports filtering layout items.. we just ignore it..
34488         
34489         this._layoutItems( this.bricks , isInstant );
34490       
34491         this._postLayout();
34492     },
34493     _layoutItems : function ( items , isInstant)
34494     {
34495        //this.fireEvent( 'layout', this, items );
34496     
34497
34498         if ( !items || !items.elements.length ) {
34499           // no items, emit event with empty array
34500             return;
34501         }
34502
34503         var queue = [];
34504         items.each(function(item) {
34505             Roo.log("layout item");
34506             Roo.log(item);
34507             // get x/y object from method
34508             var position = this._getItemLayoutPosition( item );
34509             // enqueue
34510             position.item = item;
34511             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34512             queue.push( position );
34513         }, this);
34514       
34515         this._processLayoutQueue( queue );
34516     },
34517     /** Sets position of item in DOM
34518     * @param {Element} item
34519     * @param {Number} x - horizontal position
34520     * @param {Number} y - vertical position
34521     * @param {Boolean} isInstant - disables transitions
34522     */
34523     _processLayoutQueue : function( queue )
34524     {
34525         for ( var i=0, len = queue.length; i < len; i++ ) {
34526             var obj = queue[i];
34527             obj.item.position('absolute');
34528             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34529         }
34530     },
34531       
34532     
34533     /**
34534     * Any logic you want to do after each layout,
34535     * i.e. size the container
34536     */
34537     _postLayout : function()
34538     {
34539         this.resizeContainer();
34540     },
34541     
34542     resizeContainer : function()
34543     {
34544         if ( !this.isResizingContainer ) {
34545             return;
34546         }
34547         var size = this._getContainerSize();
34548         if ( size ) {
34549             this.el.setSize(size.width,size.height);
34550             this.boxesEl.setSize(size.width,size.height);
34551         }
34552     },
34553     
34554     
34555     
34556     _resetLayout : function()
34557     {
34558         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34559         this.colWidth = this.el.getWidth();
34560         //this.gutter = this.el.getWidth(); 
34561         
34562         this.measureColumns();
34563
34564         // reset column Y
34565         var i = this.cols;
34566         this.colYs = [];
34567         while (i--) {
34568             this.colYs.push( 0 );
34569         }
34570     
34571         this.maxY = 0;
34572     },
34573
34574     measureColumns : function()
34575     {
34576         this.getContainerWidth();
34577       // if columnWidth is 0, default to outerWidth of first item
34578         if ( !this.columnWidth ) {
34579             var firstItem = this.bricks.first();
34580             Roo.log(firstItem);
34581             this.columnWidth  = this.containerWidth;
34582             if (firstItem && firstItem.attr('originalwidth') ) {
34583                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34584             }
34585             // columnWidth fall back to item of first element
34586             Roo.log("set column width?");
34587                         this.initialColumnWidth = this.columnWidth  ;
34588
34589             // if first elem has no width, default to size of container
34590             
34591         }
34592         
34593         
34594         if (this.initialColumnWidth) {
34595             this.columnWidth = this.initialColumnWidth;
34596         }
34597         
34598         
34599             
34600         // column width is fixed at the top - however if container width get's smaller we should
34601         // reduce it...
34602         
34603         // this bit calcs how man columns..
34604             
34605         var columnWidth = this.columnWidth += this.gutter;
34606       
34607         // calculate columns
34608         var containerWidth = this.containerWidth + this.gutter;
34609         
34610         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34611         // fix rounding errors, typically with gutters
34612         var excess = columnWidth - containerWidth % columnWidth;
34613         
34614         
34615         // if overshoot is less than a pixel, round up, otherwise floor it
34616         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34617         cols = Math[ mathMethod ]( cols );
34618         this.cols = Math.max( cols, 1 );
34619         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34620         
34621          // padding positioning..
34622         var totalColWidth = this.cols * this.columnWidth;
34623         var padavail = this.containerWidth - totalColWidth;
34624         // so for 2 columns - we need 3 'pads'
34625         
34626         var padNeeded = (1+this.cols) * this.padWidth;
34627         
34628         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34629         
34630         this.columnWidth += padExtra
34631         //this.padWidth = Math.floor(padavail /  ( this.cols));
34632         
34633         // adjust colum width so that padding is fixed??
34634         
34635         // we have 3 columns ... total = width * 3
34636         // we have X left over... that should be used by 
34637         
34638         //if (this.expandC) {
34639             
34640         //}
34641         
34642         
34643         
34644     },
34645     
34646     getContainerWidth : function()
34647     {
34648        /* // container is parent if fit width
34649         var container = this.isFitWidth ? this.element.parentNode : this.element;
34650         // check that this.size and size are there
34651         // IE8 triggers resize on body size change, so they might not be
34652         
34653         var size = getSize( container );  //FIXME
34654         this.containerWidth = size && size.innerWidth; //FIXME
34655         */
34656          
34657         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34658         
34659     },
34660     
34661     _getItemLayoutPosition : function( item )  // what is item?
34662     {
34663         // we resize the item to our columnWidth..
34664       
34665         item.setWidth(this.columnWidth);
34666         item.autoBoxAdjust  = false;
34667         
34668         var sz = item.getSize();
34669  
34670         // how many columns does this brick span
34671         var remainder = this.containerWidth % this.columnWidth;
34672         
34673         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34674         // round if off by 1 pixel, otherwise use ceil
34675         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34676         colSpan = Math.min( colSpan, this.cols );
34677         
34678         // normally this should be '1' as we dont' currently allow multi width columns..
34679         
34680         var colGroup = this._getColGroup( colSpan );
34681         // get the minimum Y value from the columns
34682         var minimumY = Math.min.apply( Math, colGroup );
34683         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34684         
34685         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34686          
34687         // position the brick
34688         var position = {
34689             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34690             y: this.currentSize.y + minimumY + this.padHeight
34691         };
34692         
34693         Roo.log(position);
34694         // apply setHeight to necessary columns
34695         var setHeight = minimumY + sz.height + this.padHeight;
34696         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34697         
34698         var setSpan = this.cols + 1 - colGroup.length;
34699         for ( var i = 0; i < setSpan; i++ ) {
34700           this.colYs[ shortColIndex + i ] = setHeight ;
34701         }
34702       
34703         return position;
34704     },
34705     
34706     /**
34707      * @param {Number} colSpan - number of columns the element spans
34708      * @returns {Array} colGroup
34709      */
34710     _getColGroup : function( colSpan )
34711     {
34712         if ( colSpan < 2 ) {
34713           // if brick spans only one column, use all the column Ys
34714           return this.colYs;
34715         }
34716       
34717         var colGroup = [];
34718         // how many different places could this brick fit horizontally
34719         var groupCount = this.cols + 1 - colSpan;
34720         // for each group potential horizontal position
34721         for ( var i = 0; i < groupCount; i++ ) {
34722           // make an array of colY values for that one group
34723           var groupColYs = this.colYs.slice( i, i + colSpan );
34724           // and get the max value of the array
34725           colGroup[i] = Math.max.apply( Math, groupColYs );
34726         }
34727         return colGroup;
34728     },
34729     /*
34730     _manageStamp : function( stamp )
34731     {
34732         var stampSize =  stamp.getSize();
34733         var offset = stamp.getBox();
34734         // get the columns that this stamp affects
34735         var firstX = this.isOriginLeft ? offset.x : offset.right;
34736         var lastX = firstX + stampSize.width;
34737         var firstCol = Math.floor( firstX / this.columnWidth );
34738         firstCol = Math.max( 0, firstCol );
34739         
34740         var lastCol = Math.floor( lastX / this.columnWidth );
34741         // lastCol should not go over if multiple of columnWidth #425
34742         lastCol -= lastX % this.columnWidth ? 0 : 1;
34743         lastCol = Math.min( this.cols - 1, lastCol );
34744         
34745         // set colYs to bottom of the stamp
34746         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34747             stampSize.height;
34748             
34749         for ( var i = firstCol; i <= lastCol; i++ ) {
34750           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34751         }
34752     },
34753     */
34754     
34755     _getContainerSize : function()
34756     {
34757         this.maxY = Math.max.apply( Math, this.colYs );
34758         var size = {
34759             height: this.maxY
34760         };
34761       
34762         if ( this.isFitWidth ) {
34763             size.width = this._getContainerFitWidth();
34764         }
34765       
34766         return size;
34767     },
34768     
34769     _getContainerFitWidth : function()
34770     {
34771         var unusedCols = 0;
34772         // count unused columns
34773         var i = this.cols;
34774         while ( --i ) {
34775           if ( this.colYs[i] !== 0 ) {
34776             break;
34777           }
34778           unusedCols++;
34779         }
34780         // fit container to columns that have been used
34781         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34782     },
34783     
34784     needsResizeLayout : function()
34785     {
34786         var previousWidth = this.containerWidth;
34787         this.getContainerWidth();
34788         return previousWidth !== this.containerWidth;
34789     }
34790  
34791 });
34792
34793  
34794
34795  /*
34796  * - LGPL
34797  *
34798  * element
34799  * 
34800  */
34801
34802 /**
34803  * @class Roo.bootstrap.MasonryBrick
34804  * @extends Roo.bootstrap.Component
34805  * Bootstrap MasonryBrick class
34806  * 
34807  * @constructor
34808  * Create a new MasonryBrick
34809  * @param {Object} config The config object
34810  */
34811
34812 Roo.bootstrap.MasonryBrick = function(config){
34813     
34814     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34815     
34816     Roo.bootstrap.MasonryBrick.register(this);
34817     
34818     this.addEvents({
34819         // raw events
34820         /**
34821          * @event click
34822          * When a MasonryBrick is clcik
34823          * @param {Roo.bootstrap.MasonryBrick} this
34824          * @param {Roo.EventObject} e
34825          */
34826         "click" : true
34827     });
34828 };
34829
34830 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34831     
34832     /**
34833      * @cfg {String} title
34834      */   
34835     title : '',
34836     /**
34837      * @cfg {String} html
34838      */   
34839     html : '',
34840     /**
34841      * @cfg {String} bgimage
34842      */   
34843     bgimage : '',
34844     /**
34845      * @cfg {String} videourl
34846      */   
34847     videourl : '',
34848     /**
34849      * @cfg {String} cls
34850      */   
34851     cls : '',
34852     /**
34853      * @cfg {String} href
34854      */   
34855     href : '',
34856     /**
34857      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34858      */   
34859     size : 'xs',
34860     
34861     /**
34862      * @cfg {String} placetitle (center|bottom)
34863      */   
34864     placetitle : '',
34865     
34866     /**
34867      * @cfg {Boolean} isFitContainer defalut true
34868      */   
34869     isFitContainer : true, 
34870     
34871     /**
34872      * @cfg {Boolean} preventDefault defalut false
34873      */   
34874     preventDefault : false, 
34875     
34876     /**
34877      * @cfg {Boolean} inverse defalut false
34878      */   
34879     maskInverse : false, 
34880     
34881     getAutoCreate : function()
34882     {
34883         if(!this.isFitContainer){
34884             return this.getSplitAutoCreate();
34885         }
34886         
34887         var cls = 'masonry-brick masonry-brick-full';
34888         
34889         if(this.href.length){
34890             cls += ' masonry-brick-link';
34891         }
34892         
34893         if(this.bgimage.length){
34894             cls += ' masonry-brick-image';
34895         }
34896         
34897         if(this.maskInverse){
34898             cls += ' mask-inverse';
34899         }
34900         
34901         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34902             cls += ' enable-mask';
34903         }
34904         
34905         if(this.size){
34906             cls += ' masonry-' + this.size + '-brick';
34907         }
34908         
34909         if(this.placetitle.length){
34910             
34911             switch (this.placetitle) {
34912                 case 'center' :
34913                     cls += ' masonry-center-title';
34914                     break;
34915                 case 'bottom' :
34916                     cls += ' masonry-bottom-title';
34917                     break;
34918                 default:
34919                     break;
34920             }
34921             
34922         } else {
34923             if(!this.html.length && !this.bgimage.length){
34924                 cls += ' masonry-center-title';
34925             }
34926
34927             if(!this.html.length && this.bgimage.length){
34928                 cls += ' masonry-bottom-title';
34929             }
34930         }
34931         
34932         if(this.cls){
34933             cls += ' ' + this.cls;
34934         }
34935         
34936         var cfg = {
34937             tag: (this.href.length) ? 'a' : 'div',
34938             cls: cls,
34939             cn: [
34940                 {
34941                     tag: 'div',
34942                     cls: 'masonry-brick-mask'
34943                 },
34944                 {
34945                     tag: 'div',
34946                     cls: 'masonry-brick-paragraph',
34947                     cn: []
34948                 }
34949             ]
34950         };
34951         
34952         if(this.href.length){
34953             cfg.href = this.href;
34954         }
34955         
34956         var cn = cfg.cn[1].cn;
34957         
34958         if(this.title.length){
34959             cn.push({
34960                 tag: 'h4',
34961                 cls: 'masonry-brick-title',
34962                 html: this.title
34963             });
34964         }
34965         
34966         if(this.html.length){
34967             cn.push({
34968                 tag: 'p',
34969                 cls: 'masonry-brick-text',
34970                 html: this.html
34971             });
34972         }
34973         
34974         if (!this.title.length && !this.html.length) {
34975             cfg.cn[1].cls += ' hide';
34976         }
34977         
34978         if(this.bgimage.length){
34979             cfg.cn.push({
34980                 tag: 'img',
34981                 cls: 'masonry-brick-image-view',
34982                 src: this.bgimage
34983             });
34984         }
34985         
34986         if(this.videourl.length){
34987             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34988             // youtube support only?
34989             cfg.cn.push({
34990                 tag: 'iframe',
34991                 cls: 'masonry-brick-image-view',
34992                 src: vurl,
34993                 frameborder : 0,
34994                 allowfullscreen : true
34995             });
34996         }
34997         
34998         return cfg;
34999         
35000     },
35001     
35002     getSplitAutoCreate : function()
35003     {
35004         var cls = 'masonry-brick masonry-brick-split';
35005         
35006         if(this.href.length){
35007             cls += ' masonry-brick-link';
35008         }
35009         
35010         if(this.bgimage.length){
35011             cls += ' masonry-brick-image';
35012         }
35013         
35014         if(this.size){
35015             cls += ' masonry-' + this.size + '-brick';
35016         }
35017         
35018         switch (this.placetitle) {
35019             case 'center' :
35020                 cls += ' masonry-center-title';
35021                 break;
35022             case 'bottom' :
35023                 cls += ' masonry-bottom-title';
35024                 break;
35025             default:
35026                 if(!this.bgimage.length){
35027                     cls += ' masonry-center-title';
35028                 }
35029
35030                 if(this.bgimage.length){
35031                     cls += ' masonry-bottom-title';
35032                 }
35033                 break;
35034         }
35035         
35036         if(this.cls){
35037             cls += ' ' + this.cls;
35038         }
35039         
35040         var cfg = {
35041             tag: (this.href.length) ? 'a' : 'div',
35042             cls: cls,
35043             cn: [
35044                 {
35045                     tag: 'div',
35046                     cls: 'masonry-brick-split-head',
35047                     cn: [
35048                         {
35049                             tag: 'div',
35050                             cls: 'masonry-brick-paragraph',
35051                             cn: []
35052                         }
35053                     ]
35054                 },
35055                 {
35056                     tag: 'div',
35057                     cls: 'masonry-brick-split-body',
35058                     cn: []
35059                 }
35060             ]
35061         };
35062         
35063         if(this.href.length){
35064             cfg.href = this.href;
35065         }
35066         
35067         if(this.title.length){
35068             cfg.cn[0].cn[0].cn.push({
35069                 tag: 'h4',
35070                 cls: 'masonry-brick-title',
35071                 html: this.title
35072             });
35073         }
35074         
35075         if(this.html.length){
35076             cfg.cn[1].cn.push({
35077                 tag: 'p',
35078                 cls: 'masonry-brick-text',
35079                 html: this.html
35080             });
35081         }
35082
35083         if(this.bgimage.length){
35084             cfg.cn[0].cn.push({
35085                 tag: 'img',
35086                 cls: 'masonry-brick-image-view',
35087                 src: this.bgimage
35088             });
35089         }
35090         
35091         if(this.videourl.length){
35092             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35093             // youtube support only?
35094             cfg.cn[0].cn.cn.push({
35095                 tag: 'iframe',
35096                 cls: 'masonry-brick-image-view',
35097                 src: vurl,
35098                 frameborder : 0,
35099                 allowfullscreen : true
35100             });
35101         }
35102         
35103         return cfg;
35104     },
35105     
35106     initEvents: function() 
35107     {
35108         switch (this.size) {
35109             case 'xs' :
35110                 this.x = 1;
35111                 this.y = 1;
35112                 break;
35113             case 'sm' :
35114                 this.x = 2;
35115                 this.y = 2;
35116                 break;
35117             case 'md' :
35118             case 'md-left' :
35119             case 'md-right' :
35120                 this.x = 3;
35121                 this.y = 3;
35122                 break;
35123             case 'tall' :
35124                 this.x = 2;
35125                 this.y = 3;
35126                 break;
35127             case 'wide' :
35128                 this.x = 3;
35129                 this.y = 2;
35130                 break;
35131             case 'wide-thin' :
35132                 this.x = 3;
35133                 this.y = 1;
35134                 break;
35135                         
35136             default :
35137                 break;
35138         }
35139         
35140         if(Roo.isTouch){
35141             this.el.on('touchstart', this.onTouchStart, this);
35142             this.el.on('touchmove', this.onTouchMove, this);
35143             this.el.on('touchend', this.onTouchEnd, this);
35144             this.el.on('contextmenu', this.onContextMenu, this);
35145         } else {
35146             this.el.on('mouseenter'  ,this.enter, this);
35147             this.el.on('mouseleave', this.leave, this);
35148             this.el.on('click', this.onClick, this);
35149         }
35150         
35151         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35152             this.parent().bricks.push(this);   
35153         }
35154         
35155     },
35156     
35157     onClick: function(e, el)
35158     {
35159         var time = this.endTimer - this.startTimer;
35160         // Roo.log(e.preventDefault());
35161         if(Roo.isTouch){
35162             if(time > 1000){
35163                 e.preventDefault();
35164                 return;
35165             }
35166         }
35167         
35168         if(!this.preventDefault){
35169             return;
35170         }
35171         
35172         e.preventDefault();
35173         
35174         if (this.activeClass != '') {
35175             this.selectBrick();
35176         }
35177         
35178         this.fireEvent('click', this, e);
35179     },
35180     
35181     enter: function(e, el)
35182     {
35183         e.preventDefault();
35184         
35185         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35186             return;
35187         }
35188         
35189         if(this.bgimage.length && this.html.length){
35190             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35191         }
35192     },
35193     
35194     leave: function(e, el)
35195     {
35196         e.preventDefault();
35197         
35198         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35199             return;
35200         }
35201         
35202         if(this.bgimage.length && this.html.length){
35203             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35204         }
35205     },
35206     
35207     onTouchStart: function(e, el)
35208     {
35209 //        e.preventDefault();
35210         
35211         this.touchmoved = false;
35212         
35213         if(!this.isFitContainer){
35214             return;
35215         }
35216         
35217         if(!this.bgimage.length || !this.html.length){
35218             return;
35219         }
35220         
35221         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35222         
35223         this.timer = new Date().getTime();
35224         
35225     },
35226     
35227     onTouchMove: function(e, el)
35228     {
35229         this.touchmoved = true;
35230     },
35231     
35232     onContextMenu : function(e,el)
35233     {
35234         e.preventDefault();
35235         e.stopPropagation();
35236         return false;
35237     },
35238     
35239     onTouchEnd: function(e, el)
35240     {
35241 //        e.preventDefault();
35242         
35243         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35244         
35245             this.leave(e,el);
35246             
35247             return;
35248         }
35249         
35250         if(!this.bgimage.length || !this.html.length){
35251             
35252             if(this.href.length){
35253                 window.location.href = this.href;
35254             }
35255             
35256             return;
35257         }
35258         
35259         if(!this.isFitContainer){
35260             return;
35261         }
35262         
35263         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35264         
35265         window.location.href = this.href;
35266     },
35267     
35268     //selection on single brick only
35269     selectBrick : function() {
35270         
35271         if (!this.parentId) {
35272             return;
35273         }
35274         
35275         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35276         var index = m.selectedBrick.indexOf(this.id);
35277         
35278         if ( index > -1) {
35279             m.selectedBrick.splice(index,1);
35280             this.el.removeClass(this.activeClass);
35281             return;
35282         }
35283         
35284         for(var i = 0; i < m.selectedBrick.length; i++) {
35285             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35286             b.el.removeClass(b.activeClass);
35287         }
35288         
35289         m.selectedBrick = [];
35290         
35291         m.selectedBrick.push(this.id);
35292         this.el.addClass(this.activeClass);
35293         return;
35294     },
35295     
35296     isSelected : function(){
35297         return this.el.hasClass(this.activeClass);
35298         
35299     }
35300 });
35301
35302 Roo.apply(Roo.bootstrap.MasonryBrick, {
35303     
35304     //groups: {},
35305     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35306      /**
35307     * register a Masonry Brick
35308     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35309     */
35310     
35311     register : function(brick)
35312     {
35313         //this.groups[brick.id] = brick;
35314         this.groups.add(brick.id, brick);
35315     },
35316     /**
35317     * fetch a  masonry brick based on the masonry brick ID
35318     * @param {string} the masonry brick to add
35319     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35320     */
35321     
35322     get: function(brick_id) 
35323     {
35324         // if (typeof(this.groups[brick_id]) == 'undefined') {
35325         //     return false;
35326         // }
35327         // return this.groups[brick_id] ;
35328         
35329         if(this.groups.key(brick_id)) {
35330             return this.groups.key(brick_id);
35331         }
35332         
35333         return false;
35334     }
35335     
35336     
35337     
35338 });
35339
35340  /*
35341  * - LGPL
35342  *
35343  * element
35344  * 
35345  */
35346
35347 /**
35348  * @class Roo.bootstrap.Brick
35349  * @extends Roo.bootstrap.Component
35350  * Bootstrap Brick class
35351  * 
35352  * @constructor
35353  * Create a new Brick
35354  * @param {Object} config The config object
35355  */
35356
35357 Roo.bootstrap.Brick = function(config){
35358     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35359     
35360     this.addEvents({
35361         // raw events
35362         /**
35363          * @event click
35364          * When a Brick is click
35365          * @param {Roo.bootstrap.Brick} this
35366          * @param {Roo.EventObject} e
35367          */
35368         "click" : true
35369     });
35370 };
35371
35372 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35373     
35374     /**
35375      * @cfg {String} title
35376      */   
35377     title : '',
35378     /**
35379      * @cfg {String} html
35380      */   
35381     html : '',
35382     /**
35383      * @cfg {String} bgimage
35384      */   
35385     bgimage : '',
35386     /**
35387      * @cfg {String} cls
35388      */   
35389     cls : '',
35390     /**
35391      * @cfg {String} href
35392      */   
35393     href : '',
35394     /**
35395      * @cfg {String} video
35396      */   
35397     video : '',
35398     /**
35399      * @cfg {Boolean} square
35400      */   
35401     square : true,
35402     
35403     getAutoCreate : function()
35404     {
35405         var cls = 'roo-brick';
35406         
35407         if(this.href.length){
35408             cls += ' roo-brick-link';
35409         }
35410         
35411         if(this.bgimage.length){
35412             cls += ' roo-brick-image';
35413         }
35414         
35415         if(!this.html.length && !this.bgimage.length){
35416             cls += ' roo-brick-center-title';
35417         }
35418         
35419         if(!this.html.length && this.bgimage.length){
35420             cls += ' roo-brick-bottom-title';
35421         }
35422         
35423         if(this.cls){
35424             cls += ' ' + this.cls;
35425         }
35426         
35427         var cfg = {
35428             tag: (this.href.length) ? 'a' : 'div',
35429             cls: cls,
35430             cn: [
35431                 {
35432                     tag: 'div',
35433                     cls: 'roo-brick-paragraph',
35434                     cn: []
35435                 }
35436             ]
35437         };
35438         
35439         if(this.href.length){
35440             cfg.href = this.href;
35441         }
35442         
35443         var cn = cfg.cn[0].cn;
35444         
35445         if(this.title.length){
35446             cn.push({
35447                 tag: 'h4',
35448                 cls: 'roo-brick-title',
35449                 html: this.title
35450             });
35451         }
35452         
35453         if(this.html.length){
35454             cn.push({
35455                 tag: 'p',
35456                 cls: 'roo-brick-text',
35457                 html: this.html
35458             });
35459         } else {
35460             cn.cls += ' hide';
35461         }
35462         
35463         if(this.bgimage.length){
35464             cfg.cn.push({
35465                 tag: 'img',
35466                 cls: 'roo-brick-image-view',
35467                 src: this.bgimage
35468             });
35469         }
35470         
35471         return cfg;
35472     },
35473     
35474     initEvents: function() 
35475     {
35476         if(this.title.length || this.html.length){
35477             this.el.on('mouseenter'  ,this.enter, this);
35478             this.el.on('mouseleave', this.leave, this);
35479         }
35480         
35481         Roo.EventManager.onWindowResize(this.resize, this); 
35482         
35483         if(this.bgimage.length){
35484             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35485             this.imageEl.on('load', this.onImageLoad, this);
35486             return;
35487         }
35488         
35489         this.resize();
35490     },
35491     
35492     onImageLoad : function()
35493     {
35494         this.resize();
35495     },
35496     
35497     resize : function()
35498     {
35499         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35500         
35501         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35502         
35503         if(this.bgimage.length){
35504             var image = this.el.select('.roo-brick-image-view', true).first();
35505             
35506             image.setWidth(paragraph.getWidth());
35507             
35508             if(this.square){
35509                 image.setHeight(paragraph.getWidth());
35510             }
35511             
35512             this.el.setHeight(image.getHeight());
35513             paragraph.setHeight(image.getHeight());
35514             
35515         }
35516         
35517     },
35518     
35519     enter: function(e, el)
35520     {
35521         e.preventDefault();
35522         
35523         if(this.bgimage.length){
35524             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35525             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35526         }
35527     },
35528     
35529     leave: function(e, el)
35530     {
35531         e.preventDefault();
35532         
35533         if(this.bgimage.length){
35534             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35535             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35536         }
35537     }
35538     
35539 });
35540
35541  
35542
35543  /*
35544  * - LGPL
35545  *
35546  * Number field 
35547  */
35548
35549 /**
35550  * @class Roo.bootstrap.NumberField
35551  * @extends Roo.bootstrap.Input
35552  * Bootstrap NumberField class
35553  * 
35554  * 
35555  * 
35556  * 
35557  * @constructor
35558  * Create a new NumberField
35559  * @param {Object} config The config object
35560  */
35561
35562 Roo.bootstrap.NumberField = function(config){
35563     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35564 };
35565
35566 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35567     
35568     /**
35569      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35570      */
35571     allowDecimals : true,
35572     /**
35573      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35574      */
35575     decimalSeparator : ".",
35576     /**
35577      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35578      */
35579     decimalPrecision : 2,
35580     /**
35581      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35582      */
35583     allowNegative : true,
35584     
35585     /**
35586      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35587      */
35588     allowZero: true,
35589     /**
35590      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35591      */
35592     minValue : Number.NEGATIVE_INFINITY,
35593     /**
35594      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35595      */
35596     maxValue : Number.MAX_VALUE,
35597     /**
35598      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35599      */
35600     minText : "The minimum value for this field is {0}",
35601     /**
35602      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35603      */
35604     maxText : "The maximum value for this field is {0}",
35605     /**
35606      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35607      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35608      */
35609     nanText : "{0} is not a valid number",
35610     /**
35611      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35612      */
35613     thousandsDelimiter : false,
35614     /**
35615      * @cfg {String} valueAlign alignment of value
35616      */
35617     valueAlign : "left",
35618
35619     getAutoCreate : function()
35620     {
35621         var hiddenInput = {
35622             tag: 'input',
35623             type: 'hidden',
35624             id: Roo.id(),
35625             cls: 'hidden-number-input'
35626         };
35627         
35628         if (this.name) {
35629             hiddenInput.name = this.name;
35630         }
35631         
35632         this.name = '';
35633         
35634         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35635         
35636         this.name = hiddenInput.name;
35637         
35638         if(cfg.cn.length > 0) {
35639             cfg.cn.push(hiddenInput);
35640         }
35641         
35642         return cfg;
35643     },
35644
35645     // private
35646     initEvents : function()
35647     {   
35648         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35649         
35650         var allowed = "0123456789";
35651         
35652         if(this.allowDecimals){
35653             allowed += this.decimalSeparator;
35654         }
35655         
35656         if(this.allowNegative){
35657             allowed += "-";
35658         }
35659         
35660         if(this.thousandsDelimiter) {
35661             allowed += ",";
35662         }
35663         
35664         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35665         
35666         var keyPress = function(e){
35667             
35668             var k = e.getKey();
35669             
35670             var c = e.getCharCode();
35671             
35672             if(
35673                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35674                     allowed.indexOf(String.fromCharCode(c)) === -1
35675             ){
35676                 e.stopEvent();
35677                 return;
35678             }
35679             
35680             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35681                 return;
35682             }
35683             
35684             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35685                 e.stopEvent();
35686             }
35687         };
35688         
35689         this.el.on("keypress", keyPress, this);
35690     },
35691     
35692     validateValue : function(value)
35693     {
35694         
35695         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35696             return false;
35697         }
35698         
35699         var num = this.parseValue(value);
35700         
35701         if(isNaN(num)){
35702             this.markInvalid(String.format(this.nanText, value));
35703             return false;
35704         }
35705         
35706         if(num < this.minValue){
35707             this.markInvalid(String.format(this.minText, this.minValue));
35708             return false;
35709         }
35710         
35711         if(num > this.maxValue){
35712             this.markInvalid(String.format(this.maxText, this.maxValue));
35713             return false;
35714         }
35715         
35716         return true;
35717     },
35718
35719     getValue : function()
35720     {
35721         var v = this.hiddenEl().getValue();
35722         
35723         return this.fixPrecision(this.parseValue(v));
35724     },
35725
35726     parseValue : function(value)
35727     {
35728         if(this.thousandsDelimiter) {
35729             value += "";
35730             r = new RegExp(",", "g");
35731             value = value.replace(r, "");
35732         }
35733         
35734         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35735         return isNaN(value) ? '' : value;
35736     },
35737
35738     fixPrecision : function(value)
35739     {
35740         if(this.thousandsDelimiter) {
35741             value += "";
35742             r = new RegExp(",", "g");
35743             value = value.replace(r, "");
35744         }
35745         
35746         var nan = isNaN(value);
35747         
35748         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35749             return nan ? '' : value;
35750         }
35751         return parseFloat(value).toFixed(this.decimalPrecision);
35752     },
35753
35754     setValue : function(v)
35755     {
35756         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35757         
35758         this.value = v;
35759         
35760         if(this.rendered){
35761             
35762             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35763             
35764             this.inputEl().dom.value = (v == '') ? '' :
35765                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35766             
35767             if(!this.allowZero && v === '0') {
35768                 this.hiddenEl().dom.value = '';
35769                 this.inputEl().dom.value = '';
35770             }
35771             
35772             this.validate();
35773         }
35774     },
35775
35776     decimalPrecisionFcn : function(v)
35777     {
35778         return Math.floor(v);
35779     },
35780
35781     beforeBlur : function()
35782     {
35783         var v = this.parseValue(this.getRawValue());
35784         
35785         if(v || v === 0 || v === ''){
35786             this.setValue(v);
35787         }
35788     },
35789     
35790     hiddenEl : function()
35791     {
35792         return this.el.select('input.hidden-number-input',true).first();
35793     }
35794     
35795 });
35796
35797  
35798
35799 /*
35800 * Licence: LGPL
35801 */
35802
35803 /**
35804  * @class Roo.bootstrap.DocumentSlider
35805  * @extends Roo.bootstrap.Component
35806  * Bootstrap DocumentSlider class
35807  * 
35808  * @constructor
35809  * Create a new DocumentViewer
35810  * @param {Object} config The config object
35811  */
35812
35813 Roo.bootstrap.DocumentSlider = function(config){
35814     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35815     
35816     this.files = [];
35817     
35818     this.addEvents({
35819         /**
35820          * @event initial
35821          * Fire after initEvent
35822          * @param {Roo.bootstrap.DocumentSlider} this
35823          */
35824         "initial" : true,
35825         /**
35826          * @event update
35827          * Fire after update
35828          * @param {Roo.bootstrap.DocumentSlider} this
35829          */
35830         "update" : true,
35831         /**
35832          * @event click
35833          * Fire after click
35834          * @param {Roo.bootstrap.DocumentSlider} this
35835          */
35836         "click" : true
35837     });
35838 };
35839
35840 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35841     
35842     files : false,
35843     
35844     indicator : 0,
35845     
35846     getAutoCreate : function()
35847     {
35848         var cfg = {
35849             tag : 'div',
35850             cls : 'roo-document-slider',
35851             cn : [
35852                 {
35853                     tag : 'div',
35854                     cls : 'roo-document-slider-header',
35855                     cn : [
35856                         {
35857                             tag : 'div',
35858                             cls : 'roo-document-slider-header-title'
35859                         }
35860                     ]
35861                 },
35862                 {
35863                     tag : 'div',
35864                     cls : 'roo-document-slider-body',
35865                     cn : [
35866                         {
35867                             tag : 'div',
35868                             cls : 'roo-document-slider-prev',
35869                             cn : [
35870                                 {
35871                                     tag : 'i',
35872                                     cls : 'fa fa-chevron-left'
35873                                 }
35874                             ]
35875                         },
35876                         {
35877                             tag : 'div',
35878                             cls : 'roo-document-slider-thumb',
35879                             cn : [
35880                                 {
35881                                     tag : 'img',
35882                                     cls : 'roo-document-slider-image'
35883                                 }
35884                             ]
35885                         },
35886                         {
35887                             tag : 'div',
35888                             cls : 'roo-document-slider-next',
35889                             cn : [
35890                                 {
35891                                     tag : 'i',
35892                                     cls : 'fa fa-chevron-right'
35893                                 }
35894                             ]
35895                         }
35896                     ]
35897                 }
35898             ]
35899         };
35900         
35901         return cfg;
35902     },
35903     
35904     initEvents : function()
35905     {
35906         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35907         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35908         
35909         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35910         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35911         
35912         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35913         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35914         
35915         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35916         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35917         
35918         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35919         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35920         
35921         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35922         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35923         
35924         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35925         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35926         
35927         this.thumbEl.on('click', this.onClick, this);
35928         
35929         this.prevIndicator.on('click', this.prev, this);
35930         
35931         this.nextIndicator.on('click', this.next, this);
35932         
35933     },
35934     
35935     initial : function()
35936     {
35937         if(this.files.length){
35938             this.indicator = 1;
35939             this.update()
35940         }
35941         
35942         this.fireEvent('initial', this);
35943     },
35944     
35945     update : function()
35946     {
35947         this.imageEl.attr('src', this.files[this.indicator - 1]);
35948         
35949         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35950         
35951         this.prevIndicator.show();
35952         
35953         if(this.indicator == 1){
35954             this.prevIndicator.hide();
35955         }
35956         
35957         this.nextIndicator.show();
35958         
35959         if(this.indicator == this.files.length){
35960             this.nextIndicator.hide();
35961         }
35962         
35963         this.thumbEl.scrollTo('top');
35964         
35965         this.fireEvent('update', this);
35966     },
35967     
35968     onClick : function(e)
35969     {
35970         e.preventDefault();
35971         
35972         this.fireEvent('click', this);
35973     },
35974     
35975     prev : function(e)
35976     {
35977         e.preventDefault();
35978         
35979         this.indicator = Math.max(1, this.indicator - 1);
35980         
35981         this.update();
35982     },
35983     
35984     next : function(e)
35985     {
35986         e.preventDefault();
35987         
35988         this.indicator = Math.min(this.files.length, this.indicator + 1);
35989         
35990         this.update();
35991     }
35992 });
35993 /*
35994  * - LGPL
35995  *
35996  * RadioSet
35997  *
35998  *
35999  */
36000
36001 /**
36002  * @class Roo.bootstrap.RadioSet
36003  * @extends Roo.bootstrap.Input
36004  * Bootstrap RadioSet class
36005  * @cfg {String} indicatorpos (left|right) default left
36006  * @cfg {Boolean} inline (true|false) inline the element (default true)
36007  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36008  * @constructor
36009  * Create a new RadioSet
36010  * @param {Object} config The config object
36011  */
36012
36013 Roo.bootstrap.RadioSet = function(config){
36014     
36015     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36016     
36017     this.radioes = [];
36018     
36019     Roo.bootstrap.RadioSet.register(this);
36020     
36021     this.addEvents({
36022         /**
36023         * @event check
36024         * Fires when the element is checked or unchecked.
36025         * @param {Roo.bootstrap.RadioSet} this This radio
36026         * @param {Roo.bootstrap.Radio} item The checked item
36027         */
36028        check : true,
36029        /**
36030         * @event click
36031         * Fires when the element is click.
36032         * @param {Roo.bootstrap.RadioSet} this This radio set
36033         * @param {Roo.bootstrap.Radio} item The checked item
36034         * @param {Roo.EventObject} e The event object
36035         */
36036        click : true
36037     });
36038     
36039 };
36040
36041 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36042
36043     radioes : false,
36044     
36045     inline : true,
36046     
36047     weight : '',
36048     
36049     indicatorpos : 'left',
36050     
36051     getAutoCreate : function()
36052     {
36053         var label = {
36054             tag : 'label',
36055             cls : 'roo-radio-set-label',
36056             cn : [
36057                 {
36058                     tag : 'span',
36059                     html : this.fieldLabel
36060                 }
36061             ]
36062         };
36063         if (Roo.bootstrap.version == 3) {
36064             
36065             
36066             if(this.indicatorpos == 'left'){
36067                 label.cn.unshift({
36068                     tag : 'i',
36069                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36070                     tooltip : 'This field is required'
36071                 });
36072             } else {
36073                 label.cn.push({
36074                     tag : 'i',
36075                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36076                     tooltip : 'This field is required'
36077                 });
36078             }
36079         }
36080         var items = {
36081             tag : 'div',
36082             cls : 'roo-radio-set-items'
36083         };
36084         
36085         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36086         
36087         if (align === 'left' && this.fieldLabel.length) {
36088             
36089             items = {
36090                 cls : "roo-radio-set-right", 
36091                 cn: [
36092                     items
36093                 ]
36094             };
36095             
36096             if(this.labelWidth > 12){
36097                 label.style = "width: " + this.labelWidth + 'px';
36098             }
36099             
36100             if(this.labelWidth < 13 && this.labelmd == 0){
36101                 this.labelmd = this.labelWidth;
36102             }
36103             
36104             if(this.labellg > 0){
36105                 label.cls += ' col-lg-' + this.labellg;
36106                 items.cls += ' col-lg-' + (12 - this.labellg);
36107             }
36108             
36109             if(this.labelmd > 0){
36110                 label.cls += ' col-md-' + this.labelmd;
36111                 items.cls += ' col-md-' + (12 - this.labelmd);
36112             }
36113             
36114             if(this.labelsm > 0){
36115                 label.cls += ' col-sm-' + this.labelsm;
36116                 items.cls += ' col-sm-' + (12 - this.labelsm);
36117             }
36118             
36119             if(this.labelxs > 0){
36120                 label.cls += ' col-xs-' + this.labelxs;
36121                 items.cls += ' col-xs-' + (12 - this.labelxs);
36122             }
36123         }
36124         
36125         var cfg = {
36126             tag : 'div',
36127             cls : 'roo-radio-set',
36128             cn : [
36129                 {
36130                     tag : 'input',
36131                     cls : 'roo-radio-set-input',
36132                     type : 'hidden',
36133                     name : this.name,
36134                     value : this.value ? this.value :  ''
36135                 },
36136                 label,
36137                 items
36138             ]
36139         };
36140         
36141         if(this.weight.length){
36142             cfg.cls += ' roo-radio-' + this.weight;
36143         }
36144         
36145         if(this.inline) {
36146             cfg.cls += ' roo-radio-set-inline';
36147         }
36148         
36149         var settings=this;
36150         ['xs','sm','md','lg'].map(function(size){
36151             if (settings[size]) {
36152                 cfg.cls += ' col-' + size + '-' + settings[size];
36153             }
36154         });
36155         
36156         return cfg;
36157         
36158     },
36159
36160     initEvents : function()
36161     {
36162         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36163         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36164         
36165         if(!this.fieldLabel.length){
36166             this.labelEl.hide();
36167         }
36168         
36169         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36170         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36171         
36172         this.indicator = this.indicatorEl();
36173         
36174         if(this.indicator){
36175             this.indicator.addClass('invisible');
36176         }
36177         
36178         this.originalValue = this.getValue();
36179         
36180     },
36181     
36182     inputEl: function ()
36183     {
36184         return this.el.select('.roo-radio-set-input', true).first();
36185     },
36186     
36187     getChildContainer : function()
36188     {
36189         return this.itemsEl;
36190     },
36191     
36192     register : function(item)
36193     {
36194         this.radioes.push(item);
36195         
36196     },
36197     
36198     validate : function()
36199     {   
36200         if(this.getVisibilityEl().hasClass('hidden')){
36201             return true;
36202         }
36203         
36204         var valid = false;
36205         
36206         Roo.each(this.radioes, function(i){
36207             if(!i.checked){
36208                 return;
36209             }
36210             
36211             valid = true;
36212             return false;
36213         });
36214         
36215         if(this.allowBlank) {
36216             return true;
36217         }
36218         
36219         if(this.disabled || valid){
36220             this.markValid();
36221             return true;
36222         }
36223         
36224         this.markInvalid();
36225         return false;
36226         
36227     },
36228     
36229     markValid : function()
36230     {
36231         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36232             this.indicatorEl().removeClass('visible');
36233             this.indicatorEl().addClass('invisible');
36234         }
36235         
36236         
36237         if (Roo.bootstrap.version == 3) {
36238             this.el.removeClass([this.invalidClass, this.validClass]);
36239             this.el.addClass(this.validClass);
36240         } else {
36241             this.el.removeClass(['is-invalid','is-valid']);
36242             this.el.addClass(['is-valid']);
36243         }
36244         this.fireEvent('valid', this);
36245     },
36246     
36247     markInvalid : function(msg)
36248     {
36249         if(this.allowBlank || this.disabled){
36250             return;
36251         }
36252         
36253         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36254             this.indicatorEl().removeClass('invisible');
36255             this.indicatorEl().addClass('visible');
36256         }
36257         if (Roo.bootstrap.version == 3) {
36258             this.el.removeClass([this.invalidClass, this.validClass]);
36259             this.el.addClass(this.invalidClass);
36260         } else {
36261             this.el.removeClass(['is-invalid','is-valid']);
36262             this.el.addClass(['is-invalid']);
36263         }
36264         
36265         this.fireEvent('invalid', this, msg);
36266         
36267     },
36268     
36269     setValue : function(v, suppressEvent)
36270     {   
36271         if(this.value === v){
36272             return;
36273         }
36274         
36275         this.value = v;
36276         
36277         if(this.rendered){
36278             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36279         }
36280         
36281         Roo.each(this.radioes, function(i){
36282             i.checked = false;
36283             i.el.removeClass('checked');
36284         });
36285         
36286         Roo.each(this.radioes, function(i){
36287             
36288             if(i.value === v || i.value.toString() === v.toString()){
36289                 i.checked = true;
36290                 i.el.addClass('checked');
36291                 
36292                 if(suppressEvent !== true){
36293                     this.fireEvent('check', this, i);
36294                 }
36295                 
36296                 return false;
36297             }
36298             
36299         }, this);
36300         
36301         this.validate();
36302     },
36303     
36304     clearInvalid : function(){
36305         
36306         if(!this.el || this.preventMark){
36307             return;
36308         }
36309         
36310         this.el.removeClass([this.invalidClass]);
36311         
36312         this.fireEvent('valid', this);
36313     }
36314     
36315 });
36316
36317 Roo.apply(Roo.bootstrap.RadioSet, {
36318     
36319     groups: {},
36320     
36321     register : function(set)
36322     {
36323         this.groups[set.name] = set;
36324     },
36325     
36326     get: function(name) 
36327     {
36328         if (typeof(this.groups[name]) == 'undefined') {
36329             return false;
36330         }
36331         
36332         return this.groups[name] ;
36333     }
36334     
36335 });
36336 /*
36337  * Based on:
36338  * Ext JS Library 1.1.1
36339  * Copyright(c) 2006-2007, Ext JS, LLC.
36340  *
36341  * Originally Released Under LGPL - original licence link has changed is not relivant.
36342  *
36343  * Fork - LGPL
36344  * <script type="text/javascript">
36345  */
36346
36347
36348 /**
36349  * @class Roo.bootstrap.SplitBar
36350  * @extends Roo.util.Observable
36351  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36352  * <br><br>
36353  * Usage:
36354  * <pre><code>
36355 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36356                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36357 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36358 split.minSize = 100;
36359 split.maxSize = 600;
36360 split.animate = true;
36361 split.on('moved', splitterMoved);
36362 </code></pre>
36363  * @constructor
36364  * Create a new SplitBar
36365  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36366  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36367  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36368  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36369                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36370                         position of the SplitBar).
36371  */
36372 Roo.bootstrap.SplitBar = function(cfg){
36373     
36374     /** @private */
36375     
36376     //{
36377     //  dragElement : elm
36378     //  resizingElement: el,
36379         // optional..
36380     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36381     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36382         // existingProxy ???
36383     //}
36384     
36385     this.el = Roo.get(cfg.dragElement, true);
36386     this.el.dom.unselectable = "on";
36387     /** @private */
36388     this.resizingEl = Roo.get(cfg.resizingElement, true);
36389
36390     /**
36391      * @private
36392      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36393      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36394      * @type Number
36395      */
36396     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36397     
36398     /**
36399      * The minimum size of the resizing element. (Defaults to 0)
36400      * @type Number
36401      */
36402     this.minSize = 0;
36403     
36404     /**
36405      * The maximum size of the resizing element. (Defaults to 2000)
36406      * @type Number
36407      */
36408     this.maxSize = 2000;
36409     
36410     /**
36411      * Whether to animate the transition to the new size
36412      * @type Boolean
36413      */
36414     this.animate = false;
36415     
36416     /**
36417      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36418      * @type Boolean
36419      */
36420     this.useShim = false;
36421     
36422     /** @private */
36423     this.shim = null;
36424     
36425     if(!cfg.existingProxy){
36426         /** @private */
36427         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36428     }else{
36429         this.proxy = Roo.get(cfg.existingProxy).dom;
36430     }
36431     /** @private */
36432     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36433     
36434     /** @private */
36435     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36436     
36437     /** @private */
36438     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36439     
36440     /** @private */
36441     this.dragSpecs = {};
36442     
36443     /**
36444      * @private The adapter to use to positon and resize elements
36445      */
36446     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36447     this.adapter.init(this);
36448     
36449     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36450         /** @private */
36451         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36452         this.el.addClass("roo-splitbar-h");
36453     }else{
36454         /** @private */
36455         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36456         this.el.addClass("roo-splitbar-v");
36457     }
36458     
36459     this.addEvents({
36460         /**
36461          * @event resize
36462          * Fires when the splitter is moved (alias for {@link #event-moved})
36463          * @param {Roo.bootstrap.SplitBar} this
36464          * @param {Number} newSize the new width or height
36465          */
36466         "resize" : true,
36467         /**
36468          * @event moved
36469          * Fires when the splitter is moved
36470          * @param {Roo.bootstrap.SplitBar} this
36471          * @param {Number} newSize the new width or height
36472          */
36473         "moved" : true,
36474         /**
36475          * @event beforeresize
36476          * Fires before the splitter is dragged
36477          * @param {Roo.bootstrap.SplitBar} this
36478          */
36479         "beforeresize" : true,
36480
36481         "beforeapply" : true
36482     });
36483
36484     Roo.util.Observable.call(this);
36485 };
36486
36487 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36488     onStartProxyDrag : function(x, y){
36489         this.fireEvent("beforeresize", this);
36490         if(!this.overlay){
36491             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36492             o.unselectable();
36493             o.enableDisplayMode("block");
36494             // all splitbars share the same overlay
36495             Roo.bootstrap.SplitBar.prototype.overlay = o;
36496         }
36497         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36498         this.overlay.show();
36499         Roo.get(this.proxy).setDisplayed("block");
36500         var size = this.adapter.getElementSize(this);
36501         this.activeMinSize = this.getMinimumSize();;
36502         this.activeMaxSize = this.getMaximumSize();;
36503         var c1 = size - this.activeMinSize;
36504         var c2 = Math.max(this.activeMaxSize - size, 0);
36505         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36506             this.dd.resetConstraints();
36507             this.dd.setXConstraint(
36508                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36509                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36510             );
36511             this.dd.setYConstraint(0, 0);
36512         }else{
36513             this.dd.resetConstraints();
36514             this.dd.setXConstraint(0, 0);
36515             this.dd.setYConstraint(
36516                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36517                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36518             );
36519          }
36520         this.dragSpecs.startSize = size;
36521         this.dragSpecs.startPoint = [x, y];
36522         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36523     },
36524     
36525     /** 
36526      * @private Called after the drag operation by the DDProxy
36527      */
36528     onEndProxyDrag : function(e){
36529         Roo.get(this.proxy).setDisplayed(false);
36530         var endPoint = Roo.lib.Event.getXY(e);
36531         if(this.overlay){
36532             this.overlay.hide();
36533         }
36534         var newSize;
36535         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36536             newSize = this.dragSpecs.startSize + 
36537                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36538                     endPoint[0] - this.dragSpecs.startPoint[0] :
36539                     this.dragSpecs.startPoint[0] - endPoint[0]
36540                 );
36541         }else{
36542             newSize = this.dragSpecs.startSize + 
36543                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36544                     endPoint[1] - this.dragSpecs.startPoint[1] :
36545                     this.dragSpecs.startPoint[1] - endPoint[1]
36546                 );
36547         }
36548         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36549         if(newSize != this.dragSpecs.startSize){
36550             if(this.fireEvent('beforeapply', this, newSize) !== false){
36551                 this.adapter.setElementSize(this, newSize);
36552                 this.fireEvent("moved", this, newSize);
36553                 this.fireEvent("resize", this, newSize);
36554             }
36555         }
36556     },
36557     
36558     /**
36559      * Get the adapter this SplitBar uses
36560      * @return The adapter object
36561      */
36562     getAdapter : function(){
36563         return this.adapter;
36564     },
36565     
36566     /**
36567      * Set the adapter this SplitBar uses
36568      * @param {Object} adapter A SplitBar adapter object
36569      */
36570     setAdapter : function(adapter){
36571         this.adapter = adapter;
36572         this.adapter.init(this);
36573     },
36574     
36575     /**
36576      * Gets the minimum size for the resizing element
36577      * @return {Number} The minimum size
36578      */
36579     getMinimumSize : function(){
36580         return this.minSize;
36581     },
36582     
36583     /**
36584      * Sets the minimum size for the resizing element
36585      * @param {Number} minSize The minimum size
36586      */
36587     setMinimumSize : function(minSize){
36588         this.minSize = minSize;
36589     },
36590     
36591     /**
36592      * Gets the maximum size for the resizing element
36593      * @return {Number} The maximum size
36594      */
36595     getMaximumSize : function(){
36596         return this.maxSize;
36597     },
36598     
36599     /**
36600      * Sets the maximum size for the resizing element
36601      * @param {Number} maxSize The maximum size
36602      */
36603     setMaximumSize : function(maxSize){
36604         this.maxSize = maxSize;
36605     },
36606     
36607     /**
36608      * Sets the initialize size for the resizing element
36609      * @param {Number} size The initial size
36610      */
36611     setCurrentSize : function(size){
36612         var oldAnimate = this.animate;
36613         this.animate = false;
36614         this.adapter.setElementSize(this, size);
36615         this.animate = oldAnimate;
36616     },
36617     
36618     /**
36619      * Destroy this splitbar. 
36620      * @param {Boolean} removeEl True to remove the element
36621      */
36622     destroy : function(removeEl){
36623         if(this.shim){
36624             this.shim.remove();
36625         }
36626         this.dd.unreg();
36627         this.proxy.parentNode.removeChild(this.proxy);
36628         if(removeEl){
36629             this.el.remove();
36630         }
36631     }
36632 });
36633
36634 /**
36635  * @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.
36636  */
36637 Roo.bootstrap.SplitBar.createProxy = function(dir){
36638     var proxy = new Roo.Element(document.createElement("div"));
36639     proxy.unselectable();
36640     var cls = 'roo-splitbar-proxy';
36641     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36642     document.body.appendChild(proxy.dom);
36643     return proxy.dom;
36644 };
36645
36646 /** 
36647  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36648  * Default Adapter. It assumes the splitter and resizing element are not positioned
36649  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36650  */
36651 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36652 };
36653
36654 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36655     // do nothing for now
36656     init : function(s){
36657     
36658     },
36659     /**
36660      * Called before drag operations to get the current size of the resizing element. 
36661      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36662      */
36663      getElementSize : function(s){
36664         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36665             return s.resizingEl.getWidth();
36666         }else{
36667             return s.resizingEl.getHeight();
36668         }
36669     },
36670     
36671     /**
36672      * Called after drag operations to set the size of the resizing element.
36673      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36674      * @param {Number} newSize The new size to set
36675      * @param {Function} onComplete A function to be invoked when resizing is complete
36676      */
36677     setElementSize : function(s, newSize, onComplete){
36678         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36679             if(!s.animate){
36680                 s.resizingEl.setWidth(newSize);
36681                 if(onComplete){
36682                     onComplete(s, newSize);
36683                 }
36684             }else{
36685                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36686             }
36687         }else{
36688             
36689             if(!s.animate){
36690                 s.resizingEl.setHeight(newSize);
36691                 if(onComplete){
36692                     onComplete(s, newSize);
36693                 }
36694             }else{
36695                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36696             }
36697         }
36698     }
36699 };
36700
36701 /** 
36702  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36703  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36704  * Adapter that  moves the splitter element to align with the resized sizing element. 
36705  * Used with an absolute positioned SplitBar.
36706  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36707  * document.body, make sure you assign an id to the body element.
36708  */
36709 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36710     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36711     this.container = Roo.get(container);
36712 };
36713
36714 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36715     init : function(s){
36716         this.basic.init(s);
36717     },
36718     
36719     getElementSize : function(s){
36720         return this.basic.getElementSize(s);
36721     },
36722     
36723     setElementSize : function(s, newSize, onComplete){
36724         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36725     },
36726     
36727     moveSplitter : function(s){
36728         var yes = Roo.bootstrap.SplitBar;
36729         switch(s.placement){
36730             case yes.LEFT:
36731                 s.el.setX(s.resizingEl.getRight());
36732                 break;
36733             case yes.RIGHT:
36734                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36735                 break;
36736             case yes.TOP:
36737                 s.el.setY(s.resizingEl.getBottom());
36738                 break;
36739             case yes.BOTTOM:
36740                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36741                 break;
36742         }
36743     }
36744 };
36745
36746 /**
36747  * Orientation constant - Create a vertical SplitBar
36748  * @static
36749  * @type Number
36750  */
36751 Roo.bootstrap.SplitBar.VERTICAL = 1;
36752
36753 /**
36754  * Orientation constant - Create a horizontal SplitBar
36755  * @static
36756  * @type Number
36757  */
36758 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36759
36760 /**
36761  * Placement constant - The resizing element is to the left of the splitter element
36762  * @static
36763  * @type Number
36764  */
36765 Roo.bootstrap.SplitBar.LEFT = 1;
36766
36767 /**
36768  * Placement constant - The resizing element is to the right of the splitter element
36769  * @static
36770  * @type Number
36771  */
36772 Roo.bootstrap.SplitBar.RIGHT = 2;
36773
36774 /**
36775  * Placement constant - The resizing element is positioned above the splitter element
36776  * @static
36777  * @type Number
36778  */
36779 Roo.bootstrap.SplitBar.TOP = 3;
36780
36781 /**
36782  * Placement constant - The resizing element is positioned under splitter element
36783  * @static
36784  * @type Number
36785  */
36786 Roo.bootstrap.SplitBar.BOTTOM = 4;
36787 Roo.namespace("Roo.bootstrap.layout");/*
36788  * Based on:
36789  * Ext JS Library 1.1.1
36790  * Copyright(c) 2006-2007, Ext JS, LLC.
36791  *
36792  * Originally Released Under LGPL - original licence link has changed is not relivant.
36793  *
36794  * Fork - LGPL
36795  * <script type="text/javascript">
36796  */
36797
36798 /**
36799  * @class Roo.bootstrap.layout.Manager
36800  * @extends Roo.bootstrap.Component
36801  * Base class for layout managers.
36802  */
36803 Roo.bootstrap.layout.Manager = function(config)
36804 {
36805     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36806
36807
36808
36809
36810
36811     /** false to disable window resize monitoring @type Boolean */
36812     this.monitorWindowResize = true;
36813     this.regions = {};
36814     this.addEvents({
36815         /**
36816          * @event layout
36817          * Fires when a layout is performed.
36818          * @param {Roo.LayoutManager} this
36819          */
36820         "layout" : true,
36821         /**
36822          * @event regionresized
36823          * Fires when the user resizes a region.
36824          * @param {Roo.LayoutRegion} region The resized region
36825          * @param {Number} newSize The new size (width for east/west, height for north/south)
36826          */
36827         "regionresized" : true,
36828         /**
36829          * @event regioncollapsed
36830          * Fires when a region is collapsed.
36831          * @param {Roo.LayoutRegion} region The collapsed region
36832          */
36833         "regioncollapsed" : true,
36834         /**
36835          * @event regionexpanded
36836          * Fires when a region is expanded.
36837          * @param {Roo.LayoutRegion} region The expanded region
36838          */
36839         "regionexpanded" : true
36840     });
36841     this.updating = false;
36842
36843     if (config.el) {
36844         this.el = Roo.get(config.el);
36845         this.initEvents();
36846     }
36847
36848 };
36849
36850 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36851
36852
36853     regions : null,
36854
36855     monitorWindowResize : true,
36856
36857
36858     updating : false,
36859
36860
36861     onRender : function(ct, position)
36862     {
36863         if(!this.el){
36864             this.el = Roo.get(ct);
36865             this.initEvents();
36866         }
36867         //this.fireEvent('render',this);
36868     },
36869
36870
36871     initEvents: function()
36872     {
36873
36874
36875         // ie scrollbar fix
36876         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36877             document.body.scroll = "no";
36878         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36879             this.el.position('relative');
36880         }
36881         this.id = this.el.id;
36882         this.el.addClass("roo-layout-container");
36883         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36884         if(this.el.dom != document.body ) {
36885             this.el.on('resize', this.layout,this);
36886             this.el.on('show', this.layout,this);
36887         }
36888
36889     },
36890
36891     /**
36892      * Returns true if this layout is currently being updated
36893      * @return {Boolean}
36894      */
36895     isUpdating : function(){
36896         return this.updating;
36897     },
36898
36899     /**
36900      * Suspend the LayoutManager from doing auto-layouts while
36901      * making multiple add or remove calls
36902      */
36903     beginUpdate : function(){
36904         this.updating = true;
36905     },
36906
36907     /**
36908      * Restore auto-layouts and optionally disable the manager from performing a layout
36909      * @param {Boolean} noLayout true to disable a layout update
36910      */
36911     endUpdate : function(noLayout){
36912         this.updating = false;
36913         if(!noLayout){
36914             this.layout();
36915         }
36916     },
36917
36918     layout: function(){
36919         // abstract...
36920     },
36921
36922     onRegionResized : function(region, newSize){
36923         this.fireEvent("regionresized", region, newSize);
36924         this.layout();
36925     },
36926
36927     onRegionCollapsed : function(region){
36928         this.fireEvent("regioncollapsed", region);
36929     },
36930
36931     onRegionExpanded : function(region){
36932         this.fireEvent("regionexpanded", region);
36933     },
36934
36935     /**
36936      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36937      * performs box-model adjustments.
36938      * @return {Object} The size as an object {width: (the width), height: (the height)}
36939      */
36940     getViewSize : function()
36941     {
36942         var size;
36943         if(this.el.dom != document.body){
36944             size = this.el.getSize();
36945         }else{
36946             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36947         }
36948         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36949         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36950         return size;
36951     },
36952
36953     /**
36954      * Returns the Element this layout is bound to.
36955      * @return {Roo.Element}
36956      */
36957     getEl : function(){
36958         return this.el;
36959     },
36960
36961     /**
36962      * Returns the specified region.
36963      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36964      * @return {Roo.LayoutRegion}
36965      */
36966     getRegion : function(target){
36967         return this.regions[target.toLowerCase()];
36968     },
36969
36970     onWindowResize : function(){
36971         if(this.monitorWindowResize){
36972             this.layout();
36973         }
36974     }
36975 });
36976 /*
36977  * Based on:
36978  * Ext JS Library 1.1.1
36979  * Copyright(c) 2006-2007, Ext JS, LLC.
36980  *
36981  * Originally Released Under LGPL - original licence link has changed is not relivant.
36982  *
36983  * Fork - LGPL
36984  * <script type="text/javascript">
36985  */
36986 /**
36987  * @class Roo.bootstrap.layout.Border
36988  * @extends Roo.bootstrap.layout.Manager
36989  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36990  * please see: examples/bootstrap/nested.html<br><br>
36991  
36992 <b>The container the layout is rendered into can be either the body element or any other element.
36993 If it is not the body element, the container needs to either be an absolute positioned element,
36994 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36995 the container size if it is not the body element.</b>
36996
36997 * @constructor
36998 * Create a new Border
36999 * @param {Object} config Configuration options
37000  */
37001 Roo.bootstrap.layout.Border = function(config){
37002     config = config || {};
37003     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37004     
37005     
37006     
37007     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37008         if(config[region]){
37009             config[region].region = region;
37010             this.addRegion(config[region]);
37011         }
37012     },this);
37013     
37014 };
37015
37016 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37017
37018 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37019     
37020     parent : false, // this might point to a 'nest' or a ???
37021     
37022     /**
37023      * Creates and adds a new region if it doesn't already exist.
37024      * @param {String} target The target region key (north, south, east, west or center).
37025      * @param {Object} config The regions config object
37026      * @return {BorderLayoutRegion} The new region
37027      */
37028     addRegion : function(config)
37029     {
37030         if(!this.regions[config.region]){
37031             var r = this.factory(config);
37032             this.bindRegion(r);
37033         }
37034         return this.regions[config.region];
37035     },
37036
37037     // private (kinda)
37038     bindRegion : function(r){
37039         this.regions[r.config.region] = r;
37040         
37041         r.on("visibilitychange",    this.layout, this);
37042         r.on("paneladded",          this.layout, this);
37043         r.on("panelremoved",        this.layout, this);
37044         r.on("invalidated",         this.layout, this);
37045         r.on("resized",             this.onRegionResized, this);
37046         r.on("collapsed",           this.onRegionCollapsed, this);
37047         r.on("expanded",            this.onRegionExpanded, this);
37048     },
37049
37050     /**
37051      * Performs a layout update.
37052      */
37053     layout : function()
37054     {
37055         if(this.updating) {
37056             return;
37057         }
37058         
37059         // render all the rebions if they have not been done alreayd?
37060         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37061             if(this.regions[region] && !this.regions[region].bodyEl){
37062                 this.regions[region].onRender(this.el)
37063             }
37064         },this);
37065         
37066         var size = this.getViewSize();
37067         var w = size.width;
37068         var h = size.height;
37069         var centerW = w;
37070         var centerH = h;
37071         var centerY = 0;
37072         var centerX = 0;
37073         //var x = 0, y = 0;
37074
37075         var rs = this.regions;
37076         var north = rs["north"];
37077         var south = rs["south"]; 
37078         var west = rs["west"];
37079         var east = rs["east"];
37080         var center = rs["center"];
37081         //if(this.hideOnLayout){ // not supported anymore
37082             //c.el.setStyle("display", "none");
37083         //}
37084         if(north && north.isVisible()){
37085             var b = north.getBox();
37086             var m = north.getMargins();
37087             b.width = w - (m.left+m.right);
37088             b.x = m.left;
37089             b.y = m.top;
37090             centerY = b.height + b.y + m.bottom;
37091             centerH -= centerY;
37092             north.updateBox(this.safeBox(b));
37093         }
37094         if(south && south.isVisible()){
37095             var b = south.getBox();
37096             var m = south.getMargins();
37097             b.width = w - (m.left+m.right);
37098             b.x = m.left;
37099             var totalHeight = (b.height + m.top + m.bottom);
37100             b.y = h - totalHeight + m.top;
37101             centerH -= totalHeight;
37102             south.updateBox(this.safeBox(b));
37103         }
37104         if(west && west.isVisible()){
37105             var b = west.getBox();
37106             var m = west.getMargins();
37107             b.height = centerH - (m.top+m.bottom);
37108             b.x = m.left;
37109             b.y = centerY + m.top;
37110             var totalWidth = (b.width + m.left + m.right);
37111             centerX += totalWidth;
37112             centerW -= totalWidth;
37113             west.updateBox(this.safeBox(b));
37114         }
37115         if(east && east.isVisible()){
37116             var b = east.getBox();
37117             var m = east.getMargins();
37118             b.height = centerH - (m.top+m.bottom);
37119             var totalWidth = (b.width + m.left + m.right);
37120             b.x = w - totalWidth + m.left;
37121             b.y = centerY + m.top;
37122             centerW -= totalWidth;
37123             east.updateBox(this.safeBox(b));
37124         }
37125         if(center){
37126             var m = center.getMargins();
37127             var centerBox = {
37128                 x: centerX + m.left,
37129                 y: centerY + m.top,
37130                 width: centerW - (m.left+m.right),
37131                 height: centerH - (m.top+m.bottom)
37132             };
37133             //if(this.hideOnLayout){
37134                 //center.el.setStyle("display", "block");
37135             //}
37136             center.updateBox(this.safeBox(centerBox));
37137         }
37138         this.el.repaint();
37139         this.fireEvent("layout", this);
37140     },
37141
37142     // private
37143     safeBox : function(box){
37144         box.width = Math.max(0, box.width);
37145         box.height = Math.max(0, box.height);
37146         return box;
37147     },
37148
37149     /**
37150      * Adds a ContentPanel (or subclass) to this layout.
37151      * @param {String} target The target region key (north, south, east, west or center).
37152      * @param {Roo.ContentPanel} panel The panel to add
37153      * @return {Roo.ContentPanel} The added panel
37154      */
37155     add : function(target, panel){
37156          
37157         target = target.toLowerCase();
37158         return this.regions[target].add(panel);
37159     },
37160
37161     /**
37162      * Remove a ContentPanel (or subclass) to this layout.
37163      * @param {String} target The target region key (north, south, east, west or center).
37164      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37165      * @return {Roo.ContentPanel} The removed panel
37166      */
37167     remove : function(target, panel){
37168         target = target.toLowerCase();
37169         return this.regions[target].remove(panel);
37170     },
37171
37172     /**
37173      * Searches all regions for a panel with the specified id
37174      * @param {String} panelId
37175      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37176      */
37177     findPanel : function(panelId){
37178         var rs = this.regions;
37179         for(var target in rs){
37180             if(typeof rs[target] != "function"){
37181                 var p = rs[target].getPanel(panelId);
37182                 if(p){
37183                     return p;
37184                 }
37185             }
37186         }
37187         return null;
37188     },
37189
37190     /**
37191      * Searches all regions for a panel with the specified id and activates (shows) it.
37192      * @param {String/ContentPanel} panelId The panels id or the panel itself
37193      * @return {Roo.ContentPanel} The shown panel or null
37194      */
37195     showPanel : function(panelId) {
37196       var rs = this.regions;
37197       for(var target in rs){
37198          var r = rs[target];
37199          if(typeof r != "function"){
37200             if(r.hasPanel(panelId)){
37201                return r.showPanel(panelId);
37202             }
37203          }
37204       }
37205       return null;
37206    },
37207
37208    /**
37209      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37210      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37211      */
37212    /*
37213     restoreState : function(provider){
37214         if(!provider){
37215             provider = Roo.state.Manager;
37216         }
37217         var sm = new Roo.LayoutStateManager();
37218         sm.init(this, provider);
37219     },
37220 */
37221  
37222  
37223     /**
37224      * Adds a xtype elements to the layout.
37225      * <pre><code>
37226
37227 layout.addxtype({
37228        xtype : 'ContentPanel',
37229        region: 'west',
37230        items: [ .... ]
37231    }
37232 );
37233
37234 layout.addxtype({
37235         xtype : 'NestedLayoutPanel',
37236         region: 'west',
37237         layout: {
37238            center: { },
37239            west: { }   
37240         },
37241         items : [ ... list of content panels or nested layout panels.. ]
37242    }
37243 );
37244 </code></pre>
37245      * @param {Object} cfg Xtype definition of item to add.
37246      */
37247     addxtype : function(cfg)
37248     {
37249         // basically accepts a pannel...
37250         // can accept a layout region..!?!?
37251         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37252         
37253         
37254         // theory?  children can only be panels??
37255         
37256         //if (!cfg.xtype.match(/Panel$/)) {
37257         //    return false;
37258         //}
37259         var ret = false;
37260         
37261         if (typeof(cfg.region) == 'undefined') {
37262             Roo.log("Failed to add Panel, region was not set");
37263             Roo.log(cfg);
37264             return false;
37265         }
37266         var region = cfg.region;
37267         delete cfg.region;
37268         
37269           
37270         var xitems = [];
37271         if (cfg.items) {
37272             xitems = cfg.items;
37273             delete cfg.items;
37274         }
37275         var nb = false;
37276         
37277         if ( region == 'center') {
37278             Roo.log("Center: " + cfg.title);
37279         }
37280         
37281         
37282         switch(cfg.xtype) 
37283         {
37284             case 'Content':  // ContentPanel (el, cfg)
37285             case 'Scroll':  // ContentPanel (el, cfg)
37286             case 'View': 
37287                 cfg.autoCreate = cfg.autoCreate || true;
37288                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37289                 //} else {
37290                 //    var el = this.el.createChild();
37291                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37292                 //}
37293                 
37294                 this.add(region, ret);
37295                 break;
37296             
37297             /*
37298             case 'TreePanel': // our new panel!
37299                 cfg.el = this.el.createChild();
37300                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37301                 this.add(region, ret);
37302                 break;
37303             */
37304             
37305             case 'Nest': 
37306                 // create a new Layout (which is  a Border Layout...
37307                 
37308                 var clayout = cfg.layout;
37309                 clayout.el  = this.el.createChild();
37310                 clayout.items   = clayout.items  || [];
37311                 
37312                 delete cfg.layout;
37313                 
37314                 // replace this exitems with the clayout ones..
37315                 xitems = clayout.items;
37316                  
37317                 // force background off if it's in center...
37318                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37319                     cfg.background = false;
37320                 }
37321                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37322                 
37323                 
37324                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37325                 //console.log('adding nested layout panel '  + cfg.toSource());
37326                 this.add(region, ret);
37327                 nb = {}; /// find first...
37328                 break;
37329             
37330             case 'Grid':
37331                 
37332                 // needs grid and region
37333                 
37334                 //var el = this.getRegion(region).el.createChild();
37335                 /*
37336                  *var el = this.el.createChild();
37337                 // create the grid first...
37338                 cfg.grid.container = el;
37339                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37340                 */
37341                 
37342                 if (region == 'center' && this.active ) {
37343                     cfg.background = false;
37344                 }
37345                 
37346                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37347                 
37348                 this.add(region, ret);
37349                 /*
37350                 if (cfg.background) {
37351                     // render grid on panel activation (if panel background)
37352                     ret.on('activate', function(gp) {
37353                         if (!gp.grid.rendered) {
37354                     //        gp.grid.render(el);
37355                         }
37356                     });
37357                 } else {
37358                   //  cfg.grid.render(el);
37359                 }
37360                 */
37361                 break;
37362            
37363            
37364             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37365                 // it was the old xcomponent building that caused this before.
37366                 // espeically if border is the top element in the tree.
37367                 ret = this;
37368                 break; 
37369                 
37370                     
37371                 
37372                 
37373                 
37374             default:
37375                 /*
37376                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37377                     
37378                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37379                     this.add(region, ret);
37380                 } else {
37381                 */
37382                     Roo.log(cfg);
37383                     throw "Can not add '" + cfg.xtype + "' to Border";
37384                     return null;
37385              
37386                                 
37387              
37388         }
37389         this.beginUpdate();
37390         // add children..
37391         var region = '';
37392         var abn = {};
37393         Roo.each(xitems, function(i)  {
37394             region = nb && i.region ? i.region : false;
37395             
37396             var add = ret.addxtype(i);
37397            
37398             if (region) {
37399                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37400                 if (!i.background) {
37401                     abn[region] = nb[region] ;
37402                 }
37403             }
37404             
37405         });
37406         this.endUpdate();
37407
37408         // make the last non-background panel active..
37409         //if (nb) { Roo.log(abn); }
37410         if (nb) {
37411             
37412             for(var r in abn) {
37413                 region = this.getRegion(r);
37414                 if (region) {
37415                     // tried using nb[r], but it does not work..
37416                      
37417                     region.showPanel(abn[r]);
37418                    
37419                 }
37420             }
37421         }
37422         return ret;
37423         
37424     },
37425     
37426     
37427 // private
37428     factory : function(cfg)
37429     {
37430         
37431         var validRegions = Roo.bootstrap.layout.Border.regions;
37432
37433         var target = cfg.region;
37434         cfg.mgr = this;
37435         
37436         var r = Roo.bootstrap.layout;
37437         Roo.log(target);
37438         switch(target){
37439             case "north":
37440                 return new r.North(cfg);
37441             case "south":
37442                 return new r.South(cfg);
37443             case "east":
37444                 return new r.East(cfg);
37445             case "west":
37446                 return new r.West(cfg);
37447             case "center":
37448                 return new r.Center(cfg);
37449         }
37450         throw 'Layout region "'+target+'" not supported.';
37451     }
37452     
37453     
37454 });
37455  /*
37456  * Based on:
37457  * Ext JS Library 1.1.1
37458  * Copyright(c) 2006-2007, Ext JS, LLC.
37459  *
37460  * Originally Released Under LGPL - original licence link has changed is not relivant.
37461  *
37462  * Fork - LGPL
37463  * <script type="text/javascript">
37464  */
37465  
37466 /**
37467  * @class Roo.bootstrap.layout.Basic
37468  * @extends Roo.util.Observable
37469  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37470  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37471  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37472  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37473  * @cfg {string}   region  the region that it inhabits..
37474  * @cfg {bool}   skipConfig skip config?
37475  * 
37476
37477  */
37478 Roo.bootstrap.layout.Basic = function(config){
37479     
37480     this.mgr = config.mgr;
37481     
37482     this.position = config.region;
37483     
37484     var skipConfig = config.skipConfig;
37485     
37486     this.events = {
37487         /**
37488          * @scope Roo.BasicLayoutRegion
37489          */
37490         
37491         /**
37492          * @event beforeremove
37493          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37494          * @param {Roo.LayoutRegion} this
37495          * @param {Roo.ContentPanel} panel The panel
37496          * @param {Object} e The cancel event object
37497          */
37498         "beforeremove" : true,
37499         /**
37500          * @event invalidated
37501          * Fires when the layout for this region is changed.
37502          * @param {Roo.LayoutRegion} this
37503          */
37504         "invalidated" : true,
37505         /**
37506          * @event visibilitychange
37507          * Fires when this region is shown or hidden 
37508          * @param {Roo.LayoutRegion} this
37509          * @param {Boolean} visibility true or false
37510          */
37511         "visibilitychange" : true,
37512         /**
37513          * @event paneladded
37514          * Fires when a panel is added. 
37515          * @param {Roo.LayoutRegion} this
37516          * @param {Roo.ContentPanel} panel The panel
37517          */
37518         "paneladded" : true,
37519         /**
37520          * @event panelremoved
37521          * Fires when a panel is removed. 
37522          * @param {Roo.LayoutRegion} this
37523          * @param {Roo.ContentPanel} panel The panel
37524          */
37525         "panelremoved" : true,
37526         /**
37527          * @event beforecollapse
37528          * Fires when this region before collapse.
37529          * @param {Roo.LayoutRegion} this
37530          */
37531         "beforecollapse" : true,
37532         /**
37533          * @event collapsed
37534          * Fires when this region is collapsed.
37535          * @param {Roo.LayoutRegion} this
37536          */
37537         "collapsed" : true,
37538         /**
37539          * @event expanded
37540          * Fires when this region is expanded.
37541          * @param {Roo.LayoutRegion} this
37542          */
37543         "expanded" : true,
37544         /**
37545          * @event slideshow
37546          * Fires when this region is slid into view.
37547          * @param {Roo.LayoutRegion} this
37548          */
37549         "slideshow" : true,
37550         /**
37551          * @event slidehide
37552          * Fires when this region slides out of view. 
37553          * @param {Roo.LayoutRegion} this
37554          */
37555         "slidehide" : true,
37556         /**
37557          * @event panelactivated
37558          * Fires when a panel is activated. 
37559          * @param {Roo.LayoutRegion} this
37560          * @param {Roo.ContentPanel} panel The activated panel
37561          */
37562         "panelactivated" : true,
37563         /**
37564          * @event resized
37565          * Fires when the user resizes this region. 
37566          * @param {Roo.LayoutRegion} this
37567          * @param {Number} newSize The new size (width for east/west, height for north/south)
37568          */
37569         "resized" : true
37570     };
37571     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37572     this.panels = new Roo.util.MixedCollection();
37573     this.panels.getKey = this.getPanelId.createDelegate(this);
37574     this.box = null;
37575     this.activePanel = null;
37576     // ensure listeners are added...
37577     
37578     if (config.listeners || config.events) {
37579         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37580             listeners : config.listeners || {},
37581             events : config.events || {}
37582         });
37583     }
37584     
37585     if(skipConfig !== true){
37586         this.applyConfig(config);
37587     }
37588 };
37589
37590 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37591 {
37592     getPanelId : function(p){
37593         return p.getId();
37594     },
37595     
37596     applyConfig : function(config){
37597         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37598         this.config = config;
37599         
37600     },
37601     
37602     /**
37603      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37604      * the width, for horizontal (north, south) the height.
37605      * @param {Number} newSize The new width or height
37606      */
37607     resizeTo : function(newSize){
37608         var el = this.el ? this.el :
37609                  (this.activePanel ? this.activePanel.getEl() : null);
37610         if(el){
37611             switch(this.position){
37612                 case "east":
37613                 case "west":
37614                     el.setWidth(newSize);
37615                     this.fireEvent("resized", this, newSize);
37616                 break;
37617                 case "north":
37618                 case "south":
37619                     el.setHeight(newSize);
37620                     this.fireEvent("resized", this, newSize);
37621                 break;                
37622             }
37623         }
37624     },
37625     
37626     getBox : function(){
37627         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37628     },
37629     
37630     getMargins : function(){
37631         return this.margins;
37632     },
37633     
37634     updateBox : function(box){
37635         this.box = box;
37636         var el = this.activePanel.getEl();
37637         el.dom.style.left = box.x + "px";
37638         el.dom.style.top = box.y + "px";
37639         this.activePanel.setSize(box.width, box.height);
37640     },
37641     
37642     /**
37643      * Returns the container element for this region.
37644      * @return {Roo.Element}
37645      */
37646     getEl : function(){
37647         return this.activePanel;
37648     },
37649     
37650     /**
37651      * Returns true if this region is currently visible.
37652      * @return {Boolean}
37653      */
37654     isVisible : function(){
37655         return this.activePanel ? true : false;
37656     },
37657     
37658     setActivePanel : function(panel){
37659         panel = this.getPanel(panel);
37660         if(this.activePanel && this.activePanel != panel){
37661             this.activePanel.setActiveState(false);
37662             this.activePanel.getEl().setLeftTop(-10000,-10000);
37663         }
37664         this.activePanel = panel;
37665         panel.setActiveState(true);
37666         if(this.box){
37667             panel.setSize(this.box.width, this.box.height);
37668         }
37669         this.fireEvent("panelactivated", this, panel);
37670         this.fireEvent("invalidated");
37671     },
37672     
37673     /**
37674      * Show the specified panel.
37675      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37676      * @return {Roo.ContentPanel} The shown panel or null
37677      */
37678     showPanel : function(panel){
37679         panel = this.getPanel(panel);
37680         if(panel){
37681             this.setActivePanel(panel);
37682         }
37683         return panel;
37684     },
37685     
37686     /**
37687      * Get the active panel for this region.
37688      * @return {Roo.ContentPanel} The active panel or null
37689      */
37690     getActivePanel : function(){
37691         return this.activePanel;
37692     },
37693     
37694     /**
37695      * Add the passed ContentPanel(s)
37696      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37697      * @return {Roo.ContentPanel} The panel added (if only one was added)
37698      */
37699     add : function(panel){
37700         if(arguments.length > 1){
37701             for(var i = 0, len = arguments.length; i < len; i++) {
37702                 this.add(arguments[i]);
37703             }
37704             return null;
37705         }
37706         if(this.hasPanel(panel)){
37707             this.showPanel(panel);
37708             return panel;
37709         }
37710         var el = panel.getEl();
37711         if(el.dom.parentNode != this.mgr.el.dom){
37712             this.mgr.el.dom.appendChild(el.dom);
37713         }
37714         if(panel.setRegion){
37715             panel.setRegion(this);
37716         }
37717         this.panels.add(panel);
37718         el.setStyle("position", "absolute");
37719         if(!panel.background){
37720             this.setActivePanel(panel);
37721             if(this.config.initialSize && this.panels.getCount()==1){
37722                 this.resizeTo(this.config.initialSize);
37723             }
37724         }
37725         this.fireEvent("paneladded", this, panel);
37726         return panel;
37727     },
37728     
37729     /**
37730      * Returns true if the panel is in this region.
37731      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37732      * @return {Boolean}
37733      */
37734     hasPanel : function(panel){
37735         if(typeof panel == "object"){ // must be panel obj
37736             panel = panel.getId();
37737         }
37738         return this.getPanel(panel) ? true : false;
37739     },
37740     
37741     /**
37742      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37743      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37744      * @param {Boolean} preservePanel Overrides the config preservePanel option
37745      * @return {Roo.ContentPanel} The panel that was removed
37746      */
37747     remove : function(panel, preservePanel){
37748         panel = this.getPanel(panel);
37749         if(!panel){
37750             return null;
37751         }
37752         var e = {};
37753         this.fireEvent("beforeremove", this, panel, e);
37754         if(e.cancel === true){
37755             return null;
37756         }
37757         var panelId = panel.getId();
37758         this.panels.removeKey(panelId);
37759         return panel;
37760     },
37761     
37762     /**
37763      * Returns the panel specified or null if it's not in this region.
37764      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37765      * @return {Roo.ContentPanel}
37766      */
37767     getPanel : function(id){
37768         if(typeof id == "object"){ // must be panel obj
37769             return id;
37770         }
37771         return this.panels.get(id);
37772     },
37773     
37774     /**
37775      * Returns this regions position (north/south/east/west/center).
37776      * @return {String} 
37777      */
37778     getPosition: function(){
37779         return this.position;    
37780     }
37781 });/*
37782  * Based on:
37783  * Ext JS Library 1.1.1
37784  * Copyright(c) 2006-2007, Ext JS, LLC.
37785  *
37786  * Originally Released Under LGPL - original licence link has changed is not relivant.
37787  *
37788  * Fork - LGPL
37789  * <script type="text/javascript">
37790  */
37791  
37792 /**
37793  * @class Roo.bootstrap.layout.Region
37794  * @extends Roo.bootstrap.layout.Basic
37795  * This class represents a region in a layout manager.
37796  
37797  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37798  * @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})
37799  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37800  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37801  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37802  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37803  * @cfg {String}    title           The title for the region (overrides panel titles)
37804  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37805  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37806  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37807  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37808  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37809  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37810  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37811  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37812  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37813  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37814
37815  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37816  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37817  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37818  * @cfg {Number}    width           For East/West panels
37819  * @cfg {Number}    height          For North/South panels
37820  * @cfg {Boolean}   split           To show the splitter
37821  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37822  * 
37823  * @cfg {string}   cls             Extra CSS classes to add to region
37824  * 
37825  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37826  * @cfg {string}   region  the region that it inhabits..
37827  *
37828
37829  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37830  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37831
37832  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37833  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37834  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37835  */
37836 Roo.bootstrap.layout.Region = function(config)
37837 {
37838     this.applyConfig(config);
37839
37840     var mgr = config.mgr;
37841     var pos = config.region;
37842     config.skipConfig = true;
37843     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37844     
37845     if (mgr.el) {
37846         this.onRender(mgr.el);   
37847     }
37848      
37849     this.visible = true;
37850     this.collapsed = false;
37851     this.unrendered_panels = [];
37852 };
37853
37854 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37855
37856     position: '', // set by wrapper (eg. north/south etc..)
37857     unrendered_panels : null,  // unrendered panels.
37858     
37859     tabPosition : false,
37860     
37861     mgr: false, // points to 'Border'
37862     
37863     
37864     createBody : function(){
37865         /** This region's body element 
37866         * @type Roo.Element */
37867         this.bodyEl = this.el.createChild({
37868                 tag: "div",
37869                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37870         });
37871     },
37872
37873     onRender: function(ctr, pos)
37874     {
37875         var dh = Roo.DomHelper;
37876         /** This region's container element 
37877         * @type Roo.Element */
37878         this.el = dh.append(ctr.dom, {
37879                 tag: "div",
37880                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37881             }, true);
37882         /** This region's title element 
37883         * @type Roo.Element */
37884     
37885         this.titleEl = dh.append(this.el.dom,  {
37886                 tag: "div",
37887                 unselectable: "on",
37888                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37889                 children:[
37890                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37891                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37892                 ]
37893             }, true);
37894         
37895         this.titleEl.enableDisplayMode();
37896         /** This region's title text element 
37897         * @type HTMLElement */
37898         this.titleTextEl = this.titleEl.dom.firstChild;
37899         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37900         /*
37901         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37902         this.closeBtn.enableDisplayMode();
37903         this.closeBtn.on("click", this.closeClicked, this);
37904         this.closeBtn.hide();
37905     */
37906         this.createBody(this.config);
37907         if(this.config.hideWhenEmpty){
37908             this.hide();
37909             this.on("paneladded", this.validateVisibility, this);
37910             this.on("panelremoved", this.validateVisibility, this);
37911         }
37912         if(this.autoScroll){
37913             this.bodyEl.setStyle("overflow", "auto");
37914         }else{
37915             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37916         }
37917         //if(c.titlebar !== false){
37918             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37919                 this.titleEl.hide();
37920             }else{
37921                 this.titleEl.show();
37922                 if(this.config.title){
37923                     this.titleTextEl.innerHTML = this.config.title;
37924                 }
37925             }
37926         //}
37927         if(this.config.collapsed){
37928             this.collapse(true);
37929         }
37930         if(this.config.hidden){
37931             this.hide();
37932         }
37933         
37934         if (this.unrendered_panels && this.unrendered_panels.length) {
37935             for (var i =0;i< this.unrendered_panels.length; i++) {
37936                 this.add(this.unrendered_panels[i]);
37937             }
37938             this.unrendered_panels = null;
37939             
37940         }
37941         
37942     },
37943     
37944     applyConfig : function(c)
37945     {
37946         /*
37947          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37948             var dh = Roo.DomHelper;
37949             if(c.titlebar !== false){
37950                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37951                 this.collapseBtn.on("click", this.collapse, this);
37952                 this.collapseBtn.enableDisplayMode();
37953                 /*
37954                 if(c.showPin === true || this.showPin){
37955                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37956                     this.stickBtn.enableDisplayMode();
37957                     this.stickBtn.on("click", this.expand, this);
37958                     this.stickBtn.hide();
37959                 }
37960                 
37961             }
37962             */
37963             /** This region's collapsed element
37964             * @type Roo.Element */
37965             /*
37966              *
37967             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37968                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37969             ]}, true);
37970             
37971             if(c.floatable !== false){
37972                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37973                this.collapsedEl.on("click", this.collapseClick, this);
37974             }
37975
37976             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37977                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37978                    id: "message", unselectable: "on", style:{"float":"left"}});
37979                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37980              }
37981             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37982             this.expandBtn.on("click", this.expand, this);
37983             
37984         }
37985         
37986         if(this.collapseBtn){
37987             this.collapseBtn.setVisible(c.collapsible == true);
37988         }
37989         
37990         this.cmargins = c.cmargins || this.cmargins ||
37991                          (this.position == "west" || this.position == "east" ?
37992                              {top: 0, left: 2, right:2, bottom: 0} :
37993                              {top: 2, left: 0, right:0, bottom: 2});
37994         */
37995         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37996         
37997         
37998         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37999         
38000         this.autoScroll = c.autoScroll || false;
38001         
38002         
38003        
38004         
38005         this.duration = c.duration || .30;
38006         this.slideDuration = c.slideDuration || .45;
38007         this.config = c;
38008        
38009     },
38010     /**
38011      * Returns true if this region is currently visible.
38012      * @return {Boolean}
38013      */
38014     isVisible : function(){
38015         return this.visible;
38016     },
38017
38018     /**
38019      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38020      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38021      */
38022     //setCollapsedTitle : function(title){
38023     //    title = title || "&#160;";
38024      //   if(this.collapsedTitleTextEl){
38025       //      this.collapsedTitleTextEl.innerHTML = title;
38026        // }
38027     //},
38028
38029     getBox : function(){
38030         var b;
38031       //  if(!this.collapsed){
38032             b = this.el.getBox(false, true);
38033        // }else{
38034           //  b = this.collapsedEl.getBox(false, true);
38035         //}
38036         return b;
38037     },
38038
38039     getMargins : function(){
38040         return this.margins;
38041         //return this.collapsed ? this.cmargins : this.margins;
38042     },
38043 /*
38044     highlight : function(){
38045         this.el.addClass("x-layout-panel-dragover");
38046     },
38047
38048     unhighlight : function(){
38049         this.el.removeClass("x-layout-panel-dragover");
38050     },
38051 */
38052     updateBox : function(box)
38053     {
38054         if (!this.bodyEl) {
38055             return; // not rendered yet..
38056         }
38057         
38058         this.box = box;
38059         if(!this.collapsed){
38060             this.el.dom.style.left = box.x + "px";
38061             this.el.dom.style.top = box.y + "px";
38062             this.updateBody(box.width, box.height);
38063         }else{
38064             this.collapsedEl.dom.style.left = box.x + "px";
38065             this.collapsedEl.dom.style.top = box.y + "px";
38066             this.collapsedEl.setSize(box.width, box.height);
38067         }
38068         if(this.tabs){
38069             this.tabs.autoSizeTabs();
38070         }
38071     },
38072
38073     updateBody : function(w, h)
38074     {
38075         if(w !== null){
38076             this.el.setWidth(w);
38077             w -= this.el.getBorderWidth("rl");
38078             if(this.config.adjustments){
38079                 w += this.config.adjustments[0];
38080             }
38081         }
38082         if(h !== null && h > 0){
38083             this.el.setHeight(h);
38084             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38085             h -= this.el.getBorderWidth("tb");
38086             if(this.config.adjustments){
38087                 h += this.config.adjustments[1];
38088             }
38089             this.bodyEl.setHeight(h);
38090             if(this.tabs){
38091                 h = this.tabs.syncHeight(h);
38092             }
38093         }
38094         if(this.panelSize){
38095             w = w !== null ? w : this.panelSize.width;
38096             h = h !== null ? h : this.panelSize.height;
38097         }
38098         if(this.activePanel){
38099             var el = this.activePanel.getEl();
38100             w = w !== null ? w : el.getWidth();
38101             h = h !== null ? h : el.getHeight();
38102             this.panelSize = {width: w, height: h};
38103             this.activePanel.setSize(w, h);
38104         }
38105         if(Roo.isIE && this.tabs){
38106             this.tabs.el.repaint();
38107         }
38108     },
38109
38110     /**
38111      * Returns the container element for this region.
38112      * @return {Roo.Element}
38113      */
38114     getEl : function(){
38115         return this.el;
38116     },
38117
38118     /**
38119      * Hides this region.
38120      */
38121     hide : function(){
38122         //if(!this.collapsed){
38123             this.el.dom.style.left = "-2000px";
38124             this.el.hide();
38125         //}else{
38126          //   this.collapsedEl.dom.style.left = "-2000px";
38127          //   this.collapsedEl.hide();
38128        // }
38129         this.visible = false;
38130         this.fireEvent("visibilitychange", this, false);
38131     },
38132
38133     /**
38134      * Shows this region if it was previously hidden.
38135      */
38136     show : function(){
38137         //if(!this.collapsed){
38138             this.el.show();
38139         //}else{
38140         //    this.collapsedEl.show();
38141        // }
38142         this.visible = true;
38143         this.fireEvent("visibilitychange", this, true);
38144     },
38145 /*
38146     closeClicked : function(){
38147         if(this.activePanel){
38148             this.remove(this.activePanel);
38149         }
38150     },
38151
38152     collapseClick : function(e){
38153         if(this.isSlid){
38154            e.stopPropagation();
38155            this.slideIn();
38156         }else{
38157            e.stopPropagation();
38158            this.slideOut();
38159         }
38160     },
38161 */
38162     /**
38163      * Collapses this region.
38164      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38165      */
38166     /*
38167     collapse : function(skipAnim, skipCheck = false){
38168         if(this.collapsed) {
38169             return;
38170         }
38171         
38172         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38173             
38174             this.collapsed = true;
38175             if(this.split){
38176                 this.split.el.hide();
38177             }
38178             if(this.config.animate && skipAnim !== true){
38179                 this.fireEvent("invalidated", this);
38180                 this.animateCollapse();
38181             }else{
38182                 this.el.setLocation(-20000,-20000);
38183                 this.el.hide();
38184                 this.collapsedEl.show();
38185                 this.fireEvent("collapsed", this);
38186                 this.fireEvent("invalidated", this);
38187             }
38188         }
38189         
38190     },
38191 */
38192     animateCollapse : function(){
38193         // overridden
38194     },
38195
38196     /**
38197      * Expands this region if it was previously collapsed.
38198      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38199      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38200      */
38201     /*
38202     expand : function(e, skipAnim){
38203         if(e) {
38204             e.stopPropagation();
38205         }
38206         if(!this.collapsed || this.el.hasActiveFx()) {
38207             return;
38208         }
38209         if(this.isSlid){
38210             this.afterSlideIn();
38211             skipAnim = true;
38212         }
38213         this.collapsed = false;
38214         if(this.config.animate && skipAnim !== true){
38215             this.animateExpand();
38216         }else{
38217             this.el.show();
38218             if(this.split){
38219                 this.split.el.show();
38220             }
38221             this.collapsedEl.setLocation(-2000,-2000);
38222             this.collapsedEl.hide();
38223             this.fireEvent("invalidated", this);
38224             this.fireEvent("expanded", this);
38225         }
38226     },
38227 */
38228     animateExpand : function(){
38229         // overridden
38230     },
38231
38232     initTabs : function()
38233     {
38234         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38235         
38236         var ts = new Roo.bootstrap.panel.Tabs({
38237             el: this.bodyEl.dom,
38238             region : this,
38239             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38240             disableTooltips: this.config.disableTabTips,
38241             toolbar : this.config.toolbar
38242         });
38243         
38244         if(this.config.hideTabs){
38245             ts.stripWrap.setDisplayed(false);
38246         }
38247         this.tabs = ts;
38248         ts.resizeTabs = this.config.resizeTabs === true;
38249         ts.minTabWidth = this.config.minTabWidth || 40;
38250         ts.maxTabWidth = this.config.maxTabWidth || 250;
38251         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38252         ts.monitorResize = false;
38253         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38254         ts.bodyEl.addClass('roo-layout-tabs-body');
38255         this.panels.each(this.initPanelAsTab, this);
38256     },
38257
38258     initPanelAsTab : function(panel){
38259         var ti = this.tabs.addTab(
38260             panel.getEl().id,
38261             panel.getTitle(),
38262             null,
38263             this.config.closeOnTab && panel.isClosable(),
38264             panel.tpl
38265         );
38266         if(panel.tabTip !== undefined){
38267             ti.setTooltip(panel.tabTip);
38268         }
38269         ti.on("activate", function(){
38270               this.setActivePanel(panel);
38271         }, this);
38272         
38273         if(this.config.closeOnTab){
38274             ti.on("beforeclose", function(t, e){
38275                 e.cancel = true;
38276                 this.remove(panel);
38277             }, this);
38278         }
38279         
38280         panel.tabItem = ti;
38281         
38282         return ti;
38283     },
38284
38285     updatePanelTitle : function(panel, title)
38286     {
38287         if(this.activePanel == panel){
38288             this.updateTitle(title);
38289         }
38290         if(this.tabs){
38291             var ti = this.tabs.getTab(panel.getEl().id);
38292             ti.setText(title);
38293             if(panel.tabTip !== undefined){
38294                 ti.setTooltip(panel.tabTip);
38295             }
38296         }
38297     },
38298
38299     updateTitle : function(title){
38300         if(this.titleTextEl && !this.config.title){
38301             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38302         }
38303     },
38304
38305     setActivePanel : function(panel)
38306     {
38307         panel = this.getPanel(panel);
38308         if(this.activePanel && this.activePanel != panel){
38309             if(this.activePanel.setActiveState(false) === false){
38310                 return;
38311             }
38312         }
38313         this.activePanel = panel;
38314         panel.setActiveState(true);
38315         if(this.panelSize){
38316             panel.setSize(this.panelSize.width, this.panelSize.height);
38317         }
38318         if(this.closeBtn){
38319             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38320         }
38321         this.updateTitle(panel.getTitle());
38322         if(this.tabs){
38323             this.fireEvent("invalidated", this);
38324         }
38325         this.fireEvent("panelactivated", this, panel);
38326     },
38327
38328     /**
38329      * Shows the specified panel.
38330      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38331      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38332      */
38333     showPanel : function(panel)
38334     {
38335         panel = this.getPanel(panel);
38336         if(panel){
38337             if(this.tabs){
38338                 var tab = this.tabs.getTab(panel.getEl().id);
38339                 if(tab.isHidden()){
38340                     this.tabs.unhideTab(tab.id);
38341                 }
38342                 tab.activate();
38343             }else{
38344                 this.setActivePanel(panel);
38345             }
38346         }
38347         return panel;
38348     },
38349
38350     /**
38351      * Get the active panel for this region.
38352      * @return {Roo.ContentPanel} The active panel or null
38353      */
38354     getActivePanel : function(){
38355         return this.activePanel;
38356     },
38357
38358     validateVisibility : function(){
38359         if(this.panels.getCount() < 1){
38360             this.updateTitle("&#160;");
38361             this.closeBtn.hide();
38362             this.hide();
38363         }else{
38364             if(!this.isVisible()){
38365                 this.show();
38366             }
38367         }
38368     },
38369
38370     /**
38371      * Adds the passed ContentPanel(s) to this region.
38372      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38373      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38374      */
38375     add : function(panel)
38376     {
38377         if(arguments.length > 1){
38378             for(var i = 0, len = arguments.length; i < len; i++) {
38379                 this.add(arguments[i]);
38380             }
38381             return null;
38382         }
38383         
38384         // if we have not been rendered yet, then we can not really do much of this..
38385         if (!this.bodyEl) {
38386             this.unrendered_panels.push(panel);
38387             return panel;
38388         }
38389         
38390         
38391         
38392         
38393         if(this.hasPanel(panel)){
38394             this.showPanel(panel);
38395             return panel;
38396         }
38397         panel.setRegion(this);
38398         this.panels.add(panel);
38399        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38400             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38401             // and hide them... ???
38402             this.bodyEl.dom.appendChild(panel.getEl().dom);
38403             if(panel.background !== true){
38404                 this.setActivePanel(panel);
38405             }
38406             this.fireEvent("paneladded", this, panel);
38407             return panel;
38408         }
38409         */
38410         if(!this.tabs){
38411             this.initTabs();
38412         }else{
38413             this.initPanelAsTab(panel);
38414         }
38415         
38416         
38417         if(panel.background !== true){
38418             this.tabs.activate(panel.getEl().id);
38419         }
38420         this.fireEvent("paneladded", this, panel);
38421         return panel;
38422     },
38423
38424     /**
38425      * Hides the tab for the specified panel.
38426      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38427      */
38428     hidePanel : function(panel){
38429         if(this.tabs && (panel = this.getPanel(panel))){
38430             this.tabs.hideTab(panel.getEl().id);
38431         }
38432     },
38433
38434     /**
38435      * Unhides the tab for a previously hidden panel.
38436      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38437      */
38438     unhidePanel : function(panel){
38439         if(this.tabs && (panel = this.getPanel(panel))){
38440             this.tabs.unhideTab(panel.getEl().id);
38441         }
38442     },
38443
38444     clearPanels : function(){
38445         while(this.panels.getCount() > 0){
38446              this.remove(this.panels.first());
38447         }
38448     },
38449
38450     /**
38451      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38452      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38453      * @param {Boolean} preservePanel Overrides the config preservePanel option
38454      * @return {Roo.ContentPanel} The panel that was removed
38455      */
38456     remove : function(panel, preservePanel)
38457     {
38458         panel = this.getPanel(panel);
38459         if(!panel){
38460             return null;
38461         }
38462         var e = {};
38463         this.fireEvent("beforeremove", this, panel, e);
38464         if(e.cancel === true){
38465             return null;
38466         }
38467         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38468         var panelId = panel.getId();
38469         this.panels.removeKey(panelId);
38470         if(preservePanel){
38471             document.body.appendChild(panel.getEl().dom);
38472         }
38473         if(this.tabs){
38474             this.tabs.removeTab(panel.getEl().id);
38475         }else if (!preservePanel){
38476             this.bodyEl.dom.removeChild(panel.getEl().dom);
38477         }
38478         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38479             var p = this.panels.first();
38480             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38481             tempEl.appendChild(p.getEl().dom);
38482             this.bodyEl.update("");
38483             this.bodyEl.dom.appendChild(p.getEl().dom);
38484             tempEl = null;
38485             this.updateTitle(p.getTitle());
38486             this.tabs = null;
38487             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38488             this.setActivePanel(p);
38489         }
38490         panel.setRegion(null);
38491         if(this.activePanel == panel){
38492             this.activePanel = null;
38493         }
38494         if(this.config.autoDestroy !== false && preservePanel !== true){
38495             try{panel.destroy();}catch(e){}
38496         }
38497         this.fireEvent("panelremoved", this, panel);
38498         return panel;
38499     },
38500
38501     /**
38502      * Returns the TabPanel component used by this region
38503      * @return {Roo.TabPanel}
38504      */
38505     getTabs : function(){
38506         return this.tabs;
38507     },
38508
38509     createTool : function(parentEl, className){
38510         var btn = Roo.DomHelper.append(parentEl, {
38511             tag: "div",
38512             cls: "x-layout-tools-button",
38513             children: [ {
38514                 tag: "div",
38515                 cls: "roo-layout-tools-button-inner " + className,
38516                 html: "&#160;"
38517             }]
38518         }, true);
38519         btn.addClassOnOver("roo-layout-tools-button-over");
38520         return btn;
38521     }
38522 });/*
38523  * Based on:
38524  * Ext JS Library 1.1.1
38525  * Copyright(c) 2006-2007, Ext JS, LLC.
38526  *
38527  * Originally Released Under LGPL - original licence link has changed is not relivant.
38528  *
38529  * Fork - LGPL
38530  * <script type="text/javascript">
38531  */
38532  
38533
38534
38535 /**
38536  * @class Roo.SplitLayoutRegion
38537  * @extends Roo.LayoutRegion
38538  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38539  */
38540 Roo.bootstrap.layout.Split = function(config){
38541     this.cursor = config.cursor;
38542     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38543 };
38544
38545 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38546 {
38547     splitTip : "Drag to resize.",
38548     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38549     useSplitTips : false,
38550
38551     applyConfig : function(config){
38552         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38553     },
38554     
38555     onRender : function(ctr,pos) {
38556         
38557         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38558         if(!this.config.split){
38559             return;
38560         }
38561         if(!this.split){
38562             
38563             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38564                             tag: "div",
38565                             id: this.el.id + "-split",
38566                             cls: "roo-layout-split roo-layout-split-"+this.position,
38567                             html: "&#160;"
38568             });
38569             /** The SplitBar for this region 
38570             * @type Roo.SplitBar */
38571             // does not exist yet...
38572             Roo.log([this.position, this.orientation]);
38573             
38574             this.split = new Roo.bootstrap.SplitBar({
38575                 dragElement : splitEl,
38576                 resizingElement: this.el,
38577                 orientation : this.orientation
38578             });
38579             
38580             this.split.on("moved", this.onSplitMove, this);
38581             this.split.useShim = this.config.useShim === true;
38582             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38583             if(this.useSplitTips){
38584                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38585             }
38586             //if(config.collapsible){
38587             //    this.split.el.on("dblclick", this.collapse,  this);
38588             //}
38589         }
38590         if(typeof this.config.minSize != "undefined"){
38591             this.split.minSize = this.config.minSize;
38592         }
38593         if(typeof this.config.maxSize != "undefined"){
38594             this.split.maxSize = this.config.maxSize;
38595         }
38596         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38597             this.hideSplitter();
38598         }
38599         
38600     },
38601
38602     getHMaxSize : function(){
38603          var cmax = this.config.maxSize || 10000;
38604          var center = this.mgr.getRegion("center");
38605          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38606     },
38607
38608     getVMaxSize : function(){
38609          var cmax = this.config.maxSize || 10000;
38610          var center = this.mgr.getRegion("center");
38611          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38612     },
38613
38614     onSplitMove : function(split, newSize){
38615         this.fireEvent("resized", this, newSize);
38616     },
38617     
38618     /** 
38619      * Returns the {@link Roo.SplitBar} for this region.
38620      * @return {Roo.SplitBar}
38621      */
38622     getSplitBar : function(){
38623         return this.split;
38624     },
38625     
38626     hide : function(){
38627         this.hideSplitter();
38628         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38629     },
38630
38631     hideSplitter : function(){
38632         if(this.split){
38633             this.split.el.setLocation(-2000,-2000);
38634             this.split.el.hide();
38635         }
38636     },
38637
38638     show : function(){
38639         if(this.split){
38640             this.split.el.show();
38641         }
38642         Roo.bootstrap.layout.Split.superclass.show.call(this);
38643     },
38644     
38645     beforeSlide: function(){
38646         if(Roo.isGecko){// firefox overflow auto bug workaround
38647             this.bodyEl.clip();
38648             if(this.tabs) {
38649                 this.tabs.bodyEl.clip();
38650             }
38651             if(this.activePanel){
38652                 this.activePanel.getEl().clip();
38653                 
38654                 if(this.activePanel.beforeSlide){
38655                     this.activePanel.beforeSlide();
38656                 }
38657             }
38658         }
38659     },
38660     
38661     afterSlide : function(){
38662         if(Roo.isGecko){// firefox overflow auto bug workaround
38663             this.bodyEl.unclip();
38664             if(this.tabs) {
38665                 this.tabs.bodyEl.unclip();
38666             }
38667             if(this.activePanel){
38668                 this.activePanel.getEl().unclip();
38669                 if(this.activePanel.afterSlide){
38670                     this.activePanel.afterSlide();
38671                 }
38672             }
38673         }
38674     },
38675
38676     initAutoHide : function(){
38677         if(this.autoHide !== false){
38678             if(!this.autoHideHd){
38679                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38680                 this.autoHideHd = {
38681                     "mouseout": function(e){
38682                         if(!e.within(this.el, true)){
38683                             st.delay(500);
38684                         }
38685                     },
38686                     "mouseover" : function(e){
38687                         st.cancel();
38688                     },
38689                     scope : this
38690                 };
38691             }
38692             this.el.on(this.autoHideHd);
38693         }
38694     },
38695
38696     clearAutoHide : function(){
38697         if(this.autoHide !== false){
38698             this.el.un("mouseout", this.autoHideHd.mouseout);
38699             this.el.un("mouseover", this.autoHideHd.mouseover);
38700         }
38701     },
38702
38703     clearMonitor : function(){
38704         Roo.get(document).un("click", this.slideInIf, this);
38705     },
38706
38707     // these names are backwards but not changed for compat
38708     slideOut : function(){
38709         if(this.isSlid || this.el.hasActiveFx()){
38710             return;
38711         }
38712         this.isSlid = true;
38713         if(this.collapseBtn){
38714             this.collapseBtn.hide();
38715         }
38716         this.closeBtnState = this.closeBtn.getStyle('display');
38717         this.closeBtn.hide();
38718         if(this.stickBtn){
38719             this.stickBtn.show();
38720         }
38721         this.el.show();
38722         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38723         this.beforeSlide();
38724         this.el.setStyle("z-index", 10001);
38725         this.el.slideIn(this.getSlideAnchor(), {
38726             callback: function(){
38727                 this.afterSlide();
38728                 this.initAutoHide();
38729                 Roo.get(document).on("click", this.slideInIf, this);
38730                 this.fireEvent("slideshow", this);
38731             },
38732             scope: this,
38733             block: true
38734         });
38735     },
38736
38737     afterSlideIn : function(){
38738         this.clearAutoHide();
38739         this.isSlid = false;
38740         this.clearMonitor();
38741         this.el.setStyle("z-index", "");
38742         if(this.collapseBtn){
38743             this.collapseBtn.show();
38744         }
38745         this.closeBtn.setStyle('display', this.closeBtnState);
38746         if(this.stickBtn){
38747             this.stickBtn.hide();
38748         }
38749         this.fireEvent("slidehide", this);
38750     },
38751
38752     slideIn : function(cb){
38753         if(!this.isSlid || this.el.hasActiveFx()){
38754             Roo.callback(cb);
38755             return;
38756         }
38757         this.isSlid = false;
38758         this.beforeSlide();
38759         this.el.slideOut(this.getSlideAnchor(), {
38760             callback: function(){
38761                 this.el.setLeftTop(-10000, -10000);
38762                 this.afterSlide();
38763                 this.afterSlideIn();
38764                 Roo.callback(cb);
38765             },
38766             scope: this,
38767             block: true
38768         });
38769     },
38770     
38771     slideInIf : function(e){
38772         if(!e.within(this.el)){
38773             this.slideIn();
38774         }
38775     },
38776
38777     animateCollapse : function(){
38778         this.beforeSlide();
38779         this.el.setStyle("z-index", 20000);
38780         var anchor = this.getSlideAnchor();
38781         this.el.slideOut(anchor, {
38782             callback : function(){
38783                 this.el.setStyle("z-index", "");
38784                 this.collapsedEl.slideIn(anchor, {duration:.3});
38785                 this.afterSlide();
38786                 this.el.setLocation(-10000,-10000);
38787                 this.el.hide();
38788                 this.fireEvent("collapsed", this);
38789             },
38790             scope: this,
38791             block: true
38792         });
38793     },
38794
38795     animateExpand : function(){
38796         this.beforeSlide();
38797         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38798         this.el.setStyle("z-index", 20000);
38799         this.collapsedEl.hide({
38800             duration:.1
38801         });
38802         this.el.slideIn(this.getSlideAnchor(), {
38803             callback : function(){
38804                 this.el.setStyle("z-index", "");
38805                 this.afterSlide();
38806                 if(this.split){
38807                     this.split.el.show();
38808                 }
38809                 this.fireEvent("invalidated", this);
38810                 this.fireEvent("expanded", this);
38811             },
38812             scope: this,
38813             block: true
38814         });
38815     },
38816
38817     anchors : {
38818         "west" : "left",
38819         "east" : "right",
38820         "north" : "top",
38821         "south" : "bottom"
38822     },
38823
38824     sanchors : {
38825         "west" : "l",
38826         "east" : "r",
38827         "north" : "t",
38828         "south" : "b"
38829     },
38830
38831     canchors : {
38832         "west" : "tl-tr",
38833         "east" : "tr-tl",
38834         "north" : "tl-bl",
38835         "south" : "bl-tl"
38836     },
38837
38838     getAnchor : function(){
38839         return this.anchors[this.position];
38840     },
38841
38842     getCollapseAnchor : function(){
38843         return this.canchors[this.position];
38844     },
38845
38846     getSlideAnchor : function(){
38847         return this.sanchors[this.position];
38848     },
38849
38850     getAlignAdj : function(){
38851         var cm = this.cmargins;
38852         switch(this.position){
38853             case "west":
38854                 return [0, 0];
38855             break;
38856             case "east":
38857                 return [0, 0];
38858             break;
38859             case "north":
38860                 return [0, 0];
38861             break;
38862             case "south":
38863                 return [0, 0];
38864             break;
38865         }
38866     },
38867
38868     getExpandAdj : function(){
38869         var c = this.collapsedEl, cm = this.cmargins;
38870         switch(this.position){
38871             case "west":
38872                 return [-(cm.right+c.getWidth()+cm.left), 0];
38873             break;
38874             case "east":
38875                 return [cm.right+c.getWidth()+cm.left, 0];
38876             break;
38877             case "north":
38878                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38879             break;
38880             case "south":
38881                 return [0, cm.top+cm.bottom+c.getHeight()];
38882             break;
38883         }
38884     }
38885 });/*
38886  * Based on:
38887  * Ext JS Library 1.1.1
38888  * Copyright(c) 2006-2007, Ext JS, LLC.
38889  *
38890  * Originally Released Under LGPL - original licence link has changed is not relivant.
38891  *
38892  * Fork - LGPL
38893  * <script type="text/javascript">
38894  */
38895 /*
38896  * These classes are private internal classes
38897  */
38898 Roo.bootstrap.layout.Center = function(config){
38899     config.region = "center";
38900     Roo.bootstrap.layout.Region.call(this, config);
38901     this.visible = true;
38902     this.minWidth = config.minWidth || 20;
38903     this.minHeight = config.minHeight || 20;
38904 };
38905
38906 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38907     hide : function(){
38908         // center panel can't be hidden
38909     },
38910     
38911     show : function(){
38912         // center panel can't be hidden
38913     },
38914     
38915     getMinWidth: function(){
38916         return this.minWidth;
38917     },
38918     
38919     getMinHeight: function(){
38920         return this.minHeight;
38921     }
38922 });
38923
38924
38925
38926
38927  
38928
38929
38930
38931
38932
38933
38934 Roo.bootstrap.layout.North = function(config)
38935 {
38936     config.region = 'north';
38937     config.cursor = 'n-resize';
38938     
38939     Roo.bootstrap.layout.Split.call(this, config);
38940     
38941     
38942     if(this.split){
38943         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38944         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38945         this.split.el.addClass("roo-layout-split-v");
38946     }
38947     var size = config.initialSize || config.height;
38948     if(typeof size != "undefined"){
38949         this.el.setHeight(size);
38950     }
38951 };
38952 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38953 {
38954     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38955     
38956     
38957     
38958     getBox : function(){
38959         if(this.collapsed){
38960             return this.collapsedEl.getBox();
38961         }
38962         var box = this.el.getBox();
38963         if(this.split){
38964             box.height += this.split.el.getHeight();
38965         }
38966         return box;
38967     },
38968     
38969     updateBox : function(box){
38970         if(this.split && !this.collapsed){
38971             box.height -= this.split.el.getHeight();
38972             this.split.el.setLeft(box.x);
38973             this.split.el.setTop(box.y+box.height);
38974             this.split.el.setWidth(box.width);
38975         }
38976         if(this.collapsed){
38977             this.updateBody(box.width, null);
38978         }
38979         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38980     }
38981 });
38982
38983
38984
38985
38986
38987 Roo.bootstrap.layout.South = function(config){
38988     config.region = 'south';
38989     config.cursor = 's-resize';
38990     Roo.bootstrap.layout.Split.call(this, config);
38991     if(this.split){
38992         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38993         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38994         this.split.el.addClass("roo-layout-split-v");
38995     }
38996     var size = config.initialSize || config.height;
38997     if(typeof size != "undefined"){
38998         this.el.setHeight(size);
38999     }
39000 };
39001
39002 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39003     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39004     getBox : function(){
39005         if(this.collapsed){
39006             return this.collapsedEl.getBox();
39007         }
39008         var box = this.el.getBox();
39009         if(this.split){
39010             var sh = this.split.el.getHeight();
39011             box.height += sh;
39012             box.y -= sh;
39013         }
39014         return box;
39015     },
39016     
39017     updateBox : function(box){
39018         if(this.split && !this.collapsed){
39019             var sh = this.split.el.getHeight();
39020             box.height -= sh;
39021             box.y += sh;
39022             this.split.el.setLeft(box.x);
39023             this.split.el.setTop(box.y-sh);
39024             this.split.el.setWidth(box.width);
39025         }
39026         if(this.collapsed){
39027             this.updateBody(box.width, null);
39028         }
39029         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39030     }
39031 });
39032
39033 Roo.bootstrap.layout.East = function(config){
39034     config.region = "east";
39035     config.cursor = "e-resize";
39036     Roo.bootstrap.layout.Split.call(this, config);
39037     if(this.split){
39038         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39039         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39040         this.split.el.addClass("roo-layout-split-h");
39041     }
39042     var size = config.initialSize || config.width;
39043     if(typeof size != "undefined"){
39044         this.el.setWidth(size);
39045     }
39046 };
39047 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39048     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39049     getBox : function(){
39050         if(this.collapsed){
39051             return this.collapsedEl.getBox();
39052         }
39053         var box = this.el.getBox();
39054         if(this.split){
39055             var sw = this.split.el.getWidth();
39056             box.width += sw;
39057             box.x -= sw;
39058         }
39059         return box;
39060     },
39061
39062     updateBox : function(box){
39063         if(this.split && !this.collapsed){
39064             var sw = this.split.el.getWidth();
39065             box.width -= sw;
39066             this.split.el.setLeft(box.x);
39067             this.split.el.setTop(box.y);
39068             this.split.el.setHeight(box.height);
39069             box.x += sw;
39070         }
39071         if(this.collapsed){
39072             this.updateBody(null, box.height);
39073         }
39074         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39075     }
39076 });
39077
39078 Roo.bootstrap.layout.West = function(config){
39079     config.region = "west";
39080     config.cursor = "w-resize";
39081     
39082     Roo.bootstrap.layout.Split.call(this, config);
39083     if(this.split){
39084         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39085         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39086         this.split.el.addClass("roo-layout-split-h");
39087     }
39088     
39089 };
39090 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39091     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39092     
39093     onRender: function(ctr, pos)
39094     {
39095         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39096         var size = this.config.initialSize || this.config.width;
39097         if(typeof size != "undefined"){
39098             this.el.setWidth(size);
39099         }
39100     },
39101     
39102     getBox : function(){
39103         if(this.collapsed){
39104             return this.collapsedEl.getBox();
39105         }
39106         var box = this.el.getBox();
39107         if(this.split){
39108             box.width += this.split.el.getWidth();
39109         }
39110         return box;
39111     },
39112     
39113     updateBox : function(box){
39114         if(this.split && !this.collapsed){
39115             var sw = this.split.el.getWidth();
39116             box.width -= sw;
39117             this.split.el.setLeft(box.x+box.width);
39118             this.split.el.setTop(box.y);
39119             this.split.el.setHeight(box.height);
39120         }
39121         if(this.collapsed){
39122             this.updateBody(null, box.height);
39123         }
39124         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39125     }
39126 });Roo.namespace("Roo.bootstrap.panel");/*
39127  * Based on:
39128  * Ext JS Library 1.1.1
39129  * Copyright(c) 2006-2007, Ext JS, LLC.
39130  *
39131  * Originally Released Under LGPL - original licence link has changed is not relivant.
39132  *
39133  * Fork - LGPL
39134  * <script type="text/javascript">
39135  */
39136 /**
39137  * @class Roo.ContentPanel
39138  * @extends Roo.util.Observable
39139  * A basic ContentPanel element.
39140  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39141  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39142  * @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
39143  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39144  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39145  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39146  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39147  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39148  * @cfg {String} title          The title for this panel
39149  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39150  * @cfg {String} url            Calls {@link #setUrl} with this value
39151  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39152  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39153  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39154  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39155  * @cfg {Boolean} badges render the badges
39156  * @cfg {String} cls  extra classes to use  
39157  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39158
39159  * @constructor
39160  * Create a new ContentPanel.
39161  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39162  * @param {String/Object} config A string to set only the title or a config object
39163  * @param {String} content (optional) Set the HTML content for this panel
39164  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39165  */
39166 Roo.bootstrap.panel.Content = function( config){
39167     
39168     this.tpl = config.tpl || false;
39169     
39170     var el = config.el;
39171     var content = config.content;
39172
39173     if(config.autoCreate){ // xtype is available if this is called from factory
39174         el = Roo.id();
39175     }
39176     this.el = Roo.get(el);
39177     if(!this.el && config && config.autoCreate){
39178         if(typeof config.autoCreate == "object"){
39179             if(!config.autoCreate.id){
39180                 config.autoCreate.id = config.id||el;
39181             }
39182             this.el = Roo.DomHelper.append(document.body,
39183                         config.autoCreate, true);
39184         }else{
39185             var elcfg =  {
39186                 tag: "div",
39187                 cls: (config.cls || '') +
39188                     (config.background ? ' bg-' + config.background : '') +
39189                     " roo-layout-inactive-content",
39190                 id: config.id||el
39191             };
39192             if (config.html) {
39193                 elcfg.html = config.html;
39194                 
39195             }
39196                         
39197             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39198         }
39199     } 
39200     this.closable = false;
39201     this.loaded = false;
39202     this.active = false;
39203    
39204       
39205     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39206         
39207         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39208         
39209         this.wrapEl = this.el; //this.el.wrap();
39210         var ti = [];
39211         if (config.toolbar.items) {
39212             ti = config.toolbar.items ;
39213             delete config.toolbar.items ;
39214         }
39215         
39216         var nitems = [];
39217         this.toolbar.render(this.wrapEl, 'before');
39218         for(var i =0;i < ti.length;i++) {
39219           //  Roo.log(['add child', items[i]]);
39220             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39221         }
39222         this.toolbar.items = nitems;
39223         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39224         delete config.toolbar;
39225         
39226     }
39227     /*
39228     // xtype created footer. - not sure if will work as we normally have to render first..
39229     if (this.footer && !this.footer.el && this.footer.xtype) {
39230         if (!this.wrapEl) {
39231             this.wrapEl = this.el.wrap();
39232         }
39233     
39234         this.footer.container = this.wrapEl.createChild();
39235          
39236         this.footer = Roo.factory(this.footer, Roo);
39237         
39238     }
39239     */
39240     
39241      if(typeof config == "string"){
39242         this.title = config;
39243     }else{
39244         Roo.apply(this, config);
39245     }
39246     
39247     if(this.resizeEl){
39248         this.resizeEl = Roo.get(this.resizeEl, true);
39249     }else{
39250         this.resizeEl = this.el;
39251     }
39252     // handle view.xtype
39253     
39254  
39255     
39256     
39257     this.addEvents({
39258         /**
39259          * @event activate
39260          * Fires when this panel is activated. 
39261          * @param {Roo.ContentPanel} this
39262          */
39263         "activate" : true,
39264         /**
39265          * @event deactivate
39266          * Fires when this panel is activated. 
39267          * @param {Roo.ContentPanel} this
39268          */
39269         "deactivate" : true,
39270
39271         /**
39272          * @event resize
39273          * Fires when this panel is resized if fitToFrame is true.
39274          * @param {Roo.ContentPanel} this
39275          * @param {Number} width The width after any component adjustments
39276          * @param {Number} height The height after any component adjustments
39277          */
39278         "resize" : true,
39279         
39280          /**
39281          * @event render
39282          * Fires when this tab is created
39283          * @param {Roo.ContentPanel} this
39284          */
39285         "render" : true
39286         
39287         
39288         
39289     });
39290     
39291
39292     
39293     
39294     if(this.autoScroll){
39295         this.resizeEl.setStyle("overflow", "auto");
39296     } else {
39297         // fix randome scrolling
39298         //this.el.on('scroll', function() {
39299         //    Roo.log('fix random scolling');
39300         //    this.scrollTo('top',0); 
39301         //});
39302     }
39303     content = content || this.content;
39304     if(content){
39305         this.setContent(content);
39306     }
39307     if(config && config.url){
39308         this.setUrl(this.url, this.params, this.loadOnce);
39309     }
39310     
39311     
39312     
39313     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39314     
39315     if (this.view && typeof(this.view.xtype) != 'undefined') {
39316         this.view.el = this.el.appendChild(document.createElement("div"));
39317         this.view = Roo.factory(this.view); 
39318         this.view.render  &&  this.view.render(false, '');  
39319     }
39320     
39321     
39322     this.fireEvent('render', this);
39323 };
39324
39325 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39326     
39327     cls : '',
39328     background : '',
39329     
39330     tabTip : '',
39331     
39332     setRegion : function(region){
39333         this.region = region;
39334         this.setActiveClass(region && !this.background);
39335     },
39336     
39337     
39338     setActiveClass: function(state)
39339     {
39340         if(state){
39341            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39342            this.el.setStyle('position','relative');
39343         }else{
39344            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39345            this.el.setStyle('position', 'absolute');
39346         } 
39347     },
39348     
39349     /**
39350      * Returns the toolbar for this Panel if one was configured. 
39351      * @return {Roo.Toolbar} 
39352      */
39353     getToolbar : function(){
39354         return this.toolbar;
39355     },
39356     
39357     setActiveState : function(active)
39358     {
39359         this.active = active;
39360         this.setActiveClass(active);
39361         if(!active){
39362             if(this.fireEvent("deactivate", this) === false){
39363                 return false;
39364             }
39365             return true;
39366         }
39367         this.fireEvent("activate", this);
39368         return true;
39369     },
39370     /**
39371      * Updates this panel's element
39372      * @param {String} content The new content
39373      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39374     */
39375     setContent : function(content, loadScripts){
39376         this.el.update(content, loadScripts);
39377     },
39378
39379     ignoreResize : function(w, h){
39380         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39381             return true;
39382         }else{
39383             this.lastSize = {width: w, height: h};
39384             return false;
39385         }
39386     },
39387     /**
39388      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39389      * @return {Roo.UpdateManager} The UpdateManager
39390      */
39391     getUpdateManager : function(){
39392         return this.el.getUpdateManager();
39393     },
39394      /**
39395      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39396      * @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:
39397 <pre><code>
39398 panel.load({
39399     url: "your-url.php",
39400     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39401     callback: yourFunction,
39402     scope: yourObject, //(optional scope)
39403     discardUrl: false,
39404     nocache: false,
39405     text: "Loading...",
39406     timeout: 30,
39407     scripts: false
39408 });
39409 </code></pre>
39410      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39411      * 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.
39412      * @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}
39413      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39414      * @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.
39415      * @return {Roo.ContentPanel} this
39416      */
39417     load : function(){
39418         var um = this.el.getUpdateManager();
39419         um.update.apply(um, arguments);
39420         return this;
39421     },
39422
39423
39424     /**
39425      * 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.
39426      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39427      * @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)
39428      * @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)
39429      * @return {Roo.UpdateManager} The UpdateManager
39430      */
39431     setUrl : function(url, params, loadOnce){
39432         if(this.refreshDelegate){
39433             this.removeListener("activate", this.refreshDelegate);
39434         }
39435         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39436         this.on("activate", this.refreshDelegate);
39437         return this.el.getUpdateManager();
39438     },
39439     
39440     _handleRefresh : function(url, params, loadOnce){
39441         if(!loadOnce || !this.loaded){
39442             var updater = this.el.getUpdateManager();
39443             updater.update(url, params, this._setLoaded.createDelegate(this));
39444         }
39445     },
39446     
39447     _setLoaded : function(){
39448         this.loaded = true;
39449     }, 
39450     
39451     /**
39452      * Returns this panel's id
39453      * @return {String} 
39454      */
39455     getId : function(){
39456         return this.el.id;
39457     },
39458     
39459     /** 
39460      * Returns this panel's element - used by regiosn to add.
39461      * @return {Roo.Element} 
39462      */
39463     getEl : function(){
39464         return this.wrapEl || this.el;
39465     },
39466     
39467    
39468     
39469     adjustForComponents : function(width, height)
39470     {
39471         //Roo.log('adjustForComponents ');
39472         if(this.resizeEl != this.el){
39473             width -= this.el.getFrameWidth('lr');
39474             height -= this.el.getFrameWidth('tb');
39475         }
39476         if(this.toolbar){
39477             var te = this.toolbar.getEl();
39478             te.setWidth(width);
39479             height -= te.getHeight();
39480         }
39481         if(this.footer){
39482             var te = this.footer.getEl();
39483             te.setWidth(width);
39484             height -= te.getHeight();
39485         }
39486         
39487         
39488         if(this.adjustments){
39489             width += this.adjustments[0];
39490             height += this.adjustments[1];
39491         }
39492         return {"width": width, "height": height};
39493     },
39494     
39495     setSize : function(width, height){
39496         if(this.fitToFrame && !this.ignoreResize(width, height)){
39497             if(this.fitContainer && this.resizeEl != this.el){
39498                 this.el.setSize(width, height);
39499             }
39500             var size = this.adjustForComponents(width, height);
39501             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39502             this.fireEvent('resize', this, size.width, size.height);
39503         }
39504     },
39505     
39506     /**
39507      * Returns this panel's title
39508      * @return {String} 
39509      */
39510     getTitle : function(){
39511         
39512         if (typeof(this.title) != 'object') {
39513             return this.title;
39514         }
39515         
39516         var t = '';
39517         for (var k in this.title) {
39518             if (!this.title.hasOwnProperty(k)) {
39519                 continue;
39520             }
39521             
39522             if (k.indexOf('-') >= 0) {
39523                 var s = k.split('-');
39524                 for (var i = 0; i<s.length; i++) {
39525                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39526                 }
39527             } else {
39528                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39529             }
39530         }
39531         return t;
39532     },
39533     
39534     /**
39535      * Set this panel's title
39536      * @param {String} title
39537      */
39538     setTitle : function(title){
39539         this.title = title;
39540         if(this.region){
39541             this.region.updatePanelTitle(this, title);
39542         }
39543     },
39544     
39545     /**
39546      * Returns true is this panel was configured to be closable
39547      * @return {Boolean} 
39548      */
39549     isClosable : function(){
39550         return this.closable;
39551     },
39552     
39553     beforeSlide : function(){
39554         this.el.clip();
39555         this.resizeEl.clip();
39556     },
39557     
39558     afterSlide : function(){
39559         this.el.unclip();
39560         this.resizeEl.unclip();
39561     },
39562     
39563     /**
39564      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39565      *   Will fail silently if the {@link #setUrl} method has not been called.
39566      *   This does not activate the panel, just updates its content.
39567      */
39568     refresh : function(){
39569         if(this.refreshDelegate){
39570            this.loaded = false;
39571            this.refreshDelegate();
39572         }
39573     },
39574     
39575     /**
39576      * Destroys this panel
39577      */
39578     destroy : function(){
39579         this.el.removeAllListeners();
39580         var tempEl = document.createElement("span");
39581         tempEl.appendChild(this.el.dom);
39582         tempEl.innerHTML = "";
39583         this.el.remove();
39584         this.el = null;
39585     },
39586     
39587     /**
39588      * form - if the content panel contains a form - this is a reference to it.
39589      * @type {Roo.form.Form}
39590      */
39591     form : false,
39592     /**
39593      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39594      *    This contains a reference to it.
39595      * @type {Roo.View}
39596      */
39597     view : false,
39598     
39599       /**
39600      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39601      * <pre><code>
39602
39603 layout.addxtype({
39604        xtype : 'Form',
39605        items: [ .... ]
39606    }
39607 );
39608
39609 </code></pre>
39610      * @param {Object} cfg Xtype definition of item to add.
39611      */
39612     
39613     
39614     getChildContainer: function () {
39615         return this.getEl();
39616     }
39617     
39618     
39619     /*
39620         var  ret = new Roo.factory(cfg);
39621         return ret;
39622         
39623         
39624         // add form..
39625         if (cfg.xtype.match(/^Form$/)) {
39626             
39627             var el;
39628             //if (this.footer) {
39629             //    el = this.footer.container.insertSibling(false, 'before');
39630             //} else {
39631                 el = this.el.createChild();
39632             //}
39633
39634             this.form = new  Roo.form.Form(cfg);
39635             
39636             
39637             if ( this.form.allItems.length) {
39638                 this.form.render(el.dom);
39639             }
39640             return this.form;
39641         }
39642         // should only have one of theses..
39643         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39644             // views.. should not be just added - used named prop 'view''
39645             
39646             cfg.el = this.el.appendChild(document.createElement("div"));
39647             // factory?
39648             
39649             var ret = new Roo.factory(cfg);
39650              
39651              ret.render && ret.render(false, ''); // render blank..
39652             this.view = ret;
39653             return ret;
39654         }
39655         return false;
39656     }
39657     \*/
39658 });
39659  
39660 /**
39661  * @class Roo.bootstrap.panel.Grid
39662  * @extends Roo.bootstrap.panel.Content
39663  * @constructor
39664  * Create a new GridPanel.
39665  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39666  * @param {Object} config A the config object
39667   
39668  */
39669
39670
39671
39672 Roo.bootstrap.panel.Grid = function(config)
39673 {
39674     
39675       
39676     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39677         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39678
39679     config.el = this.wrapper;
39680     //this.el = this.wrapper;
39681     
39682       if (config.container) {
39683         // ctor'ed from a Border/panel.grid
39684         
39685         
39686         this.wrapper.setStyle("overflow", "hidden");
39687         this.wrapper.addClass('roo-grid-container');
39688
39689     }
39690     
39691     
39692     if(config.toolbar){
39693         var tool_el = this.wrapper.createChild();    
39694         this.toolbar = Roo.factory(config.toolbar);
39695         var ti = [];
39696         if (config.toolbar.items) {
39697             ti = config.toolbar.items ;
39698             delete config.toolbar.items ;
39699         }
39700         
39701         var nitems = [];
39702         this.toolbar.render(tool_el);
39703         for(var i =0;i < ti.length;i++) {
39704           //  Roo.log(['add child', items[i]]);
39705             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39706         }
39707         this.toolbar.items = nitems;
39708         
39709         delete config.toolbar;
39710     }
39711     
39712     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39713     config.grid.scrollBody = true;;
39714     config.grid.monitorWindowResize = false; // turn off autosizing
39715     config.grid.autoHeight = false;
39716     config.grid.autoWidth = false;
39717     
39718     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39719     
39720     if (config.background) {
39721         // render grid on panel activation (if panel background)
39722         this.on('activate', function(gp) {
39723             if (!gp.grid.rendered) {
39724                 gp.grid.render(this.wrapper);
39725                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39726             }
39727         });
39728             
39729     } else {
39730         this.grid.render(this.wrapper);
39731         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39732
39733     }
39734     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39735     // ??? needed ??? config.el = this.wrapper;
39736     
39737     
39738     
39739   
39740     // xtype created footer. - not sure if will work as we normally have to render first..
39741     if (this.footer && !this.footer.el && this.footer.xtype) {
39742         
39743         var ctr = this.grid.getView().getFooterPanel(true);
39744         this.footer.dataSource = this.grid.dataSource;
39745         this.footer = Roo.factory(this.footer, Roo);
39746         this.footer.render(ctr);
39747         
39748     }
39749     
39750     
39751     
39752     
39753      
39754 };
39755
39756 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39757     getId : function(){
39758         return this.grid.id;
39759     },
39760     
39761     /**
39762      * Returns the grid for this panel
39763      * @return {Roo.bootstrap.Table} 
39764      */
39765     getGrid : function(){
39766         return this.grid;    
39767     },
39768     
39769     setSize : function(width, height){
39770         if(!this.ignoreResize(width, height)){
39771             var grid = this.grid;
39772             var size = this.adjustForComponents(width, height);
39773             // tfoot is not a footer?
39774           
39775             
39776             var gridel = grid.getGridEl();
39777             gridel.setSize(size.width, size.height);
39778             
39779             var tbd = grid.getGridEl().select('tbody', true).first();
39780             var thd = grid.getGridEl().select('thead',true).first();
39781             var tbf= grid.getGridEl().select('tfoot', true).first();
39782
39783             if (tbf) {
39784                 size.height -= thd.getHeight();
39785             }
39786             if (thd) {
39787                 size.height -= thd.getHeight();
39788             }
39789             
39790             tbd.setSize(size.width, size.height );
39791             // this is for the account management tab -seems to work there.
39792             var thd = grid.getGridEl().select('thead',true).first();
39793             //if (tbd) {
39794             //    tbd.setSize(size.width, size.height - thd.getHeight());
39795             //}
39796              
39797             grid.autoSize();
39798         }
39799     },
39800      
39801     
39802     
39803     beforeSlide : function(){
39804         this.grid.getView().scroller.clip();
39805     },
39806     
39807     afterSlide : function(){
39808         this.grid.getView().scroller.unclip();
39809     },
39810     
39811     destroy : function(){
39812         this.grid.destroy();
39813         delete this.grid;
39814         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39815     }
39816 });
39817
39818 /**
39819  * @class Roo.bootstrap.panel.Nest
39820  * @extends Roo.bootstrap.panel.Content
39821  * @constructor
39822  * Create a new Panel, that can contain a layout.Border.
39823  * 
39824  * 
39825  * @param {Roo.BorderLayout} layout The layout for this panel
39826  * @param {String/Object} config A string to set only the title or a config object
39827  */
39828 Roo.bootstrap.panel.Nest = function(config)
39829 {
39830     // construct with only one argument..
39831     /* FIXME - implement nicer consturctors
39832     if (layout.layout) {
39833         config = layout;
39834         layout = config.layout;
39835         delete config.layout;
39836     }
39837     if (layout.xtype && !layout.getEl) {
39838         // then layout needs constructing..
39839         layout = Roo.factory(layout, Roo);
39840     }
39841     */
39842     
39843     config.el =  config.layout.getEl();
39844     
39845     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39846     
39847     config.layout.monitorWindowResize = false; // turn off autosizing
39848     this.layout = config.layout;
39849     this.layout.getEl().addClass("roo-layout-nested-layout");
39850     this.layout.parent = this;
39851     
39852     
39853     
39854     
39855 };
39856
39857 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39858
39859     setSize : function(width, height){
39860         if(!this.ignoreResize(width, height)){
39861             var size = this.adjustForComponents(width, height);
39862             var el = this.layout.getEl();
39863             if (size.height < 1) {
39864                 el.setWidth(size.width);   
39865             } else {
39866                 el.setSize(size.width, size.height);
39867             }
39868             var touch = el.dom.offsetWidth;
39869             this.layout.layout();
39870             // ie requires a double layout on the first pass
39871             if(Roo.isIE && !this.initialized){
39872                 this.initialized = true;
39873                 this.layout.layout();
39874             }
39875         }
39876     },
39877     
39878     // activate all subpanels if not currently active..
39879     
39880     setActiveState : function(active){
39881         this.active = active;
39882         this.setActiveClass(active);
39883         
39884         if(!active){
39885             this.fireEvent("deactivate", this);
39886             return;
39887         }
39888         
39889         this.fireEvent("activate", this);
39890         // not sure if this should happen before or after..
39891         if (!this.layout) {
39892             return; // should not happen..
39893         }
39894         var reg = false;
39895         for (var r in this.layout.regions) {
39896             reg = this.layout.getRegion(r);
39897             if (reg.getActivePanel()) {
39898                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39899                 reg.setActivePanel(reg.getActivePanel());
39900                 continue;
39901             }
39902             if (!reg.panels.length) {
39903                 continue;
39904             }
39905             reg.showPanel(reg.getPanel(0));
39906         }
39907         
39908         
39909         
39910         
39911     },
39912     
39913     /**
39914      * Returns the nested BorderLayout for this panel
39915      * @return {Roo.BorderLayout} 
39916      */
39917     getLayout : function(){
39918         return this.layout;
39919     },
39920     
39921      /**
39922      * Adds a xtype elements to the layout of the nested panel
39923      * <pre><code>
39924
39925 panel.addxtype({
39926        xtype : 'ContentPanel',
39927        region: 'west',
39928        items: [ .... ]
39929    }
39930 );
39931
39932 panel.addxtype({
39933         xtype : 'NestedLayoutPanel',
39934         region: 'west',
39935         layout: {
39936            center: { },
39937            west: { }   
39938         },
39939         items : [ ... list of content panels or nested layout panels.. ]
39940    }
39941 );
39942 </code></pre>
39943      * @param {Object} cfg Xtype definition of item to add.
39944      */
39945     addxtype : function(cfg) {
39946         return this.layout.addxtype(cfg);
39947     
39948     }
39949 });/*
39950  * Based on:
39951  * Ext JS Library 1.1.1
39952  * Copyright(c) 2006-2007, Ext JS, LLC.
39953  *
39954  * Originally Released Under LGPL - original licence link has changed is not relivant.
39955  *
39956  * Fork - LGPL
39957  * <script type="text/javascript">
39958  */
39959 /**
39960  * @class Roo.TabPanel
39961  * @extends Roo.util.Observable
39962  * A lightweight tab container.
39963  * <br><br>
39964  * Usage:
39965  * <pre><code>
39966 // basic tabs 1, built from existing content
39967 var tabs = new Roo.TabPanel("tabs1");
39968 tabs.addTab("script", "View Script");
39969 tabs.addTab("markup", "View Markup");
39970 tabs.activate("script");
39971
39972 // more advanced tabs, built from javascript
39973 var jtabs = new Roo.TabPanel("jtabs");
39974 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39975
39976 // set up the UpdateManager
39977 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39978 var updater = tab2.getUpdateManager();
39979 updater.setDefaultUrl("ajax1.htm");
39980 tab2.on('activate', updater.refresh, updater, true);
39981
39982 // Use setUrl for Ajax loading
39983 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39984 tab3.setUrl("ajax2.htm", null, true);
39985
39986 // Disabled tab
39987 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39988 tab4.disable();
39989
39990 jtabs.activate("jtabs-1");
39991  * </code></pre>
39992  * @constructor
39993  * Create a new TabPanel.
39994  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39995  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39996  */
39997 Roo.bootstrap.panel.Tabs = function(config){
39998     /**
39999     * The container element for this TabPanel.
40000     * @type Roo.Element
40001     */
40002     this.el = Roo.get(config.el);
40003     delete config.el;
40004     if(config){
40005         if(typeof config == "boolean"){
40006             this.tabPosition = config ? "bottom" : "top";
40007         }else{
40008             Roo.apply(this, config);
40009         }
40010     }
40011     
40012     if(this.tabPosition == "bottom"){
40013         // if tabs are at the bottom = create the body first.
40014         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40015         this.el.addClass("roo-tabs-bottom");
40016     }
40017     // next create the tabs holders
40018     
40019     if (this.tabPosition == "west"){
40020         
40021         var reg = this.region; // fake it..
40022         while (reg) {
40023             if (!reg.mgr.parent) {
40024                 break;
40025             }
40026             reg = reg.mgr.parent.region;
40027         }
40028         Roo.log("got nest?");
40029         Roo.log(reg);
40030         if (reg.mgr.getRegion('west')) {
40031             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40032             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40033             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40034             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40035             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40036         
40037             
40038         }
40039         
40040         
40041     } else {
40042      
40043         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40044         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40045         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40046         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40047     }
40048     
40049     
40050     if(Roo.isIE){
40051         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40052     }
40053     
40054     // finally - if tabs are at the top, then create the body last..
40055     if(this.tabPosition != "bottom"){
40056         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40057          * @type Roo.Element
40058          */
40059         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40060         this.el.addClass("roo-tabs-top");
40061     }
40062     this.items = [];
40063
40064     this.bodyEl.setStyle("position", "relative");
40065
40066     this.active = null;
40067     this.activateDelegate = this.activate.createDelegate(this);
40068
40069     this.addEvents({
40070         /**
40071          * @event tabchange
40072          * Fires when the active tab changes
40073          * @param {Roo.TabPanel} this
40074          * @param {Roo.TabPanelItem} activePanel The new active tab
40075          */
40076         "tabchange": true,
40077         /**
40078          * @event beforetabchange
40079          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40080          * @param {Roo.TabPanel} this
40081          * @param {Object} e Set cancel to true on this object to cancel the tab change
40082          * @param {Roo.TabPanelItem} tab The tab being changed to
40083          */
40084         "beforetabchange" : true
40085     });
40086
40087     Roo.EventManager.onWindowResize(this.onResize, this);
40088     this.cpad = this.el.getPadding("lr");
40089     this.hiddenCount = 0;
40090
40091
40092     // toolbar on the tabbar support...
40093     if (this.toolbar) {
40094         alert("no toolbar support yet");
40095         this.toolbar  = false;
40096         /*
40097         var tcfg = this.toolbar;
40098         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40099         this.toolbar = new Roo.Toolbar(tcfg);
40100         if (Roo.isSafari) {
40101             var tbl = tcfg.container.child('table', true);
40102             tbl.setAttribute('width', '100%');
40103         }
40104         */
40105         
40106     }
40107    
40108
40109
40110     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40111 };
40112
40113 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40114     /*
40115      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40116      */
40117     tabPosition : "top",
40118     /*
40119      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40120      */
40121     currentTabWidth : 0,
40122     /*
40123      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40124      */
40125     minTabWidth : 40,
40126     /*
40127      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40128      */
40129     maxTabWidth : 250,
40130     /*
40131      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40132      */
40133     preferredTabWidth : 175,
40134     /*
40135      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40136      */
40137     resizeTabs : false,
40138     /*
40139      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40140      */
40141     monitorResize : true,
40142     /*
40143      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40144      */
40145     toolbar : false,  // set by caller..
40146     
40147     region : false, /// set by caller
40148     
40149     disableTooltips : true, // not used yet...
40150
40151     /**
40152      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40153      * @param {String} id The id of the div to use <b>or create</b>
40154      * @param {String} text The text for the tab
40155      * @param {String} content (optional) Content to put in the TabPanelItem body
40156      * @param {Boolean} closable (optional) True to create a close icon on the tab
40157      * @return {Roo.TabPanelItem} The created TabPanelItem
40158      */
40159     addTab : function(id, text, content, closable, tpl)
40160     {
40161         var item = new Roo.bootstrap.panel.TabItem({
40162             panel: this,
40163             id : id,
40164             text : text,
40165             closable : closable,
40166             tpl : tpl
40167         });
40168         this.addTabItem(item);
40169         if(content){
40170             item.setContent(content);
40171         }
40172         return item;
40173     },
40174
40175     /**
40176      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40177      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40178      * @return {Roo.TabPanelItem}
40179      */
40180     getTab : function(id){
40181         return this.items[id];
40182     },
40183
40184     /**
40185      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40186      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40187      */
40188     hideTab : function(id){
40189         var t = this.items[id];
40190         if(!t.isHidden()){
40191            t.setHidden(true);
40192            this.hiddenCount++;
40193            this.autoSizeTabs();
40194         }
40195     },
40196
40197     /**
40198      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40199      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40200      */
40201     unhideTab : function(id){
40202         var t = this.items[id];
40203         if(t.isHidden()){
40204            t.setHidden(false);
40205            this.hiddenCount--;
40206            this.autoSizeTabs();
40207         }
40208     },
40209
40210     /**
40211      * Adds an existing {@link Roo.TabPanelItem}.
40212      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40213      */
40214     addTabItem : function(item)
40215     {
40216         this.items[item.id] = item;
40217         this.items.push(item);
40218         this.autoSizeTabs();
40219       //  if(this.resizeTabs){
40220     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40221   //         this.autoSizeTabs();
40222 //        }else{
40223 //            item.autoSize();
40224        // }
40225     },
40226
40227     /**
40228      * Removes a {@link Roo.TabPanelItem}.
40229      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40230      */
40231     removeTab : function(id){
40232         var items = this.items;
40233         var tab = items[id];
40234         if(!tab) { return; }
40235         var index = items.indexOf(tab);
40236         if(this.active == tab && items.length > 1){
40237             var newTab = this.getNextAvailable(index);
40238             if(newTab) {
40239                 newTab.activate();
40240             }
40241         }
40242         this.stripEl.dom.removeChild(tab.pnode.dom);
40243         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40244             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40245         }
40246         items.splice(index, 1);
40247         delete this.items[tab.id];
40248         tab.fireEvent("close", tab);
40249         tab.purgeListeners();
40250         this.autoSizeTabs();
40251     },
40252
40253     getNextAvailable : function(start){
40254         var items = this.items;
40255         var index = start;
40256         // look for a next tab that will slide over to
40257         // replace the one being removed
40258         while(index < items.length){
40259             var item = items[++index];
40260             if(item && !item.isHidden()){
40261                 return item;
40262             }
40263         }
40264         // if one isn't found select the previous tab (on the left)
40265         index = start;
40266         while(index >= 0){
40267             var item = items[--index];
40268             if(item && !item.isHidden()){
40269                 return item;
40270             }
40271         }
40272         return null;
40273     },
40274
40275     /**
40276      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40277      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40278      */
40279     disableTab : function(id){
40280         var tab = this.items[id];
40281         if(tab && this.active != tab){
40282             tab.disable();
40283         }
40284     },
40285
40286     /**
40287      * Enables a {@link Roo.TabPanelItem} that is disabled.
40288      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40289      */
40290     enableTab : function(id){
40291         var tab = this.items[id];
40292         tab.enable();
40293     },
40294
40295     /**
40296      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40297      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40298      * @return {Roo.TabPanelItem} The TabPanelItem.
40299      */
40300     activate : function(id)
40301     {
40302         //Roo.log('activite:'  + id);
40303         
40304         var tab = this.items[id];
40305         if(!tab){
40306             return null;
40307         }
40308         if(tab == this.active || tab.disabled){
40309             return tab;
40310         }
40311         var e = {};
40312         this.fireEvent("beforetabchange", this, e, tab);
40313         if(e.cancel !== true && !tab.disabled){
40314             if(this.active){
40315                 this.active.hide();
40316             }
40317             this.active = this.items[id];
40318             this.active.show();
40319             this.fireEvent("tabchange", this, this.active);
40320         }
40321         return tab;
40322     },
40323
40324     /**
40325      * Gets the active {@link Roo.TabPanelItem}.
40326      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40327      */
40328     getActiveTab : function(){
40329         return this.active;
40330     },
40331
40332     /**
40333      * Updates the tab body element to fit the height of the container element
40334      * for overflow scrolling
40335      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40336      */
40337     syncHeight : function(targetHeight){
40338         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40339         var bm = this.bodyEl.getMargins();
40340         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40341         this.bodyEl.setHeight(newHeight);
40342         return newHeight;
40343     },
40344
40345     onResize : function(){
40346         if(this.monitorResize){
40347             this.autoSizeTabs();
40348         }
40349     },
40350
40351     /**
40352      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40353      */
40354     beginUpdate : function(){
40355         this.updating = true;
40356     },
40357
40358     /**
40359      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40360      */
40361     endUpdate : function(){
40362         this.updating = false;
40363         this.autoSizeTabs();
40364     },
40365
40366     /**
40367      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40368      */
40369     autoSizeTabs : function()
40370     {
40371         var count = this.items.length;
40372         var vcount = count - this.hiddenCount;
40373         
40374         if (vcount < 2) {
40375             this.stripEl.hide();
40376         } else {
40377             this.stripEl.show();
40378         }
40379         
40380         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40381             return;
40382         }
40383         
40384         
40385         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40386         var availWidth = Math.floor(w / vcount);
40387         var b = this.stripBody;
40388         if(b.getWidth() > w){
40389             var tabs = this.items;
40390             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40391             if(availWidth < this.minTabWidth){
40392                 /*if(!this.sleft){    // incomplete scrolling code
40393                     this.createScrollButtons();
40394                 }
40395                 this.showScroll();
40396                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40397             }
40398         }else{
40399             if(this.currentTabWidth < this.preferredTabWidth){
40400                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40401             }
40402         }
40403     },
40404
40405     /**
40406      * Returns the number of tabs in this TabPanel.
40407      * @return {Number}
40408      */
40409      getCount : function(){
40410          return this.items.length;
40411      },
40412
40413     /**
40414      * Resizes all the tabs to the passed width
40415      * @param {Number} The new width
40416      */
40417     setTabWidth : function(width){
40418         this.currentTabWidth = width;
40419         for(var i = 0, len = this.items.length; i < len; i++) {
40420                 if(!this.items[i].isHidden()) {
40421                 this.items[i].setWidth(width);
40422             }
40423         }
40424     },
40425
40426     /**
40427      * Destroys this TabPanel
40428      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40429      */
40430     destroy : function(removeEl){
40431         Roo.EventManager.removeResizeListener(this.onResize, this);
40432         for(var i = 0, len = this.items.length; i < len; i++){
40433             this.items[i].purgeListeners();
40434         }
40435         if(removeEl === true){
40436             this.el.update("");
40437             this.el.remove();
40438         }
40439     },
40440     
40441     createStrip : function(container)
40442     {
40443         var strip = document.createElement("nav");
40444         strip.className = Roo.bootstrap.version == 4 ?
40445             "navbar-light bg-light" : 
40446             "navbar navbar-default"; //"x-tabs-wrap";
40447         container.appendChild(strip);
40448         return strip;
40449     },
40450     
40451     createStripList : function(strip)
40452     {
40453         // div wrapper for retard IE
40454         // returns the "tr" element.
40455         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40456         //'<div class="x-tabs-strip-wrap">'+
40457           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40458           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40459         return strip.firstChild; //.firstChild.firstChild.firstChild;
40460     },
40461     createBody : function(container)
40462     {
40463         var body = document.createElement("div");
40464         Roo.id(body, "tab-body");
40465         //Roo.fly(body).addClass("x-tabs-body");
40466         Roo.fly(body).addClass("tab-content");
40467         container.appendChild(body);
40468         return body;
40469     },
40470     createItemBody :function(bodyEl, id){
40471         var body = Roo.getDom(id);
40472         if(!body){
40473             body = document.createElement("div");
40474             body.id = id;
40475         }
40476         //Roo.fly(body).addClass("x-tabs-item-body");
40477         Roo.fly(body).addClass("tab-pane");
40478          bodyEl.insertBefore(body, bodyEl.firstChild);
40479         return body;
40480     },
40481     /** @private */
40482     createStripElements :  function(stripEl, text, closable, tpl)
40483     {
40484         var td = document.createElement("li"); // was td..
40485         td.className = 'nav-item';
40486         
40487         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40488         
40489         
40490         stripEl.appendChild(td);
40491         /*if(closable){
40492             td.className = "x-tabs-closable";
40493             if(!this.closeTpl){
40494                 this.closeTpl = new Roo.Template(
40495                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40496                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40497                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40498                 );
40499             }
40500             var el = this.closeTpl.overwrite(td, {"text": text});
40501             var close = el.getElementsByTagName("div")[0];
40502             var inner = el.getElementsByTagName("em")[0];
40503             return {"el": el, "close": close, "inner": inner};
40504         } else {
40505         */
40506         // not sure what this is..
40507 //            if(!this.tabTpl){
40508                 //this.tabTpl = new Roo.Template(
40509                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40510                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40511                 //);
40512 //                this.tabTpl = new Roo.Template(
40513 //                   '<a href="#">' +
40514 //                   '<span unselectable="on"' +
40515 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40516 //                            ' >{text}</span></a>'
40517 //                );
40518 //                
40519 //            }
40520
40521
40522             var template = tpl || this.tabTpl || false;
40523             
40524             if(!template){
40525                 template =  new Roo.Template(
40526                         Roo.bootstrap.version == 4 ? 
40527                             (
40528                                 '<a class="nav-link" href="#" unselectable="on"' +
40529                                      (this.disableTooltips ? '' : ' title="{text}"') +
40530                                      ' >{text}</a>'
40531                             ) : (
40532                                 '<a class="nav-link" href="#">' +
40533                                 '<span unselectable="on"' +
40534                                          (this.disableTooltips ? '' : ' title="{text}"') +
40535                                     ' >{text}</span></a>'
40536                             )
40537                 );
40538             }
40539             
40540             switch (typeof(template)) {
40541                 case 'object' :
40542                     break;
40543                 case 'string' :
40544                     template = new Roo.Template(template);
40545                     break;
40546                 default :
40547                     break;
40548             }
40549             
40550             var el = template.overwrite(td, {"text": text});
40551             
40552             var inner = el.getElementsByTagName("span")[0];
40553             
40554             return {"el": el, "inner": inner};
40555             
40556     }
40557         
40558     
40559 });
40560
40561 /**
40562  * @class Roo.TabPanelItem
40563  * @extends Roo.util.Observable
40564  * Represents an individual item (tab plus body) in a TabPanel.
40565  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40566  * @param {String} id The id of this TabPanelItem
40567  * @param {String} text The text for the tab of this TabPanelItem
40568  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40569  */
40570 Roo.bootstrap.panel.TabItem = function(config){
40571     /**
40572      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40573      * @type Roo.TabPanel
40574      */
40575     this.tabPanel = config.panel;
40576     /**
40577      * The id for this TabPanelItem
40578      * @type String
40579      */
40580     this.id = config.id;
40581     /** @private */
40582     this.disabled = false;
40583     /** @private */
40584     this.text = config.text;
40585     /** @private */
40586     this.loaded = false;
40587     this.closable = config.closable;
40588
40589     /**
40590      * The body element for this TabPanelItem.
40591      * @type Roo.Element
40592      */
40593     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40594     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40595     this.bodyEl.setStyle("display", "block");
40596     this.bodyEl.setStyle("zoom", "1");
40597     //this.hideAction();
40598
40599     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40600     /** @private */
40601     this.el = Roo.get(els.el);
40602     this.inner = Roo.get(els.inner, true);
40603      this.textEl = Roo.bootstrap.version == 4 ?
40604         this.el : Roo.get(this.el.dom.firstChild, true);
40605
40606     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40607     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40608
40609     
40610 //    this.el.on("mousedown", this.onTabMouseDown, this);
40611     this.el.on("click", this.onTabClick, this);
40612     /** @private */
40613     if(config.closable){
40614         var c = Roo.get(els.close, true);
40615         c.dom.title = this.closeText;
40616         c.addClassOnOver("close-over");
40617         c.on("click", this.closeClick, this);
40618      }
40619
40620     this.addEvents({
40621          /**
40622          * @event activate
40623          * Fires when this tab becomes the active tab.
40624          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40625          * @param {Roo.TabPanelItem} this
40626          */
40627         "activate": true,
40628         /**
40629          * @event beforeclose
40630          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40631          * @param {Roo.TabPanelItem} this
40632          * @param {Object} e Set cancel to true on this object to cancel the close.
40633          */
40634         "beforeclose": true,
40635         /**
40636          * @event close
40637          * Fires when this tab is closed.
40638          * @param {Roo.TabPanelItem} this
40639          */
40640          "close": true,
40641         /**
40642          * @event deactivate
40643          * Fires when this tab is no longer the active tab.
40644          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40645          * @param {Roo.TabPanelItem} this
40646          */
40647          "deactivate" : true
40648     });
40649     this.hidden = false;
40650
40651     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40652 };
40653
40654 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40655            {
40656     purgeListeners : function(){
40657        Roo.util.Observable.prototype.purgeListeners.call(this);
40658        this.el.removeAllListeners();
40659     },
40660     /**
40661      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40662      */
40663     show : function(){
40664         this.status_node.addClass("active");
40665         this.showAction();
40666         if(Roo.isOpera){
40667             this.tabPanel.stripWrap.repaint();
40668         }
40669         this.fireEvent("activate", this.tabPanel, this);
40670     },
40671
40672     /**
40673      * Returns true if this tab is the active tab.
40674      * @return {Boolean}
40675      */
40676     isActive : function(){
40677         return this.tabPanel.getActiveTab() == this;
40678     },
40679
40680     /**
40681      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40682      */
40683     hide : function(){
40684         this.status_node.removeClass("active");
40685         this.hideAction();
40686         this.fireEvent("deactivate", this.tabPanel, this);
40687     },
40688
40689     hideAction : function(){
40690         this.bodyEl.hide();
40691         this.bodyEl.setStyle("position", "absolute");
40692         this.bodyEl.setLeft("-20000px");
40693         this.bodyEl.setTop("-20000px");
40694     },
40695
40696     showAction : function(){
40697         this.bodyEl.setStyle("position", "relative");
40698         this.bodyEl.setTop("");
40699         this.bodyEl.setLeft("");
40700         this.bodyEl.show();
40701     },
40702
40703     /**
40704      * Set the tooltip for the tab.
40705      * @param {String} tooltip The tab's tooltip
40706      */
40707     setTooltip : function(text){
40708         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40709             this.textEl.dom.qtip = text;
40710             this.textEl.dom.removeAttribute('title');
40711         }else{
40712             this.textEl.dom.title = text;
40713         }
40714     },
40715
40716     onTabClick : function(e){
40717         e.preventDefault();
40718         this.tabPanel.activate(this.id);
40719     },
40720
40721     onTabMouseDown : function(e){
40722         e.preventDefault();
40723         this.tabPanel.activate(this.id);
40724     },
40725 /*
40726     getWidth : function(){
40727         return this.inner.getWidth();
40728     },
40729
40730     setWidth : function(width){
40731         var iwidth = width - this.linode.getPadding("lr");
40732         this.inner.setWidth(iwidth);
40733         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40734         this.linode.setWidth(width);
40735     },
40736 */
40737     /**
40738      * Show or hide the tab
40739      * @param {Boolean} hidden True to hide or false to show.
40740      */
40741     setHidden : function(hidden){
40742         this.hidden = hidden;
40743         this.linode.setStyle("display", hidden ? "none" : "");
40744     },
40745
40746     /**
40747      * Returns true if this tab is "hidden"
40748      * @return {Boolean}
40749      */
40750     isHidden : function(){
40751         return this.hidden;
40752     },
40753
40754     /**
40755      * Returns the text for this tab
40756      * @return {String}
40757      */
40758     getText : function(){
40759         return this.text;
40760     },
40761     /*
40762     autoSize : function(){
40763         //this.el.beginMeasure();
40764         this.textEl.setWidth(1);
40765         /*
40766          *  #2804 [new] Tabs in Roojs
40767          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40768          */
40769         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40770         //this.el.endMeasure();
40771     //},
40772
40773     /**
40774      * Sets the text for the tab (Note: this also sets the tooltip text)
40775      * @param {String} text The tab's text and tooltip
40776      */
40777     setText : function(text){
40778         this.text = text;
40779         this.textEl.update(text);
40780         this.setTooltip(text);
40781         //if(!this.tabPanel.resizeTabs){
40782         //    this.autoSize();
40783         //}
40784     },
40785     /**
40786      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40787      */
40788     activate : function(){
40789         this.tabPanel.activate(this.id);
40790     },
40791
40792     /**
40793      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40794      */
40795     disable : function(){
40796         if(this.tabPanel.active != this){
40797             this.disabled = true;
40798             this.status_node.addClass("disabled");
40799         }
40800     },
40801
40802     /**
40803      * Enables this TabPanelItem if it was previously disabled.
40804      */
40805     enable : function(){
40806         this.disabled = false;
40807         this.status_node.removeClass("disabled");
40808     },
40809
40810     /**
40811      * Sets the content for this TabPanelItem.
40812      * @param {String} content The content
40813      * @param {Boolean} loadScripts true to look for and load scripts
40814      */
40815     setContent : function(content, loadScripts){
40816         this.bodyEl.update(content, loadScripts);
40817     },
40818
40819     /**
40820      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40821      * @return {Roo.UpdateManager} The UpdateManager
40822      */
40823     getUpdateManager : function(){
40824         return this.bodyEl.getUpdateManager();
40825     },
40826
40827     /**
40828      * Set a URL to be used to load the content for this TabPanelItem.
40829      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40830      * @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)
40831      * @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)
40832      * @return {Roo.UpdateManager} The UpdateManager
40833      */
40834     setUrl : function(url, params, loadOnce){
40835         if(this.refreshDelegate){
40836             this.un('activate', this.refreshDelegate);
40837         }
40838         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40839         this.on("activate", this.refreshDelegate);
40840         return this.bodyEl.getUpdateManager();
40841     },
40842
40843     /** @private */
40844     _handleRefresh : function(url, params, loadOnce){
40845         if(!loadOnce || !this.loaded){
40846             var updater = this.bodyEl.getUpdateManager();
40847             updater.update(url, params, this._setLoaded.createDelegate(this));
40848         }
40849     },
40850
40851     /**
40852      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40853      *   Will fail silently if the setUrl method has not been called.
40854      *   This does not activate the panel, just updates its content.
40855      */
40856     refresh : function(){
40857         if(this.refreshDelegate){
40858            this.loaded = false;
40859            this.refreshDelegate();
40860         }
40861     },
40862
40863     /** @private */
40864     _setLoaded : function(){
40865         this.loaded = true;
40866     },
40867
40868     /** @private */
40869     closeClick : function(e){
40870         var o = {};
40871         e.stopEvent();
40872         this.fireEvent("beforeclose", this, o);
40873         if(o.cancel !== true){
40874             this.tabPanel.removeTab(this.id);
40875         }
40876     },
40877     /**
40878      * The text displayed in the tooltip for the close icon.
40879      * @type String
40880      */
40881     closeText : "Close this tab"
40882 });
40883 /**
40884 *    This script refer to:
40885 *    Title: International Telephone Input
40886 *    Author: Jack O'Connor
40887 *    Code version:  v12.1.12
40888 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40889 **/
40890
40891 Roo.bootstrap.PhoneInputData = function() {
40892     var d = [
40893       [
40894         "Afghanistan (‫افغانستان‬‎)",
40895         "af",
40896         "93"
40897       ],
40898       [
40899         "Albania (Shqipëri)",
40900         "al",
40901         "355"
40902       ],
40903       [
40904         "Algeria (‫الجزائر‬‎)",
40905         "dz",
40906         "213"
40907       ],
40908       [
40909         "American Samoa",
40910         "as",
40911         "1684"
40912       ],
40913       [
40914         "Andorra",
40915         "ad",
40916         "376"
40917       ],
40918       [
40919         "Angola",
40920         "ao",
40921         "244"
40922       ],
40923       [
40924         "Anguilla",
40925         "ai",
40926         "1264"
40927       ],
40928       [
40929         "Antigua and Barbuda",
40930         "ag",
40931         "1268"
40932       ],
40933       [
40934         "Argentina",
40935         "ar",
40936         "54"
40937       ],
40938       [
40939         "Armenia (Հայաստան)",
40940         "am",
40941         "374"
40942       ],
40943       [
40944         "Aruba",
40945         "aw",
40946         "297"
40947       ],
40948       [
40949         "Australia",
40950         "au",
40951         "61",
40952         0
40953       ],
40954       [
40955         "Austria (Österreich)",
40956         "at",
40957         "43"
40958       ],
40959       [
40960         "Azerbaijan (Azərbaycan)",
40961         "az",
40962         "994"
40963       ],
40964       [
40965         "Bahamas",
40966         "bs",
40967         "1242"
40968       ],
40969       [
40970         "Bahrain (‫البحرين‬‎)",
40971         "bh",
40972         "973"
40973       ],
40974       [
40975         "Bangladesh (বাংলাদেশ)",
40976         "bd",
40977         "880"
40978       ],
40979       [
40980         "Barbados",
40981         "bb",
40982         "1246"
40983       ],
40984       [
40985         "Belarus (Беларусь)",
40986         "by",
40987         "375"
40988       ],
40989       [
40990         "Belgium (België)",
40991         "be",
40992         "32"
40993       ],
40994       [
40995         "Belize",
40996         "bz",
40997         "501"
40998       ],
40999       [
41000         "Benin (Bénin)",
41001         "bj",
41002         "229"
41003       ],
41004       [
41005         "Bermuda",
41006         "bm",
41007         "1441"
41008       ],
41009       [
41010         "Bhutan (འབྲུག)",
41011         "bt",
41012         "975"
41013       ],
41014       [
41015         "Bolivia",
41016         "bo",
41017         "591"
41018       ],
41019       [
41020         "Bosnia and Herzegovina (Босна и Херцеговина)",
41021         "ba",
41022         "387"
41023       ],
41024       [
41025         "Botswana",
41026         "bw",
41027         "267"
41028       ],
41029       [
41030         "Brazil (Brasil)",
41031         "br",
41032         "55"
41033       ],
41034       [
41035         "British Indian Ocean Territory",
41036         "io",
41037         "246"
41038       ],
41039       [
41040         "British Virgin Islands",
41041         "vg",
41042         "1284"
41043       ],
41044       [
41045         "Brunei",
41046         "bn",
41047         "673"
41048       ],
41049       [
41050         "Bulgaria (България)",
41051         "bg",
41052         "359"
41053       ],
41054       [
41055         "Burkina Faso",
41056         "bf",
41057         "226"
41058       ],
41059       [
41060         "Burundi (Uburundi)",
41061         "bi",
41062         "257"
41063       ],
41064       [
41065         "Cambodia (កម្ពុជា)",
41066         "kh",
41067         "855"
41068       ],
41069       [
41070         "Cameroon (Cameroun)",
41071         "cm",
41072         "237"
41073       ],
41074       [
41075         "Canada",
41076         "ca",
41077         "1",
41078         1,
41079         ["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"]
41080       ],
41081       [
41082         "Cape Verde (Kabu Verdi)",
41083         "cv",
41084         "238"
41085       ],
41086       [
41087         "Caribbean Netherlands",
41088         "bq",
41089         "599",
41090         1
41091       ],
41092       [
41093         "Cayman Islands",
41094         "ky",
41095         "1345"
41096       ],
41097       [
41098         "Central African Republic (République centrafricaine)",
41099         "cf",
41100         "236"
41101       ],
41102       [
41103         "Chad (Tchad)",
41104         "td",
41105         "235"
41106       ],
41107       [
41108         "Chile",
41109         "cl",
41110         "56"
41111       ],
41112       [
41113         "China (中国)",
41114         "cn",
41115         "86"
41116       ],
41117       [
41118         "Christmas Island",
41119         "cx",
41120         "61",
41121         2
41122       ],
41123       [
41124         "Cocos (Keeling) Islands",
41125         "cc",
41126         "61",
41127         1
41128       ],
41129       [
41130         "Colombia",
41131         "co",
41132         "57"
41133       ],
41134       [
41135         "Comoros (‫جزر القمر‬‎)",
41136         "km",
41137         "269"
41138       ],
41139       [
41140         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41141         "cd",
41142         "243"
41143       ],
41144       [
41145         "Congo (Republic) (Congo-Brazzaville)",
41146         "cg",
41147         "242"
41148       ],
41149       [
41150         "Cook Islands",
41151         "ck",
41152         "682"
41153       ],
41154       [
41155         "Costa Rica",
41156         "cr",
41157         "506"
41158       ],
41159       [
41160         "Côte d’Ivoire",
41161         "ci",
41162         "225"
41163       ],
41164       [
41165         "Croatia (Hrvatska)",
41166         "hr",
41167         "385"
41168       ],
41169       [
41170         "Cuba",
41171         "cu",
41172         "53"
41173       ],
41174       [
41175         "Curaçao",
41176         "cw",
41177         "599",
41178         0
41179       ],
41180       [
41181         "Cyprus (Κύπρος)",
41182         "cy",
41183         "357"
41184       ],
41185       [
41186         "Czech Republic (Česká republika)",
41187         "cz",
41188         "420"
41189       ],
41190       [
41191         "Denmark (Danmark)",
41192         "dk",
41193         "45"
41194       ],
41195       [
41196         "Djibouti",
41197         "dj",
41198         "253"
41199       ],
41200       [
41201         "Dominica",
41202         "dm",
41203         "1767"
41204       ],
41205       [
41206         "Dominican Republic (República Dominicana)",
41207         "do",
41208         "1",
41209         2,
41210         ["809", "829", "849"]
41211       ],
41212       [
41213         "Ecuador",
41214         "ec",
41215         "593"
41216       ],
41217       [
41218         "Egypt (‫مصر‬‎)",
41219         "eg",
41220         "20"
41221       ],
41222       [
41223         "El Salvador",
41224         "sv",
41225         "503"
41226       ],
41227       [
41228         "Equatorial Guinea (Guinea Ecuatorial)",
41229         "gq",
41230         "240"
41231       ],
41232       [
41233         "Eritrea",
41234         "er",
41235         "291"
41236       ],
41237       [
41238         "Estonia (Eesti)",
41239         "ee",
41240         "372"
41241       ],
41242       [
41243         "Ethiopia",
41244         "et",
41245         "251"
41246       ],
41247       [
41248         "Falkland Islands (Islas Malvinas)",
41249         "fk",
41250         "500"
41251       ],
41252       [
41253         "Faroe Islands (Føroyar)",
41254         "fo",
41255         "298"
41256       ],
41257       [
41258         "Fiji",
41259         "fj",
41260         "679"
41261       ],
41262       [
41263         "Finland (Suomi)",
41264         "fi",
41265         "358",
41266         0
41267       ],
41268       [
41269         "France",
41270         "fr",
41271         "33"
41272       ],
41273       [
41274         "French Guiana (Guyane française)",
41275         "gf",
41276         "594"
41277       ],
41278       [
41279         "French Polynesia (Polynésie française)",
41280         "pf",
41281         "689"
41282       ],
41283       [
41284         "Gabon",
41285         "ga",
41286         "241"
41287       ],
41288       [
41289         "Gambia",
41290         "gm",
41291         "220"
41292       ],
41293       [
41294         "Georgia (საქართველო)",
41295         "ge",
41296         "995"
41297       ],
41298       [
41299         "Germany (Deutschland)",
41300         "de",
41301         "49"
41302       ],
41303       [
41304         "Ghana (Gaana)",
41305         "gh",
41306         "233"
41307       ],
41308       [
41309         "Gibraltar",
41310         "gi",
41311         "350"
41312       ],
41313       [
41314         "Greece (Ελλάδα)",
41315         "gr",
41316         "30"
41317       ],
41318       [
41319         "Greenland (Kalaallit Nunaat)",
41320         "gl",
41321         "299"
41322       ],
41323       [
41324         "Grenada",
41325         "gd",
41326         "1473"
41327       ],
41328       [
41329         "Guadeloupe",
41330         "gp",
41331         "590",
41332         0
41333       ],
41334       [
41335         "Guam",
41336         "gu",
41337         "1671"
41338       ],
41339       [
41340         "Guatemala",
41341         "gt",
41342         "502"
41343       ],
41344       [
41345         "Guernsey",
41346         "gg",
41347         "44",
41348         1
41349       ],
41350       [
41351         "Guinea (Guinée)",
41352         "gn",
41353         "224"
41354       ],
41355       [
41356         "Guinea-Bissau (Guiné Bissau)",
41357         "gw",
41358         "245"
41359       ],
41360       [
41361         "Guyana",
41362         "gy",
41363         "592"
41364       ],
41365       [
41366         "Haiti",
41367         "ht",
41368         "509"
41369       ],
41370       [
41371         "Honduras",
41372         "hn",
41373         "504"
41374       ],
41375       [
41376         "Hong Kong (香港)",
41377         "hk",
41378         "852"
41379       ],
41380       [
41381         "Hungary (Magyarország)",
41382         "hu",
41383         "36"
41384       ],
41385       [
41386         "Iceland (Ísland)",
41387         "is",
41388         "354"
41389       ],
41390       [
41391         "India (भारत)",
41392         "in",
41393         "91"
41394       ],
41395       [
41396         "Indonesia",
41397         "id",
41398         "62"
41399       ],
41400       [
41401         "Iran (‫ایران‬‎)",
41402         "ir",
41403         "98"
41404       ],
41405       [
41406         "Iraq (‫العراق‬‎)",
41407         "iq",
41408         "964"
41409       ],
41410       [
41411         "Ireland",
41412         "ie",
41413         "353"
41414       ],
41415       [
41416         "Isle of Man",
41417         "im",
41418         "44",
41419         2
41420       ],
41421       [
41422         "Israel (‫ישראל‬‎)",
41423         "il",
41424         "972"
41425       ],
41426       [
41427         "Italy (Italia)",
41428         "it",
41429         "39",
41430         0
41431       ],
41432       [
41433         "Jamaica",
41434         "jm",
41435         "1876"
41436       ],
41437       [
41438         "Japan (日本)",
41439         "jp",
41440         "81"
41441       ],
41442       [
41443         "Jersey",
41444         "je",
41445         "44",
41446         3
41447       ],
41448       [
41449         "Jordan (‫الأردن‬‎)",
41450         "jo",
41451         "962"
41452       ],
41453       [
41454         "Kazakhstan (Казахстан)",
41455         "kz",
41456         "7",
41457         1
41458       ],
41459       [
41460         "Kenya",
41461         "ke",
41462         "254"
41463       ],
41464       [
41465         "Kiribati",
41466         "ki",
41467         "686"
41468       ],
41469       [
41470         "Kosovo",
41471         "xk",
41472         "383"
41473       ],
41474       [
41475         "Kuwait (‫الكويت‬‎)",
41476         "kw",
41477         "965"
41478       ],
41479       [
41480         "Kyrgyzstan (Кыргызстан)",
41481         "kg",
41482         "996"
41483       ],
41484       [
41485         "Laos (ລາວ)",
41486         "la",
41487         "856"
41488       ],
41489       [
41490         "Latvia (Latvija)",
41491         "lv",
41492         "371"
41493       ],
41494       [
41495         "Lebanon (‫لبنان‬‎)",
41496         "lb",
41497         "961"
41498       ],
41499       [
41500         "Lesotho",
41501         "ls",
41502         "266"
41503       ],
41504       [
41505         "Liberia",
41506         "lr",
41507         "231"
41508       ],
41509       [
41510         "Libya (‫ليبيا‬‎)",
41511         "ly",
41512         "218"
41513       ],
41514       [
41515         "Liechtenstein",
41516         "li",
41517         "423"
41518       ],
41519       [
41520         "Lithuania (Lietuva)",
41521         "lt",
41522         "370"
41523       ],
41524       [
41525         "Luxembourg",
41526         "lu",
41527         "352"
41528       ],
41529       [
41530         "Macau (澳門)",
41531         "mo",
41532         "853"
41533       ],
41534       [
41535         "Macedonia (FYROM) (Македонија)",
41536         "mk",
41537         "389"
41538       ],
41539       [
41540         "Madagascar (Madagasikara)",
41541         "mg",
41542         "261"
41543       ],
41544       [
41545         "Malawi",
41546         "mw",
41547         "265"
41548       ],
41549       [
41550         "Malaysia",
41551         "my",
41552         "60"
41553       ],
41554       [
41555         "Maldives",
41556         "mv",
41557         "960"
41558       ],
41559       [
41560         "Mali",
41561         "ml",
41562         "223"
41563       ],
41564       [
41565         "Malta",
41566         "mt",
41567         "356"
41568       ],
41569       [
41570         "Marshall Islands",
41571         "mh",
41572         "692"
41573       ],
41574       [
41575         "Martinique",
41576         "mq",
41577         "596"
41578       ],
41579       [
41580         "Mauritania (‫موريتانيا‬‎)",
41581         "mr",
41582         "222"
41583       ],
41584       [
41585         "Mauritius (Moris)",
41586         "mu",
41587         "230"
41588       ],
41589       [
41590         "Mayotte",
41591         "yt",
41592         "262",
41593         1
41594       ],
41595       [
41596         "Mexico (México)",
41597         "mx",
41598         "52"
41599       ],
41600       [
41601         "Micronesia",
41602         "fm",
41603         "691"
41604       ],
41605       [
41606         "Moldova (Republica Moldova)",
41607         "md",
41608         "373"
41609       ],
41610       [
41611         "Monaco",
41612         "mc",
41613         "377"
41614       ],
41615       [
41616         "Mongolia (Монгол)",
41617         "mn",
41618         "976"
41619       ],
41620       [
41621         "Montenegro (Crna Gora)",
41622         "me",
41623         "382"
41624       ],
41625       [
41626         "Montserrat",
41627         "ms",
41628         "1664"
41629       ],
41630       [
41631         "Morocco (‫المغرب‬‎)",
41632         "ma",
41633         "212",
41634         0
41635       ],
41636       [
41637         "Mozambique (Moçambique)",
41638         "mz",
41639         "258"
41640       ],
41641       [
41642         "Myanmar (Burma) (မြန်မာ)",
41643         "mm",
41644         "95"
41645       ],
41646       [
41647         "Namibia (Namibië)",
41648         "na",
41649         "264"
41650       ],
41651       [
41652         "Nauru",
41653         "nr",
41654         "674"
41655       ],
41656       [
41657         "Nepal (नेपाल)",
41658         "np",
41659         "977"
41660       ],
41661       [
41662         "Netherlands (Nederland)",
41663         "nl",
41664         "31"
41665       ],
41666       [
41667         "New Caledonia (Nouvelle-Calédonie)",
41668         "nc",
41669         "687"
41670       ],
41671       [
41672         "New Zealand",
41673         "nz",
41674         "64"
41675       ],
41676       [
41677         "Nicaragua",
41678         "ni",
41679         "505"
41680       ],
41681       [
41682         "Niger (Nijar)",
41683         "ne",
41684         "227"
41685       ],
41686       [
41687         "Nigeria",
41688         "ng",
41689         "234"
41690       ],
41691       [
41692         "Niue",
41693         "nu",
41694         "683"
41695       ],
41696       [
41697         "Norfolk Island",
41698         "nf",
41699         "672"
41700       ],
41701       [
41702         "North Korea (조선 민주주의 인민 공화국)",
41703         "kp",
41704         "850"
41705       ],
41706       [
41707         "Northern Mariana Islands",
41708         "mp",
41709         "1670"
41710       ],
41711       [
41712         "Norway (Norge)",
41713         "no",
41714         "47",
41715         0
41716       ],
41717       [
41718         "Oman (‫عُمان‬‎)",
41719         "om",
41720         "968"
41721       ],
41722       [
41723         "Pakistan (‫پاکستان‬‎)",
41724         "pk",
41725         "92"
41726       ],
41727       [
41728         "Palau",
41729         "pw",
41730         "680"
41731       ],
41732       [
41733         "Palestine (‫فلسطين‬‎)",
41734         "ps",
41735         "970"
41736       ],
41737       [
41738         "Panama (Panamá)",
41739         "pa",
41740         "507"
41741       ],
41742       [
41743         "Papua New Guinea",
41744         "pg",
41745         "675"
41746       ],
41747       [
41748         "Paraguay",
41749         "py",
41750         "595"
41751       ],
41752       [
41753         "Peru (Perú)",
41754         "pe",
41755         "51"
41756       ],
41757       [
41758         "Philippines",
41759         "ph",
41760         "63"
41761       ],
41762       [
41763         "Poland (Polska)",
41764         "pl",
41765         "48"
41766       ],
41767       [
41768         "Portugal",
41769         "pt",
41770         "351"
41771       ],
41772       [
41773         "Puerto Rico",
41774         "pr",
41775         "1",
41776         3,
41777         ["787", "939"]
41778       ],
41779       [
41780         "Qatar (‫قطر‬‎)",
41781         "qa",
41782         "974"
41783       ],
41784       [
41785         "Réunion (La Réunion)",
41786         "re",
41787         "262",
41788         0
41789       ],
41790       [
41791         "Romania (România)",
41792         "ro",
41793         "40"
41794       ],
41795       [
41796         "Russia (Россия)",
41797         "ru",
41798         "7",
41799         0
41800       ],
41801       [
41802         "Rwanda",
41803         "rw",
41804         "250"
41805       ],
41806       [
41807         "Saint Barthélemy",
41808         "bl",
41809         "590",
41810         1
41811       ],
41812       [
41813         "Saint Helena",
41814         "sh",
41815         "290"
41816       ],
41817       [
41818         "Saint Kitts and Nevis",
41819         "kn",
41820         "1869"
41821       ],
41822       [
41823         "Saint Lucia",
41824         "lc",
41825         "1758"
41826       ],
41827       [
41828         "Saint Martin (Saint-Martin (partie française))",
41829         "mf",
41830         "590",
41831         2
41832       ],
41833       [
41834         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41835         "pm",
41836         "508"
41837       ],
41838       [
41839         "Saint Vincent and the Grenadines",
41840         "vc",
41841         "1784"
41842       ],
41843       [
41844         "Samoa",
41845         "ws",
41846         "685"
41847       ],
41848       [
41849         "San Marino",
41850         "sm",
41851         "378"
41852       ],
41853       [
41854         "São Tomé and Príncipe (São Tomé e Príncipe)",
41855         "st",
41856         "239"
41857       ],
41858       [
41859         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41860         "sa",
41861         "966"
41862       ],
41863       [
41864         "Senegal (Sénégal)",
41865         "sn",
41866         "221"
41867       ],
41868       [
41869         "Serbia (Србија)",
41870         "rs",
41871         "381"
41872       ],
41873       [
41874         "Seychelles",
41875         "sc",
41876         "248"
41877       ],
41878       [
41879         "Sierra Leone",
41880         "sl",
41881         "232"
41882       ],
41883       [
41884         "Singapore",
41885         "sg",
41886         "65"
41887       ],
41888       [
41889         "Sint Maarten",
41890         "sx",
41891         "1721"
41892       ],
41893       [
41894         "Slovakia (Slovensko)",
41895         "sk",
41896         "421"
41897       ],
41898       [
41899         "Slovenia (Slovenija)",
41900         "si",
41901         "386"
41902       ],
41903       [
41904         "Solomon Islands",
41905         "sb",
41906         "677"
41907       ],
41908       [
41909         "Somalia (Soomaaliya)",
41910         "so",
41911         "252"
41912       ],
41913       [
41914         "South Africa",
41915         "za",
41916         "27"
41917       ],
41918       [
41919         "South Korea (대한민국)",
41920         "kr",
41921         "82"
41922       ],
41923       [
41924         "South Sudan (‫جنوب السودان‬‎)",
41925         "ss",
41926         "211"
41927       ],
41928       [
41929         "Spain (España)",
41930         "es",
41931         "34"
41932       ],
41933       [
41934         "Sri Lanka (ශ්‍රී ලංකාව)",
41935         "lk",
41936         "94"
41937       ],
41938       [
41939         "Sudan (‫السودان‬‎)",
41940         "sd",
41941         "249"
41942       ],
41943       [
41944         "Suriname",
41945         "sr",
41946         "597"
41947       ],
41948       [
41949         "Svalbard and Jan Mayen",
41950         "sj",
41951         "47",
41952         1
41953       ],
41954       [
41955         "Swaziland",
41956         "sz",
41957         "268"
41958       ],
41959       [
41960         "Sweden (Sverige)",
41961         "se",
41962         "46"
41963       ],
41964       [
41965         "Switzerland (Schweiz)",
41966         "ch",
41967         "41"
41968       ],
41969       [
41970         "Syria (‫سوريا‬‎)",
41971         "sy",
41972         "963"
41973       ],
41974       [
41975         "Taiwan (台灣)",
41976         "tw",
41977         "886"
41978       ],
41979       [
41980         "Tajikistan",
41981         "tj",
41982         "992"
41983       ],
41984       [
41985         "Tanzania",
41986         "tz",
41987         "255"
41988       ],
41989       [
41990         "Thailand (ไทย)",
41991         "th",
41992         "66"
41993       ],
41994       [
41995         "Timor-Leste",
41996         "tl",
41997         "670"
41998       ],
41999       [
42000         "Togo",
42001         "tg",
42002         "228"
42003       ],
42004       [
42005         "Tokelau",
42006         "tk",
42007         "690"
42008       ],
42009       [
42010         "Tonga",
42011         "to",
42012         "676"
42013       ],
42014       [
42015         "Trinidad and Tobago",
42016         "tt",
42017         "1868"
42018       ],
42019       [
42020         "Tunisia (‫تونس‬‎)",
42021         "tn",
42022         "216"
42023       ],
42024       [
42025         "Turkey (Türkiye)",
42026         "tr",
42027         "90"
42028       ],
42029       [
42030         "Turkmenistan",
42031         "tm",
42032         "993"
42033       ],
42034       [
42035         "Turks and Caicos Islands",
42036         "tc",
42037         "1649"
42038       ],
42039       [
42040         "Tuvalu",
42041         "tv",
42042         "688"
42043       ],
42044       [
42045         "U.S. Virgin Islands",
42046         "vi",
42047         "1340"
42048       ],
42049       [
42050         "Uganda",
42051         "ug",
42052         "256"
42053       ],
42054       [
42055         "Ukraine (Україна)",
42056         "ua",
42057         "380"
42058       ],
42059       [
42060         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42061         "ae",
42062         "971"
42063       ],
42064       [
42065         "United Kingdom",
42066         "gb",
42067         "44",
42068         0
42069       ],
42070       [
42071         "United States",
42072         "us",
42073         "1",
42074         0
42075       ],
42076       [
42077         "Uruguay",
42078         "uy",
42079         "598"
42080       ],
42081       [
42082         "Uzbekistan (Oʻzbekiston)",
42083         "uz",
42084         "998"
42085       ],
42086       [
42087         "Vanuatu",
42088         "vu",
42089         "678"
42090       ],
42091       [
42092         "Vatican City (Città del Vaticano)",
42093         "va",
42094         "39",
42095         1
42096       ],
42097       [
42098         "Venezuela",
42099         "ve",
42100         "58"
42101       ],
42102       [
42103         "Vietnam (Việt Nam)",
42104         "vn",
42105         "84"
42106       ],
42107       [
42108         "Wallis and Futuna (Wallis-et-Futuna)",
42109         "wf",
42110         "681"
42111       ],
42112       [
42113         "Western Sahara (‫الصحراء الغربية‬‎)",
42114         "eh",
42115         "212",
42116         1
42117       ],
42118       [
42119         "Yemen (‫اليمن‬‎)",
42120         "ye",
42121         "967"
42122       ],
42123       [
42124         "Zambia",
42125         "zm",
42126         "260"
42127       ],
42128       [
42129         "Zimbabwe",
42130         "zw",
42131         "263"
42132       ],
42133       [
42134         "Åland Islands",
42135         "ax",
42136         "358",
42137         1
42138       ]
42139   ];
42140   
42141   return d;
42142 }/**
42143 *    This script refer to:
42144 *    Title: International Telephone Input
42145 *    Author: Jack O'Connor
42146 *    Code version:  v12.1.12
42147 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42148 **/
42149
42150 /**
42151  * @class Roo.bootstrap.PhoneInput
42152  * @extends Roo.bootstrap.TriggerField
42153  * An input with International dial-code selection
42154  
42155  * @cfg {String} defaultDialCode default '+852'
42156  * @cfg {Array} preferedCountries default []
42157   
42158  * @constructor
42159  * Create a new PhoneInput.
42160  * @param {Object} config Configuration options
42161  */
42162
42163 Roo.bootstrap.PhoneInput = function(config) {
42164     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42165 };
42166
42167 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42168         
42169         listWidth: undefined,
42170         
42171         selectedClass: 'active',
42172         
42173         invalidClass : "has-warning",
42174         
42175         validClass: 'has-success',
42176         
42177         allowed: '0123456789',
42178         
42179         max_length: 15,
42180         
42181         /**
42182          * @cfg {String} defaultDialCode The default dial code when initializing the input
42183          */
42184         defaultDialCode: '+852',
42185         
42186         /**
42187          * @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
42188          */
42189         preferedCountries: false,
42190         
42191         getAutoCreate : function()
42192         {
42193             var data = Roo.bootstrap.PhoneInputData();
42194             var align = this.labelAlign || this.parentLabelAlign();
42195             var id = Roo.id();
42196             
42197             this.allCountries = [];
42198             this.dialCodeMapping = [];
42199             
42200             for (var i = 0; i < data.length; i++) {
42201               var c = data[i];
42202               this.allCountries[i] = {
42203                 name: c[0],
42204                 iso2: c[1],
42205                 dialCode: c[2],
42206                 priority: c[3] || 0,
42207                 areaCodes: c[4] || null
42208               };
42209               this.dialCodeMapping[c[2]] = {
42210                   name: c[0],
42211                   iso2: c[1],
42212                   priority: c[3] || 0,
42213                   areaCodes: c[4] || null
42214               };
42215             }
42216             
42217             var cfg = {
42218                 cls: 'form-group',
42219                 cn: []
42220             };
42221             
42222             var input =  {
42223                 tag: 'input',
42224                 id : id,
42225                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42226                 maxlength: this.max_length,
42227                 cls : 'form-control tel-input',
42228                 autocomplete: 'new-password'
42229             };
42230             
42231             var hiddenInput = {
42232                 tag: 'input',
42233                 type: 'hidden',
42234                 cls: 'hidden-tel-input'
42235             };
42236             
42237             if (this.name) {
42238                 hiddenInput.name = this.name;
42239             }
42240             
42241             if (this.disabled) {
42242                 input.disabled = true;
42243             }
42244             
42245             var flag_container = {
42246                 tag: 'div',
42247                 cls: 'flag-box',
42248                 cn: [
42249                     {
42250                         tag: 'div',
42251                         cls: 'flag'
42252                     },
42253                     {
42254                         tag: 'div',
42255                         cls: 'caret'
42256                     }
42257                 ]
42258             };
42259             
42260             var box = {
42261                 tag: 'div',
42262                 cls: this.hasFeedback ? 'has-feedback' : '',
42263                 cn: [
42264                     hiddenInput,
42265                     input,
42266                     {
42267                         tag: 'input',
42268                         cls: 'dial-code-holder',
42269                         disabled: true
42270                     }
42271                 ]
42272             };
42273             
42274             var container = {
42275                 cls: 'roo-select2-container input-group',
42276                 cn: [
42277                     flag_container,
42278                     box
42279                 ]
42280             };
42281             
42282             if (this.fieldLabel.length) {
42283                 var indicator = {
42284                     tag: 'i',
42285                     tooltip: 'This field is required'
42286                 };
42287                 
42288                 var label = {
42289                     tag: 'label',
42290                     'for':  id,
42291                     cls: 'control-label',
42292                     cn: []
42293                 };
42294                 
42295                 var label_text = {
42296                     tag: 'span',
42297                     html: this.fieldLabel
42298                 };
42299                 
42300                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42301                 label.cn = [
42302                     indicator,
42303                     label_text
42304                 ];
42305                 
42306                 if(this.indicatorpos == 'right') {
42307                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42308                     label.cn = [
42309                         label_text,
42310                         indicator
42311                     ];
42312                 }
42313                 
42314                 if(align == 'left') {
42315                     container = {
42316                         tag: 'div',
42317                         cn: [
42318                             container
42319                         ]
42320                     };
42321                     
42322                     if(this.labelWidth > 12){
42323                         label.style = "width: " + this.labelWidth + 'px';
42324                     }
42325                     if(this.labelWidth < 13 && this.labelmd == 0){
42326                         this.labelmd = this.labelWidth;
42327                     }
42328                     if(this.labellg > 0){
42329                         label.cls += ' col-lg-' + this.labellg;
42330                         input.cls += ' col-lg-' + (12 - this.labellg);
42331                     }
42332                     if(this.labelmd > 0){
42333                         label.cls += ' col-md-' + this.labelmd;
42334                         container.cls += ' col-md-' + (12 - this.labelmd);
42335                     }
42336                     if(this.labelsm > 0){
42337                         label.cls += ' col-sm-' + this.labelsm;
42338                         container.cls += ' col-sm-' + (12 - this.labelsm);
42339                     }
42340                     if(this.labelxs > 0){
42341                         label.cls += ' col-xs-' + this.labelxs;
42342                         container.cls += ' col-xs-' + (12 - this.labelxs);
42343                     }
42344                 }
42345             }
42346             
42347             cfg.cn = [
42348                 label,
42349                 container
42350             ];
42351             
42352             var settings = this;
42353             
42354             ['xs','sm','md','lg'].map(function(size){
42355                 if (settings[size]) {
42356                     cfg.cls += ' col-' + size + '-' + settings[size];
42357                 }
42358             });
42359             
42360             this.store = new Roo.data.Store({
42361                 proxy : new Roo.data.MemoryProxy({}),
42362                 reader : new Roo.data.JsonReader({
42363                     fields : [
42364                         {
42365                             'name' : 'name',
42366                             'type' : 'string'
42367                         },
42368                         {
42369                             'name' : 'iso2',
42370                             'type' : 'string'
42371                         },
42372                         {
42373                             'name' : 'dialCode',
42374                             'type' : 'string'
42375                         },
42376                         {
42377                             'name' : 'priority',
42378                             'type' : 'string'
42379                         },
42380                         {
42381                             'name' : 'areaCodes',
42382                             'type' : 'string'
42383                         }
42384                     ]
42385                 })
42386             });
42387             
42388             if(!this.preferedCountries) {
42389                 this.preferedCountries = [
42390                     'hk',
42391                     'gb',
42392                     'us'
42393                 ];
42394             }
42395             
42396             var p = this.preferedCountries.reverse();
42397             
42398             if(p) {
42399                 for (var i = 0; i < p.length; i++) {
42400                     for (var j = 0; j < this.allCountries.length; j++) {
42401                         if(this.allCountries[j].iso2 == p[i]) {
42402                             var t = this.allCountries[j];
42403                             this.allCountries.splice(j,1);
42404                             this.allCountries.unshift(t);
42405                         }
42406                     } 
42407                 }
42408             }
42409             
42410             this.store.proxy.data = {
42411                 success: true,
42412                 data: this.allCountries
42413             };
42414             
42415             return cfg;
42416         },
42417         
42418         initEvents : function()
42419         {
42420             this.createList();
42421             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42422             
42423             this.indicator = this.indicatorEl();
42424             this.flag = this.flagEl();
42425             this.dialCodeHolder = this.dialCodeHolderEl();
42426             
42427             this.trigger = this.el.select('div.flag-box',true).first();
42428             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42429             
42430             var _this = this;
42431             
42432             (function(){
42433                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42434                 _this.list.setWidth(lw);
42435             }).defer(100);
42436             
42437             this.list.on('mouseover', this.onViewOver, this);
42438             this.list.on('mousemove', this.onViewMove, this);
42439             this.inputEl().on("keyup", this.onKeyUp, this);
42440             this.inputEl().on("keypress", this.onKeyPress, this);
42441             
42442             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42443
42444             this.view = new Roo.View(this.list, this.tpl, {
42445                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42446             });
42447             
42448             this.view.on('click', this.onViewClick, this);
42449             this.setValue(this.defaultDialCode);
42450         },
42451         
42452         onTriggerClick : function(e)
42453         {
42454             Roo.log('trigger click');
42455             if(this.disabled){
42456                 return;
42457             }
42458             
42459             if(this.isExpanded()){
42460                 this.collapse();
42461                 this.hasFocus = false;
42462             }else {
42463                 this.store.load({});
42464                 this.hasFocus = true;
42465                 this.expand();
42466             }
42467         },
42468         
42469         isExpanded : function()
42470         {
42471             return this.list.isVisible();
42472         },
42473         
42474         collapse : function()
42475         {
42476             if(!this.isExpanded()){
42477                 return;
42478             }
42479             this.list.hide();
42480             Roo.get(document).un('mousedown', this.collapseIf, this);
42481             Roo.get(document).un('mousewheel', this.collapseIf, this);
42482             this.fireEvent('collapse', this);
42483             this.validate();
42484         },
42485         
42486         expand : function()
42487         {
42488             Roo.log('expand');
42489
42490             if(this.isExpanded() || !this.hasFocus){
42491                 return;
42492             }
42493             
42494             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42495             this.list.setWidth(lw);
42496             
42497             this.list.show();
42498             this.restrictHeight();
42499             
42500             Roo.get(document).on('mousedown', this.collapseIf, this);
42501             Roo.get(document).on('mousewheel', this.collapseIf, this);
42502             
42503             this.fireEvent('expand', this);
42504         },
42505         
42506         restrictHeight : function()
42507         {
42508             this.list.alignTo(this.inputEl(), this.listAlign);
42509             this.list.alignTo(this.inputEl(), this.listAlign);
42510         },
42511         
42512         onViewOver : function(e, t)
42513         {
42514             if(this.inKeyMode){
42515                 return;
42516             }
42517             var item = this.view.findItemFromChild(t);
42518             
42519             if(item){
42520                 var index = this.view.indexOf(item);
42521                 this.select(index, false);
42522             }
42523         },
42524
42525         // private
42526         onViewClick : function(view, doFocus, el, e)
42527         {
42528             var index = this.view.getSelectedIndexes()[0];
42529             
42530             var r = this.store.getAt(index);
42531             
42532             if(r){
42533                 this.onSelect(r, index);
42534             }
42535             if(doFocus !== false && !this.blockFocus){
42536                 this.inputEl().focus();
42537             }
42538         },
42539         
42540         onViewMove : function(e, t)
42541         {
42542             this.inKeyMode = false;
42543         },
42544         
42545         select : function(index, scrollIntoView)
42546         {
42547             this.selectedIndex = index;
42548             this.view.select(index);
42549             if(scrollIntoView !== false){
42550                 var el = this.view.getNode(index);
42551                 if(el){
42552                     this.list.scrollChildIntoView(el, false);
42553                 }
42554             }
42555         },
42556         
42557         createList : function()
42558         {
42559             this.list = Roo.get(document.body).createChild({
42560                 tag: 'ul',
42561                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42562                 style: 'display:none'
42563             });
42564             
42565             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42566         },
42567         
42568         collapseIf : function(e)
42569         {
42570             var in_combo  = e.within(this.el);
42571             var in_list =  e.within(this.list);
42572             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42573             
42574             if (in_combo || in_list || is_list) {
42575                 return;
42576             }
42577             this.collapse();
42578         },
42579         
42580         onSelect : function(record, index)
42581         {
42582             if(this.fireEvent('beforeselect', this, record, index) !== false){
42583                 
42584                 this.setFlagClass(record.data.iso2);
42585                 this.setDialCode(record.data.dialCode);
42586                 this.hasFocus = false;
42587                 this.collapse();
42588                 this.fireEvent('select', this, record, index);
42589             }
42590         },
42591         
42592         flagEl : function()
42593         {
42594             var flag = this.el.select('div.flag',true).first();
42595             if(!flag){
42596                 return false;
42597             }
42598             return flag;
42599         },
42600         
42601         dialCodeHolderEl : function()
42602         {
42603             var d = this.el.select('input.dial-code-holder',true).first();
42604             if(!d){
42605                 return false;
42606             }
42607             return d;
42608         },
42609         
42610         setDialCode : function(v)
42611         {
42612             this.dialCodeHolder.dom.value = '+'+v;
42613         },
42614         
42615         setFlagClass : function(n)
42616         {
42617             this.flag.dom.className = 'flag '+n;
42618         },
42619         
42620         getValue : function()
42621         {
42622             var v = this.inputEl().getValue();
42623             if(this.dialCodeHolder) {
42624                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42625             }
42626             return v;
42627         },
42628         
42629         setValue : function(v)
42630         {
42631             var d = this.getDialCode(v);
42632             
42633             //invalid dial code
42634             if(v.length == 0 || !d || d.length == 0) {
42635                 if(this.rendered){
42636                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42637                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42638                 }
42639                 return;
42640             }
42641             
42642             //valid dial code
42643             this.setFlagClass(this.dialCodeMapping[d].iso2);
42644             this.setDialCode(d);
42645             this.inputEl().dom.value = v.replace('+'+d,'');
42646             this.hiddenEl().dom.value = this.getValue();
42647             
42648             this.validate();
42649         },
42650         
42651         getDialCode : function(v)
42652         {
42653             v = v ||  '';
42654             
42655             if (v.length == 0) {
42656                 return this.dialCodeHolder.dom.value;
42657             }
42658             
42659             var dialCode = "";
42660             if (v.charAt(0) != "+") {
42661                 return false;
42662             }
42663             var numericChars = "";
42664             for (var i = 1; i < v.length; i++) {
42665               var c = v.charAt(i);
42666               if (!isNaN(c)) {
42667                 numericChars += c;
42668                 if (this.dialCodeMapping[numericChars]) {
42669                   dialCode = v.substr(1, i);
42670                 }
42671                 if (numericChars.length == 4) {
42672                   break;
42673                 }
42674               }
42675             }
42676             return dialCode;
42677         },
42678         
42679         reset : function()
42680         {
42681             this.setValue(this.defaultDialCode);
42682             this.validate();
42683         },
42684         
42685         hiddenEl : function()
42686         {
42687             return this.el.select('input.hidden-tel-input',true).first();
42688         },
42689         
42690         // after setting val
42691         onKeyUp : function(e){
42692             this.setValue(this.getValue());
42693         },
42694         
42695         onKeyPress : function(e){
42696             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42697                 e.stopEvent();
42698             }
42699         }
42700         
42701 });
42702 /**
42703  * @class Roo.bootstrap.MoneyField
42704  * @extends Roo.bootstrap.ComboBox
42705  * Bootstrap MoneyField class
42706  * 
42707  * @constructor
42708  * Create a new MoneyField.
42709  * @param {Object} config Configuration options
42710  */
42711
42712 Roo.bootstrap.MoneyField = function(config) {
42713     
42714     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42715     
42716 };
42717
42718 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42719     
42720     /**
42721      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42722      */
42723     allowDecimals : true,
42724     /**
42725      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42726      */
42727     decimalSeparator : ".",
42728     /**
42729      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42730      */
42731     decimalPrecision : 0,
42732     /**
42733      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42734      */
42735     allowNegative : true,
42736     /**
42737      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42738      */
42739     allowZero: true,
42740     /**
42741      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42742      */
42743     minValue : Number.NEGATIVE_INFINITY,
42744     /**
42745      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42746      */
42747     maxValue : Number.MAX_VALUE,
42748     /**
42749      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42750      */
42751     minText : "The minimum value for this field is {0}",
42752     /**
42753      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42754      */
42755     maxText : "The maximum value for this field is {0}",
42756     /**
42757      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42758      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42759      */
42760     nanText : "{0} is not a valid number",
42761     /**
42762      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42763      */
42764     castInt : true,
42765     /**
42766      * @cfg {String} defaults currency of the MoneyField
42767      * value should be in lkey
42768      */
42769     defaultCurrency : false,
42770     /**
42771      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42772      */
42773     thousandsDelimiter : false,
42774     /**
42775      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42776      */
42777     max_length: false,
42778     
42779     inputlg : 9,
42780     inputmd : 9,
42781     inputsm : 9,
42782     inputxs : 6,
42783     
42784     store : false,
42785     
42786     getAutoCreate : function()
42787     {
42788         var align = this.labelAlign || this.parentLabelAlign();
42789         
42790         var id = Roo.id();
42791
42792         var cfg = {
42793             cls: 'form-group',
42794             cn: []
42795         };
42796
42797         var input =  {
42798             tag: 'input',
42799             id : id,
42800             cls : 'form-control roo-money-amount-input',
42801             autocomplete: 'new-password'
42802         };
42803         
42804         var hiddenInput = {
42805             tag: 'input',
42806             type: 'hidden',
42807             id: Roo.id(),
42808             cls: 'hidden-number-input'
42809         };
42810         
42811         if(this.max_length) {
42812             input.maxlength = this.max_length; 
42813         }
42814         
42815         if (this.name) {
42816             hiddenInput.name = this.name;
42817         }
42818
42819         if (this.disabled) {
42820             input.disabled = true;
42821         }
42822
42823         var clg = 12 - this.inputlg;
42824         var cmd = 12 - this.inputmd;
42825         var csm = 12 - this.inputsm;
42826         var cxs = 12 - this.inputxs;
42827         
42828         var container = {
42829             tag : 'div',
42830             cls : 'row roo-money-field',
42831             cn : [
42832                 {
42833                     tag : 'div',
42834                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42835                     cn : [
42836                         {
42837                             tag : 'div',
42838                             cls: 'roo-select2-container input-group',
42839                             cn: [
42840                                 {
42841                                     tag : 'input',
42842                                     cls : 'form-control roo-money-currency-input',
42843                                     autocomplete: 'new-password',
42844                                     readOnly : 1,
42845                                     name : this.currencyName
42846                                 },
42847                                 {
42848                                     tag :'span',
42849                                     cls : 'input-group-addon',
42850                                     cn : [
42851                                         {
42852                                             tag: 'span',
42853                                             cls: 'caret'
42854                                         }
42855                                     ]
42856                                 }
42857                             ]
42858                         }
42859                     ]
42860                 },
42861                 {
42862                     tag : 'div',
42863                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42864                     cn : [
42865                         {
42866                             tag: 'div',
42867                             cls: this.hasFeedback ? 'has-feedback' : '',
42868                             cn: [
42869                                 input
42870                             ]
42871                         }
42872                     ]
42873                 }
42874             ]
42875             
42876         };
42877         
42878         if (this.fieldLabel.length) {
42879             var indicator = {
42880                 tag: 'i',
42881                 tooltip: 'This field is required'
42882             };
42883
42884             var label = {
42885                 tag: 'label',
42886                 'for':  id,
42887                 cls: 'control-label',
42888                 cn: []
42889             };
42890
42891             var label_text = {
42892                 tag: 'span',
42893                 html: this.fieldLabel
42894             };
42895
42896             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42897             label.cn = [
42898                 indicator,
42899                 label_text
42900             ];
42901
42902             if(this.indicatorpos == 'right') {
42903                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42904                 label.cn = [
42905                     label_text,
42906                     indicator
42907                 ];
42908             }
42909
42910             if(align == 'left') {
42911                 container = {
42912                     tag: 'div',
42913                     cn: [
42914                         container
42915                     ]
42916                 };
42917
42918                 if(this.labelWidth > 12){
42919                     label.style = "width: " + this.labelWidth + 'px';
42920                 }
42921                 if(this.labelWidth < 13 && this.labelmd == 0){
42922                     this.labelmd = this.labelWidth;
42923                 }
42924                 if(this.labellg > 0){
42925                     label.cls += ' col-lg-' + this.labellg;
42926                     input.cls += ' col-lg-' + (12 - this.labellg);
42927                 }
42928                 if(this.labelmd > 0){
42929                     label.cls += ' col-md-' + this.labelmd;
42930                     container.cls += ' col-md-' + (12 - this.labelmd);
42931                 }
42932                 if(this.labelsm > 0){
42933                     label.cls += ' col-sm-' + this.labelsm;
42934                     container.cls += ' col-sm-' + (12 - this.labelsm);
42935                 }
42936                 if(this.labelxs > 0){
42937                     label.cls += ' col-xs-' + this.labelxs;
42938                     container.cls += ' col-xs-' + (12 - this.labelxs);
42939                 }
42940             }
42941         }
42942
42943         cfg.cn = [
42944             label,
42945             container,
42946             hiddenInput
42947         ];
42948         
42949         var settings = this;
42950
42951         ['xs','sm','md','lg'].map(function(size){
42952             if (settings[size]) {
42953                 cfg.cls += ' col-' + size + '-' + settings[size];
42954             }
42955         });
42956         
42957         return cfg;
42958     },
42959     
42960     initEvents : function()
42961     {
42962         this.indicator = this.indicatorEl();
42963         
42964         this.initCurrencyEvent();
42965         
42966         this.initNumberEvent();
42967     },
42968     
42969     initCurrencyEvent : function()
42970     {
42971         if (!this.store) {
42972             throw "can not find store for combo";
42973         }
42974         
42975         this.store = Roo.factory(this.store, Roo.data);
42976         this.store.parent = this;
42977         
42978         this.createList();
42979         
42980         this.triggerEl = this.el.select('.input-group-addon', true).first();
42981         
42982         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42983         
42984         var _this = this;
42985         
42986         (function(){
42987             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42988             _this.list.setWidth(lw);
42989         }).defer(100);
42990         
42991         this.list.on('mouseover', this.onViewOver, this);
42992         this.list.on('mousemove', this.onViewMove, this);
42993         this.list.on('scroll', this.onViewScroll, this);
42994         
42995         if(!this.tpl){
42996             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42997         }
42998         
42999         this.view = new Roo.View(this.list, this.tpl, {
43000             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43001         });
43002         
43003         this.view.on('click', this.onViewClick, this);
43004         
43005         this.store.on('beforeload', this.onBeforeLoad, this);
43006         this.store.on('load', this.onLoad, this);
43007         this.store.on('loadexception', this.onLoadException, this);
43008         
43009         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43010             "up" : function(e){
43011                 this.inKeyMode = true;
43012                 this.selectPrev();
43013             },
43014
43015             "down" : function(e){
43016                 if(!this.isExpanded()){
43017                     this.onTriggerClick();
43018                 }else{
43019                     this.inKeyMode = true;
43020                     this.selectNext();
43021                 }
43022             },
43023
43024             "enter" : function(e){
43025                 this.collapse();
43026                 
43027                 if(this.fireEvent("specialkey", this, e)){
43028                     this.onViewClick(false);
43029                 }
43030                 
43031                 return true;
43032             },
43033
43034             "esc" : function(e){
43035                 this.collapse();
43036             },
43037
43038             "tab" : function(e){
43039                 this.collapse();
43040                 
43041                 if(this.fireEvent("specialkey", this, e)){
43042                     this.onViewClick(false);
43043                 }
43044                 
43045                 return true;
43046             },
43047
43048             scope : this,
43049
43050             doRelay : function(foo, bar, hname){
43051                 if(hname == 'down' || this.scope.isExpanded()){
43052                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43053                 }
43054                 return true;
43055             },
43056
43057             forceKeyDown: true
43058         });
43059         
43060         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43061         
43062     },
43063     
43064     initNumberEvent : function(e)
43065     {
43066         this.inputEl().on("keydown" , this.fireKey,  this);
43067         this.inputEl().on("focus", this.onFocus,  this);
43068         this.inputEl().on("blur", this.onBlur,  this);
43069         
43070         this.inputEl().relayEvent('keyup', this);
43071         
43072         if(this.indicator){
43073             this.indicator.addClass('invisible');
43074         }
43075  
43076         this.originalValue = this.getValue();
43077         
43078         if(this.validationEvent == 'keyup'){
43079             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43080             this.inputEl().on('keyup', this.filterValidation, this);
43081         }
43082         else if(this.validationEvent !== false){
43083             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43084         }
43085         
43086         if(this.selectOnFocus){
43087             this.on("focus", this.preFocus, this);
43088             
43089         }
43090         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43091             this.inputEl().on("keypress", this.filterKeys, this);
43092         } else {
43093             this.inputEl().relayEvent('keypress', this);
43094         }
43095         
43096         var allowed = "0123456789";
43097         
43098         if(this.allowDecimals){
43099             allowed += this.decimalSeparator;
43100         }
43101         
43102         if(this.allowNegative){
43103             allowed += "-";
43104         }
43105         
43106         if(this.thousandsDelimiter) {
43107             allowed += ",";
43108         }
43109         
43110         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43111         
43112         var keyPress = function(e){
43113             
43114             var k = e.getKey();
43115             
43116             var c = e.getCharCode();
43117             
43118             if(
43119                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43120                     allowed.indexOf(String.fromCharCode(c)) === -1
43121             ){
43122                 e.stopEvent();
43123                 return;
43124             }
43125             
43126             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43127                 return;
43128             }
43129             
43130             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43131                 e.stopEvent();
43132             }
43133         };
43134         
43135         this.inputEl().on("keypress", keyPress, this);
43136         
43137     },
43138     
43139     onTriggerClick : function(e)
43140     {   
43141         if(this.disabled){
43142             return;
43143         }
43144         
43145         this.page = 0;
43146         this.loadNext = false;
43147         
43148         if(this.isExpanded()){
43149             this.collapse();
43150             return;
43151         }
43152         
43153         this.hasFocus = true;
43154         
43155         if(this.triggerAction == 'all') {
43156             this.doQuery(this.allQuery, true);
43157             return;
43158         }
43159         
43160         this.doQuery(this.getRawValue());
43161     },
43162     
43163     getCurrency : function()
43164     {   
43165         var v = this.currencyEl().getValue();
43166         
43167         return v;
43168     },
43169     
43170     restrictHeight : function()
43171     {
43172         this.list.alignTo(this.currencyEl(), this.listAlign);
43173         this.list.alignTo(this.currencyEl(), this.listAlign);
43174     },
43175     
43176     onViewClick : function(view, doFocus, el, e)
43177     {
43178         var index = this.view.getSelectedIndexes()[0];
43179         
43180         var r = this.store.getAt(index);
43181         
43182         if(r){
43183             this.onSelect(r, index);
43184         }
43185     },
43186     
43187     onSelect : function(record, index){
43188         
43189         if(this.fireEvent('beforeselect', this, record, index) !== false){
43190         
43191             this.setFromCurrencyData(index > -1 ? record.data : false);
43192             
43193             this.collapse();
43194             
43195             this.fireEvent('select', this, record, index);
43196         }
43197     },
43198     
43199     setFromCurrencyData : function(o)
43200     {
43201         var currency = '';
43202         
43203         this.lastCurrency = o;
43204         
43205         if (this.currencyField) {
43206             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43207         } else {
43208             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43209         }
43210         
43211         this.lastSelectionText = currency;
43212         
43213         //setting default currency
43214         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43215             this.setCurrency(this.defaultCurrency);
43216             return;
43217         }
43218         
43219         this.setCurrency(currency);
43220     },
43221     
43222     setFromData : function(o)
43223     {
43224         var c = {};
43225         
43226         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43227         
43228         this.setFromCurrencyData(c);
43229         
43230         var value = '';
43231         
43232         if (this.name) {
43233             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43234         } else {
43235             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43236         }
43237         
43238         this.setValue(value);
43239         
43240     },
43241     
43242     setCurrency : function(v)
43243     {   
43244         this.currencyValue = v;
43245         
43246         if(this.rendered){
43247             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43248             this.validate();
43249         }
43250     },
43251     
43252     setValue : function(v)
43253     {
43254         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43255         
43256         this.value = v;
43257         
43258         if(this.rendered){
43259             
43260             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43261             
43262             this.inputEl().dom.value = (v == '') ? '' :
43263                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43264             
43265             if(!this.allowZero && v === '0') {
43266                 this.hiddenEl().dom.value = '';
43267                 this.inputEl().dom.value = '';
43268             }
43269             
43270             this.validate();
43271         }
43272     },
43273     
43274     getRawValue : function()
43275     {
43276         var v = this.inputEl().getValue();
43277         
43278         return v;
43279     },
43280     
43281     getValue : function()
43282     {
43283         return this.fixPrecision(this.parseValue(this.getRawValue()));
43284     },
43285     
43286     parseValue : function(value)
43287     {
43288         if(this.thousandsDelimiter) {
43289             value += "";
43290             r = new RegExp(",", "g");
43291             value = value.replace(r, "");
43292         }
43293         
43294         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43295         return isNaN(value) ? '' : value;
43296         
43297     },
43298     
43299     fixPrecision : function(value)
43300     {
43301         if(this.thousandsDelimiter) {
43302             value += "";
43303             r = new RegExp(",", "g");
43304             value = value.replace(r, "");
43305         }
43306         
43307         var nan = isNaN(value);
43308         
43309         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43310             return nan ? '' : value;
43311         }
43312         return parseFloat(value).toFixed(this.decimalPrecision);
43313     },
43314     
43315     decimalPrecisionFcn : function(v)
43316     {
43317         return Math.floor(v);
43318     },
43319     
43320     validateValue : function(value)
43321     {
43322         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43323             return false;
43324         }
43325         
43326         var num = this.parseValue(value);
43327         
43328         if(isNaN(num)){
43329             this.markInvalid(String.format(this.nanText, value));
43330             return false;
43331         }
43332         
43333         if(num < this.minValue){
43334             this.markInvalid(String.format(this.minText, this.minValue));
43335             return false;
43336         }
43337         
43338         if(num > this.maxValue){
43339             this.markInvalid(String.format(this.maxText, this.maxValue));
43340             return false;
43341         }
43342         
43343         return true;
43344     },
43345     
43346     validate : function()
43347     {
43348         if(this.disabled || this.allowBlank){
43349             this.markValid();
43350             return true;
43351         }
43352         
43353         var currency = this.getCurrency();
43354         
43355         if(this.validateValue(this.getRawValue()) && currency.length){
43356             this.markValid();
43357             return true;
43358         }
43359         
43360         this.markInvalid();
43361         return false;
43362     },
43363     
43364     getName: function()
43365     {
43366         return this.name;
43367     },
43368     
43369     beforeBlur : function()
43370     {
43371         if(!this.castInt){
43372             return;
43373         }
43374         
43375         var v = this.parseValue(this.getRawValue());
43376         
43377         if(v || v == 0){
43378             this.setValue(v);
43379         }
43380     },
43381     
43382     onBlur : function()
43383     {
43384         this.beforeBlur();
43385         
43386         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43387             //this.el.removeClass(this.focusClass);
43388         }
43389         
43390         this.hasFocus = false;
43391         
43392         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43393             this.validate();
43394         }
43395         
43396         var v = this.getValue();
43397         
43398         if(String(v) !== String(this.startValue)){
43399             this.fireEvent('change', this, v, this.startValue);
43400         }
43401         
43402         this.fireEvent("blur", this);
43403     },
43404     
43405     inputEl : function()
43406     {
43407         return this.el.select('.roo-money-amount-input', true).first();
43408     },
43409     
43410     currencyEl : function()
43411     {
43412         return this.el.select('.roo-money-currency-input', true).first();
43413     },
43414     
43415     hiddenEl : function()
43416     {
43417         return this.el.select('input.hidden-number-input',true).first();
43418     }
43419     
43420 });/**
43421  * @class Roo.bootstrap.BezierSignature
43422  * @extends Roo.bootstrap.Component
43423  * Bootstrap BezierSignature class
43424  * This script refer to:
43425  *    Title: Signature Pad
43426  *    Author: szimek
43427  *    Availability: https://github.com/szimek/signature_pad
43428  *
43429  * @constructor
43430  * Create a new BezierSignature
43431  * @param {Object} config The config object
43432  */
43433
43434 Roo.bootstrap.BezierSignature = function(config){
43435     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43436     this.addEvents({
43437         "resize" : true
43438     });
43439 };
43440
43441 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43442 {
43443      
43444     curve_data: [],
43445     
43446     is_empty: true,
43447     
43448     mouse_btn_down: true,
43449     
43450     /**
43451      * @cfg {int} canvas height
43452      */
43453     canvas_height: '200px',
43454     
43455     /**
43456      * @cfg {float|function} Radius of a single dot.
43457      */ 
43458     dot_size: false,
43459     
43460     /**
43461      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43462      */
43463     min_width: 0.5,
43464     
43465     /**
43466      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43467      */
43468     max_width: 2.5,
43469     
43470     /**
43471      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43472      */
43473     throttle: 16,
43474     
43475     /**
43476      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43477      */
43478     min_distance: 5,
43479     
43480     /**
43481      * @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.
43482      */
43483     bg_color: 'rgba(0, 0, 0, 0)',
43484     
43485     /**
43486      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43487      */
43488     dot_color: 'black',
43489     
43490     /**
43491      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43492      */ 
43493     velocity_filter_weight: 0.7,
43494     
43495     /**
43496      * @cfg {function} Callback when stroke begin. 
43497      */
43498     onBegin: false,
43499     
43500     /**
43501      * @cfg {function} Callback when stroke end.
43502      */
43503     onEnd: false,
43504     
43505     getAutoCreate : function()
43506     {
43507         var cls = 'roo-signature column';
43508         
43509         if(this.cls){
43510             cls += ' ' + this.cls;
43511         }
43512         
43513         var col_sizes = [
43514             'lg',
43515             'md',
43516             'sm',
43517             'xs'
43518         ];
43519         
43520         for(var i = 0; i < col_sizes.length; i++) {
43521             if(this[col_sizes[i]]) {
43522                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43523             }
43524         }
43525         
43526         var cfg = {
43527             tag: 'div',
43528             cls: cls,
43529             cn: [
43530                 {
43531                     tag: 'div',
43532                     cls: 'roo-signature-body',
43533                     cn: [
43534                         {
43535                             tag: 'canvas',
43536                             cls: 'roo-signature-body-canvas',
43537                             height: this.canvas_height,
43538                             width: this.canvas_width
43539                         }
43540                     ]
43541                 },
43542                 {
43543                     tag: 'input',
43544                     type: 'file',
43545                     style: 'display: none'
43546                 }
43547             ]
43548         };
43549         
43550         return cfg;
43551     },
43552     
43553     initEvents: function() 
43554     {
43555         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43556         
43557         var canvas = this.canvasEl();
43558         
43559         // mouse && touch event swapping...
43560         canvas.dom.style.touchAction = 'none';
43561         canvas.dom.style.msTouchAction = 'none';
43562         
43563         this.mouse_btn_down = false;
43564         canvas.on('mousedown', this._handleMouseDown, this);
43565         canvas.on('mousemove', this._handleMouseMove, this);
43566         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43567         
43568         if (window.PointerEvent) {
43569             canvas.on('pointerdown', this._handleMouseDown, this);
43570             canvas.on('pointermove', this._handleMouseMove, this);
43571             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43572         }
43573         
43574         if ('ontouchstart' in window) {
43575             canvas.on('touchstart', this._handleTouchStart, this);
43576             canvas.on('touchmove', this._handleTouchMove, this);
43577             canvas.on('touchend', this._handleTouchEnd, this);
43578         }
43579         
43580         Roo.EventManager.onWindowResize(this.resize, this, true);
43581         
43582         // file input event
43583         this.fileEl().on('change', this.uploadImage, this);
43584         
43585         this.clear();
43586         
43587         this.resize();
43588     },
43589     
43590     resize: function(){
43591         
43592         var canvas = this.canvasEl().dom;
43593         var ctx = this.canvasElCtx();
43594         var img_data = false;
43595         
43596         if(canvas.width > 0) {
43597             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43598         }
43599         // setting canvas width will clean img data
43600         canvas.width = 0;
43601         
43602         var style = window.getComputedStyle ? 
43603             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43604             
43605         var padding_left = parseInt(style.paddingLeft) || 0;
43606         var padding_right = parseInt(style.paddingRight) || 0;
43607         
43608         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43609         
43610         if(img_data) {
43611             ctx.putImageData(img_data, 0, 0);
43612         }
43613     },
43614     
43615     _handleMouseDown: function(e)
43616     {
43617         if (e.browserEvent.which === 1) {
43618             this.mouse_btn_down = true;
43619             this.strokeBegin(e);
43620         }
43621     },
43622     
43623     _handleMouseMove: function (e)
43624     {
43625         if (this.mouse_btn_down) {
43626             this.strokeMoveUpdate(e);
43627         }
43628     },
43629     
43630     _handleMouseUp: function (e)
43631     {
43632         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43633             this.mouse_btn_down = false;
43634             this.strokeEnd(e);
43635         }
43636     },
43637     
43638     _handleTouchStart: function (e) {
43639         
43640         e.preventDefault();
43641         if (e.browserEvent.targetTouches.length === 1) {
43642             // var touch = e.browserEvent.changedTouches[0];
43643             // this.strokeBegin(touch);
43644             
43645              this.strokeBegin(e); // assume e catching the correct xy...
43646         }
43647     },
43648     
43649     _handleTouchMove: function (e) {
43650         e.preventDefault();
43651         // var touch = event.targetTouches[0];
43652         // _this._strokeMoveUpdate(touch);
43653         this.strokeMoveUpdate(e);
43654     },
43655     
43656     _handleTouchEnd: function (e) {
43657         var wasCanvasTouched = e.target === this.canvasEl().dom;
43658         if (wasCanvasTouched) {
43659             e.preventDefault();
43660             // var touch = event.changedTouches[0];
43661             // _this._strokeEnd(touch);
43662             this.strokeEnd(e);
43663         }
43664     },
43665     
43666     reset: function () {
43667         this._lastPoints = [];
43668         this._lastVelocity = 0;
43669         this._lastWidth = (this.min_width + this.max_width) / 2;
43670         this.canvasElCtx().fillStyle = this.dot_color;
43671     },
43672     
43673     strokeMoveUpdate: function(e)
43674     {
43675         this.strokeUpdate(e);
43676         
43677         if (this.throttle) {
43678             this.throttleStroke(this.strokeUpdate, this.throttle);
43679         }
43680         else {
43681             this.strokeUpdate(e);
43682         }
43683     },
43684     
43685     strokeBegin: function(e)
43686     {
43687         var newPointGroup = {
43688             color: this.dot_color,
43689             points: []
43690         };
43691         
43692         if (typeof this.onBegin === 'function') {
43693             this.onBegin(e);
43694         }
43695         
43696         this.curve_data.push(newPointGroup);
43697         this.reset();
43698         this.strokeUpdate(e);
43699     },
43700     
43701     strokeUpdate: function(e)
43702     {
43703         var rect = this.canvasEl().dom.getBoundingClientRect();
43704         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43705         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43706         var lastPoints = lastPointGroup.points;
43707         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43708         var isLastPointTooClose = lastPoint
43709             ? point.distanceTo(lastPoint) <= this.min_distance
43710             : false;
43711         var color = lastPointGroup.color;
43712         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43713             var curve = this.addPoint(point);
43714             if (!lastPoint) {
43715                 this.drawDot({color: color, point: point});
43716             }
43717             else if (curve) {
43718                 this.drawCurve({color: color, curve: curve});
43719             }
43720             lastPoints.push({
43721                 time: point.time,
43722                 x: point.x,
43723                 y: point.y
43724             });
43725         }
43726     },
43727     
43728     strokeEnd: function(e)
43729     {
43730         this.strokeUpdate(e);
43731         if (typeof this.onEnd === 'function') {
43732             this.onEnd(e);
43733         }
43734     },
43735     
43736     addPoint:  function (point) {
43737         var _lastPoints = this._lastPoints;
43738         _lastPoints.push(point);
43739         if (_lastPoints.length > 2) {
43740             if (_lastPoints.length === 3) {
43741                 _lastPoints.unshift(_lastPoints[0]);
43742             }
43743             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43744             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43745             _lastPoints.shift();
43746             return curve;
43747         }
43748         return null;
43749     },
43750     
43751     calculateCurveWidths: function (startPoint, endPoint) {
43752         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43753             (1 - this.velocity_filter_weight) * this._lastVelocity;
43754
43755         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43756         var widths = {
43757             end: newWidth,
43758             start: this._lastWidth
43759         };
43760         
43761         this._lastVelocity = velocity;
43762         this._lastWidth = newWidth;
43763         return widths;
43764     },
43765     
43766     drawDot: function (_a) {
43767         var color = _a.color, point = _a.point;
43768         var ctx = this.canvasElCtx();
43769         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43770         ctx.beginPath();
43771         this.drawCurveSegment(point.x, point.y, width);
43772         ctx.closePath();
43773         ctx.fillStyle = color;
43774         ctx.fill();
43775     },
43776     
43777     drawCurve: function (_a) {
43778         var color = _a.color, curve = _a.curve;
43779         var ctx = this.canvasElCtx();
43780         var widthDelta = curve.endWidth - curve.startWidth;
43781         var drawSteps = Math.floor(curve.length()) * 2;
43782         ctx.beginPath();
43783         ctx.fillStyle = color;
43784         for (var i = 0; i < drawSteps; i += 1) {
43785         var t = i / drawSteps;
43786         var tt = t * t;
43787         var ttt = tt * t;
43788         var u = 1 - t;
43789         var uu = u * u;
43790         var uuu = uu * u;
43791         var x = uuu * curve.startPoint.x;
43792         x += 3 * uu * t * curve.control1.x;
43793         x += 3 * u * tt * curve.control2.x;
43794         x += ttt * curve.endPoint.x;
43795         var y = uuu * curve.startPoint.y;
43796         y += 3 * uu * t * curve.control1.y;
43797         y += 3 * u * tt * curve.control2.y;
43798         y += ttt * curve.endPoint.y;
43799         var width = curve.startWidth + ttt * widthDelta;
43800         this.drawCurveSegment(x, y, width);
43801         }
43802         ctx.closePath();
43803         ctx.fill();
43804     },
43805     
43806     drawCurveSegment: function (x, y, width) {
43807         var ctx = this.canvasElCtx();
43808         ctx.moveTo(x, y);
43809         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43810         this.is_empty = false;
43811     },
43812     
43813     clear: function()
43814     {
43815         var ctx = this.canvasElCtx();
43816         var canvas = this.canvasEl().dom;
43817         ctx.fillStyle = this.bg_color;
43818         ctx.clearRect(0, 0, canvas.width, canvas.height);
43819         ctx.fillRect(0, 0, canvas.width, canvas.height);
43820         this.curve_data = [];
43821         this.reset();
43822         this.is_empty = true;
43823     },
43824     
43825     fileEl: function()
43826     {
43827         return  this.el.select('input',true).first();
43828     },
43829     
43830     canvasEl: function()
43831     {
43832         return this.el.select('canvas',true).first();
43833     },
43834     
43835     canvasElCtx: function()
43836     {
43837         return this.el.select('canvas',true).first().dom.getContext('2d');
43838     },
43839     
43840     getImage: function(type)
43841     {
43842         if(this.is_empty) {
43843             return false;
43844         }
43845         
43846         // encryption ?
43847         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43848     },
43849     
43850     drawFromImage: function(img_src)
43851     {
43852         var img = new Image();
43853         
43854         img.onload = function(){
43855             this.canvasElCtx().drawImage(img, 0, 0);
43856         }.bind(this);
43857         
43858         img.src = img_src;
43859         
43860         this.is_empty = false;
43861     },
43862     
43863     selectImage: function()
43864     {
43865         this.fileEl().dom.click();
43866     },
43867     
43868     uploadImage: function(e)
43869     {
43870         var reader = new FileReader();
43871         
43872         reader.onload = function(e){
43873             var img = new Image();
43874             img.onload = function(){
43875                 this.reset();
43876                 this.canvasElCtx().drawImage(img, 0, 0);
43877             }.bind(this);
43878             img.src = e.target.result;
43879         }.bind(this);
43880         
43881         reader.readAsDataURL(e.target.files[0]);
43882     },
43883     
43884     // Bezier Point Constructor
43885     Point: (function () {
43886         function Point(x, y, time) {
43887             this.x = x;
43888             this.y = y;
43889             this.time = time || Date.now();
43890         }
43891         Point.prototype.distanceTo = function (start) {
43892             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43893         };
43894         Point.prototype.equals = function (other) {
43895             return this.x === other.x && this.y === other.y && this.time === other.time;
43896         };
43897         Point.prototype.velocityFrom = function (start) {
43898             return this.time !== start.time
43899             ? this.distanceTo(start) / (this.time - start.time)
43900             : 0;
43901         };
43902         return Point;
43903     }()),
43904     
43905     
43906     // Bezier Constructor
43907     Bezier: (function () {
43908         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43909             this.startPoint = startPoint;
43910             this.control2 = control2;
43911             this.control1 = control1;
43912             this.endPoint = endPoint;
43913             this.startWidth = startWidth;
43914             this.endWidth = endWidth;
43915         }
43916         Bezier.fromPoints = function (points, widths, scope) {
43917             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43918             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43919             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43920         };
43921         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43922             var dx1 = s1.x - s2.x;
43923             var dy1 = s1.y - s2.y;
43924             var dx2 = s2.x - s3.x;
43925             var dy2 = s2.y - s3.y;
43926             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43927             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43928             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43929             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43930             var dxm = m1.x - m2.x;
43931             var dym = m1.y - m2.y;
43932             var k = l2 / (l1 + l2);
43933             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43934             var tx = s2.x - cm.x;
43935             var ty = s2.y - cm.y;
43936             return {
43937                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43938                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43939             };
43940         };
43941         Bezier.prototype.length = function () {
43942             var steps = 10;
43943             var length = 0;
43944             var px;
43945             var py;
43946             for (var i = 0; i <= steps; i += 1) {
43947                 var t = i / steps;
43948                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43949                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43950                 if (i > 0) {
43951                     var xdiff = cx - px;
43952                     var ydiff = cy - py;
43953                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43954                 }
43955                 px = cx;
43956                 py = cy;
43957             }
43958             return length;
43959         };
43960         Bezier.prototype.point = function (t, start, c1, c2, end) {
43961             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43962             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43963             + (3.0 * c2 * (1.0 - t) * t * t)
43964             + (end * t * t * t);
43965         };
43966         return Bezier;
43967     }()),
43968     
43969     throttleStroke: function(fn, wait) {
43970       if (wait === void 0) { wait = 250; }
43971       var previous = 0;
43972       var timeout = null;
43973       var result;
43974       var storedContext;
43975       var storedArgs;
43976       var later = function () {
43977           previous = Date.now();
43978           timeout = null;
43979           result = fn.apply(storedContext, storedArgs);
43980           if (!timeout) {
43981               storedContext = null;
43982               storedArgs = [];
43983           }
43984       };
43985       return function wrapper() {
43986           var args = [];
43987           for (var _i = 0; _i < arguments.length; _i++) {
43988               args[_i] = arguments[_i];
43989           }
43990           var now = Date.now();
43991           var remaining = wait - (now - previous);
43992           storedContext = this;
43993           storedArgs = args;
43994           if (remaining <= 0 || remaining > wait) {
43995               if (timeout) {
43996                   clearTimeout(timeout);
43997                   timeout = null;
43998               }
43999               previous = now;
44000               result = fn.apply(storedContext, storedArgs);
44001               if (!timeout) {
44002                   storedContext = null;
44003                   storedArgs = [];
44004               }
44005           }
44006           else if (!timeout) {
44007               timeout = window.setTimeout(later, remaining);
44008           }
44009           return result;
44010       };
44011   }
44012   
44013 });
44014
44015  
44016
44017