revet delay on chrome for window resize
[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 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<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>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <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>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         //Roo.log('item on click ');
4202         
4203         if(this.href === false || this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 // we force children not to montor widnow resize  - as we do that for them.
4422                 items[i].monitorWindowResize = false;
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630         // any layout/border etc.. resize..
4631         (function () {
4632             this.items.forEach( function(e) {
4633                 e.layout ? e.layout() : false;
4634
4635             });
4636         }).defer(100,this);
4637         
4638     },
4639
4640     show : function() {
4641
4642         if (!this.rendered) {
4643             this.render();
4644         }
4645         this.toggleHeaderInput(false);
4646         //this.el.setStyle('display', 'block');
4647         this.el.removeClass('hideing');
4648         this.el.dom.style.display='block';
4649         
4650         Roo.get(document.body).addClass('modal-open');
4651  
4652         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4653             
4654             (function(){
4655                 this.el.addClass('show');
4656                 this.el.addClass('in');
4657             }).defer(50, this);
4658         }else{
4659             this.el.addClass('show');
4660             this.el.addClass('in');
4661         }
4662
4663         // not sure how we can show data in here..
4664         //if (this.tmpl) {
4665         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4666         //}
4667
4668         Roo.get(document.body).addClass("x-body-masked");
4669         
4670         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4671         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672         this.maskEl.dom.style.display = 'block';
4673         this.maskEl.addClass('show');
4674         
4675         
4676         this.resize();
4677         
4678         this.fireEvent('show', this);
4679
4680         // set zindex here - otherwise it appears to be ignored...
4681         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4682         
4683         
4684         // this is for children that are... layout.Border 
4685         (function () {
4686             this.items.forEach( function(e) {
4687                 e.layout ? e.layout() : false;
4688
4689             });
4690         }).defer(100,this);
4691
4692     },
4693     hide : function()
4694     {
4695         if(this.fireEvent("beforehide", this) !== false){
4696             
4697             this.maskEl.removeClass('show');
4698             
4699             this.maskEl.dom.style.display = '';
4700             Roo.get(document.body).removeClass("x-body-masked");
4701             this.el.removeClass('in');
4702             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4703
4704             if(this.animate){ // why
4705                 this.el.addClass('hideing');
4706                 this.el.removeClass('show');
4707                 (function(){
4708                     if (!this.el.hasClass('hideing')) {
4709                         return; // it's been shown again...
4710                     }
4711                     
4712                     this.el.dom.style.display='';
4713
4714                     Roo.get(document.body).removeClass('modal-open');
4715                     this.el.removeClass('hideing');
4716                 }).defer(150,this);
4717                 
4718             }else{
4719                 this.el.removeClass('show');
4720                 this.el.dom.style.display='';
4721                 Roo.get(document.body).removeClass('modal-open');
4722
4723             }
4724             this.fireEvent('hide', this);
4725         }
4726     },
4727     isVisible : function()
4728     {
4729         
4730         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4731         
4732     },
4733
4734     addButton : function(str, cb)
4735     {
4736
4737
4738         var b = Roo.apply({}, { html : str } );
4739         b.xns = b.xns || Roo.bootstrap;
4740         b.xtype = b.xtype || 'Button';
4741         if (typeof(b.listeners) == 'undefined') {
4742             b.listeners = { click : cb.createDelegate(this)  };
4743         }
4744
4745         var btn = Roo.factory(b);
4746
4747         btn.render(this.getButtonContainer());
4748
4749         return btn;
4750
4751     },
4752
4753     setDefaultButton : function(btn)
4754     {
4755         //this.el.select('.modal-footer').()
4756     },
4757
4758     resizeTo: function(w,h)
4759     {
4760         this.dialogEl.setWidth(w);
4761         
4762         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4763
4764         this.bodyEl.setHeight(h - diff);
4765         
4766         this.fireEvent('resize', this);
4767     },
4768     
4769     setContentSize  : function(w, h)
4770     {
4771
4772     },
4773     onButtonClick: function(btn,e)
4774     {
4775         //Roo.log([a,b,c]);
4776         this.fireEvent('btnclick', btn.name, e);
4777     },
4778      /**
4779      * Set the title of the Dialog
4780      * @param {String} str new Title
4781      */
4782     setTitle: function(str) {
4783         this.titleEl.dom.innerHTML = str;
4784         this.title = str;
4785     },
4786     /**
4787      * Set the body of the Dialog
4788      * @param {String} str new Title
4789      */
4790     setBody: function(str) {
4791         this.bodyEl.dom.innerHTML = str;
4792     },
4793     /**
4794      * Set the body of the Dialog using the template
4795      * @param {Obj} data - apply this data to the template and replace the body contents.
4796      */
4797     applyBody: function(obj)
4798     {
4799         if (!this.tmpl) {
4800             Roo.log("Error - using apply Body without a template");
4801             //code
4802         }
4803         this.tmpl.overwrite(this.bodyEl, obj);
4804     },
4805     
4806     getChildHeight : function(child_nodes)
4807     {
4808         if(
4809             !child_nodes ||
4810             child_nodes.length == 0
4811         ) {
4812             return 0;
4813         }
4814         
4815         var child_height = 0;
4816         
4817         for(var i = 0; i < child_nodes.length; i++) {
4818             
4819             /*
4820             * for modal with tabs...
4821             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4822                 
4823                 var layout_childs = child_nodes[i].childNodes;
4824                 
4825                 for(var j = 0; j < layout_childs.length; j++) {
4826                     
4827                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4828                         
4829                         var layout_body_childs = layout_childs[j].childNodes;
4830                         
4831                         for(var k = 0; k < layout_body_childs.length; k++) {
4832                             
4833                             if(layout_body_childs[k].classList.contains('navbar')) {
4834                                 child_height += layout_body_childs[k].offsetHeight;
4835                                 continue;
4836                             }
4837                             
4838                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4839                                 
4840                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4841                                 
4842                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4843                                     
4844                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4845                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4846                                         continue;
4847                                     }
4848                                     
4849                                 }
4850                                 
4851                             }
4852                             
4853                         }
4854                     }
4855                 }
4856                 continue;
4857             }
4858             */
4859             
4860             child_height += child_nodes[i].offsetHeight;
4861             // Roo.log(child_nodes[i].offsetHeight);
4862         }
4863         
4864         return child_height;
4865     },
4866     toggleHeaderInput : function(is_edit)
4867     {
4868         if (!this.editableTitle) {
4869             return; // not editable.
4870         }
4871         if (is_edit && this.is_header_editing) {
4872             return; // already editing..
4873         }
4874         if (is_edit) {
4875     
4876             this.headerEditEl.dom.value = this.title;
4877             this.headerEditEl.removeClass('d-none');
4878             this.headerEditEl.dom.focus();
4879             this.titleEl.addClass('d-none');
4880             
4881             this.is_header_editing = true;
4882             return
4883         }
4884         // flip back to not editing.
4885         this.title = this.headerEditEl.dom.value;
4886         this.headerEditEl.addClass('d-none');
4887         this.titleEl.removeClass('d-none');
4888         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4889         this.is_header_editing = false;
4890         this.fireEvent('titlechanged', this, this.title);
4891     
4892             
4893         
4894     }
4895
4896 });
4897
4898
4899 Roo.apply(Roo.bootstrap.Modal,  {
4900     /**
4901          * Button config that displays a single OK button
4902          * @type Object
4903          */
4904         OK :  [{
4905             name : 'ok',
4906             weight : 'primary',
4907             html : 'OK'
4908         }],
4909         /**
4910          * Button config that displays Yes and No buttons
4911          * @type Object
4912          */
4913         YESNO : [
4914             {
4915                 name  : 'no',
4916                 html : 'No'
4917             },
4918             {
4919                 name  :'yes',
4920                 weight : 'primary',
4921                 html : 'Yes'
4922             }
4923         ],
4924
4925         /**
4926          * Button config that displays OK and Cancel buttons
4927          * @type Object
4928          */
4929         OKCANCEL : [
4930             {
4931                name : 'cancel',
4932                 html : 'Cancel'
4933             },
4934             {
4935                 name : 'ok',
4936                 weight : 'primary',
4937                 html : 'OK'
4938             }
4939         ],
4940         /**
4941          * Button config that displays Yes, No and Cancel buttons
4942          * @type Object
4943          */
4944         YESNOCANCEL : [
4945             {
4946                 name : 'yes',
4947                 weight : 'primary',
4948                 html : 'Yes'
4949             },
4950             {
4951                 name : 'no',
4952                 html : 'No'
4953             },
4954             {
4955                 name : 'cancel',
4956                 html : 'Cancel'
4957             }
4958         ],
4959         
4960         zIndex : 10001
4961 });
4962
4963 /*
4964  * - LGPL
4965  *
4966  * messagebox - can be used as a replace
4967  * 
4968  */
4969 /**
4970  * @class Roo.MessageBox
4971  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4972  * Example usage:
4973  *<pre><code>
4974 // Basic alert:
4975 Roo.Msg.alert('Status', 'Changes saved successfully.');
4976
4977 // Prompt for user data:
4978 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4979     if (btn == 'ok'){
4980         // process text value...
4981     }
4982 });
4983
4984 // Show a dialog using config options:
4985 Roo.Msg.show({
4986    title:'Save Changes?',
4987    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4988    buttons: Roo.Msg.YESNOCANCEL,
4989    fn: processResult,
4990    animEl: 'elId'
4991 });
4992 </code></pre>
4993  * @static
4994  */
4995 Roo.bootstrap.MessageBox = function(){
4996     var dlg, opt, mask, waitTimer;
4997     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4998     var buttons, activeTextEl, bwidth;
4999
5000     
5001     // private
5002     var handleButton = function(button){
5003         dlg.hide();
5004         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5005     };
5006
5007     // private
5008     var handleHide = function(){
5009         if(opt && opt.cls){
5010             dlg.el.removeClass(opt.cls);
5011         }
5012         //if(waitTimer){
5013         //    Roo.TaskMgr.stop(waitTimer);
5014         //    waitTimer = null;
5015         //}
5016     };
5017
5018     // private
5019     var updateButtons = function(b){
5020         var width = 0;
5021         if(!b){
5022             buttons["ok"].hide();
5023             buttons["cancel"].hide();
5024             buttons["yes"].hide();
5025             buttons["no"].hide();
5026             dlg.footerEl.hide();
5027             
5028             return width;
5029         }
5030         dlg.footerEl.show();
5031         for(var k in buttons){
5032             if(typeof buttons[k] != "function"){
5033                 if(b[k]){
5034                     buttons[k].show();
5035                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5036                     width += buttons[k].el.getWidth()+15;
5037                 }else{
5038                     buttons[k].hide();
5039                 }
5040             }
5041         }
5042         return width;
5043     };
5044
5045     // private
5046     var handleEsc = function(d, k, e){
5047         if(opt && opt.closable !== false){
5048             dlg.hide();
5049         }
5050         if(e){
5051             e.stopEvent();
5052         }
5053     };
5054
5055     return {
5056         /**
5057          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5058          * @return {Roo.BasicDialog} The BasicDialog element
5059          */
5060         getDialog : function(){
5061            if(!dlg){
5062                 dlg = new Roo.bootstrap.Modal( {
5063                     //draggable: true,
5064                     //resizable:false,
5065                     //constraintoviewport:false,
5066                     //fixedcenter:true,
5067                     //collapsible : false,
5068                     //shim:true,
5069                     //modal: true,
5070                 //    width: 'auto',
5071                   //  height:100,
5072                     //buttonAlign:"center",
5073                     closeClick : function(){
5074                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5075                             handleButton("no");
5076                         }else{
5077                             handleButton("cancel");
5078                         }
5079                     }
5080                 });
5081                 dlg.render();
5082                 dlg.on("hide", handleHide);
5083                 mask = dlg.mask;
5084                 //dlg.addKeyListener(27, handleEsc);
5085                 buttons = {};
5086                 this.buttons = buttons;
5087                 var bt = this.buttonText;
5088                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5089                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5090                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5091                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5092                 //Roo.log(buttons);
5093                 bodyEl = dlg.bodyEl.createChild({
5094
5095                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5096                         '<textarea class="roo-mb-textarea"></textarea>' +
5097                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5098                 });
5099                 msgEl = bodyEl.dom.firstChild;
5100                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5101                 textboxEl.enableDisplayMode();
5102                 textboxEl.addKeyListener([10,13], function(){
5103                     if(dlg.isVisible() && opt && opt.buttons){
5104                         if(opt.buttons.ok){
5105                             handleButton("ok");
5106                         }else if(opt.buttons.yes){
5107                             handleButton("yes");
5108                         }
5109                     }
5110                 });
5111                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5112                 textareaEl.enableDisplayMode();
5113                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5114                 progressEl.enableDisplayMode();
5115                 
5116                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5117                 var pf = progressEl.dom.firstChild;
5118                 if (pf) {
5119                     pp = Roo.get(pf.firstChild);
5120                     pp.setHeight(pf.offsetHeight);
5121                 }
5122                 
5123             }
5124             return dlg;
5125         },
5126
5127         /**
5128          * Updates the message box body text
5129          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5130          * the XHTML-compliant non-breaking space character '&amp;#160;')
5131          * @return {Roo.MessageBox} This message box
5132          */
5133         updateText : function(text)
5134         {
5135             if(!dlg.isVisible() && !opt.width){
5136                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5137                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5138             }
5139             msgEl.innerHTML = text || '&#160;';
5140       
5141             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5142             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5143             var w = Math.max(
5144                     Math.min(opt.width || cw , this.maxWidth), 
5145                     Math.max(opt.minWidth || this.minWidth, bwidth)
5146             );
5147             if(opt.prompt){
5148                 activeTextEl.setWidth(w);
5149             }
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = false;
5152             }
5153             // to big, make it scroll. = But as usual stupid IE does not support
5154             // !important..
5155             
5156             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5157                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5158                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5159             } else {
5160                 bodyEl.dom.style.height = '';
5161                 bodyEl.dom.style.overflowY = '';
5162             }
5163             if (cw > w) {
5164                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5165             } else {
5166                 bodyEl.dom.style.overflowX = '';
5167             }
5168             
5169             dlg.setContentSize(w, bodyEl.getHeight());
5170             if(dlg.isVisible()){
5171                 dlg.fixedcenter = true;
5172             }
5173             return this;
5174         },
5175
5176         /**
5177          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5178          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5179          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5180          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5181          * @return {Roo.MessageBox} This message box
5182          */
5183         updateProgress : function(value, text){
5184             if(text){
5185                 this.updateText(text);
5186             }
5187             
5188             if (pp) { // weird bug on my firefox - for some reason this is not defined
5189                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5190                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5191             }
5192             return this;
5193         },        
5194
5195         /**
5196          * Returns true if the message box is currently displayed
5197          * @return {Boolean} True if the message box is visible, else false
5198          */
5199         isVisible : function(){
5200             return dlg && dlg.isVisible();  
5201         },
5202
5203         /**
5204          * Hides the message box if it is displayed
5205          */
5206         hide : function(){
5207             if(this.isVisible()){
5208                 dlg.hide();
5209             }  
5210         },
5211
5212         /**
5213          * Displays a new message box, or reinitializes an existing message box, based on the config options
5214          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5215          * The following config object properties are supported:
5216          * <pre>
5217 Property    Type             Description
5218 ----------  ---------------  ------------------------------------------------------------------------------------
5219 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5220                                    closes (defaults to undefined)
5221 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5222                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5223 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5224                                    progress and wait dialogs will ignore this property and always hide the
5225                                    close button as they can only be closed programmatically.
5226 cls               String           A custom CSS class to apply to the message box element
5227 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5228                                    displayed (defaults to 75)
5229 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5230                                    function will be btn (the name of the button that was clicked, if applicable,
5231                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5232                                    Progress and wait dialogs will ignore this option since they do not respond to
5233                                    user actions and can only be closed programmatically, so any required function
5234                                    should be called by the same code after it closes the dialog.
5235 icon              String           A CSS class that provides a background image to be used as an icon for
5236                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5237 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5238 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5239 modal             Boolean          False to allow user interaction with the page while the message box is
5240                                    displayed (defaults to true)
5241 msg               String           A string that will replace the existing message box body text (defaults
5242                                    to the XHTML-compliant non-breaking space character '&#160;')
5243 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5244 progress          Boolean          True to display a progress bar (defaults to false)
5245 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5246 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5247 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5248 title             String           The title text
5249 value             String           The string value to set into the active textbox element if displayed
5250 wait              Boolean          True to display a progress bar (defaults to false)
5251 width             Number           The width of the dialog in pixels
5252 </pre>
5253          *
5254          * Example usage:
5255          * <pre><code>
5256 Roo.Msg.show({
5257    title: 'Address',
5258    msg: 'Please enter your address:',
5259    width: 300,
5260    buttons: Roo.MessageBox.OKCANCEL,
5261    multiline: true,
5262    fn: saveAddress,
5263    animEl: 'addAddressBtn'
5264 });
5265 </code></pre>
5266          * @param {Object} config Configuration options
5267          * @return {Roo.MessageBox} This message box
5268          */
5269         show : function(options)
5270         {
5271             
5272             // this causes nightmares if you show one dialog after another
5273             // especially on callbacks..
5274              
5275             if(this.isVisible()){
5276                 
5277                 this.hide();
5278                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5279                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5280                 Roo.log("New Dialog Message:" +  options.msg )
5281                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5282                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5283                 
5284             }
5285             var d = this.getDialog();
5286             opt = options;
5287             d.setTitle(opt.title || "&#160;");
5288             d.closeEl.setDisplayed(opt.closable !== false);
5289             activeTextEl = textboxEl;
5290             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5291             if(opt.prompt){
5292                 if(opt.multiline){
5293                     textboxEl.hide();
5294                     textareaEl.show();
5295                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5296                         opt.multiline : this.defaultTextHeight);
5297                     activeTextEl = textareaEl;
5298                 }else{
5299                     textboxEl.show();
5300                     textareaEl.hide();
5301                 }
5302             }else{
5303                 textboxEl.hide();
5304                 textareaEl.hide();
5305             }
5306             progressEl.setDisplayed(opt.progress === true);
5307             if (opt.progress) {
5308                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5309             }
5310             this.updateProgress(0);
5311             activeTextEl.dom.value = opt.value || "";
5312             if(opt.prompt){
5313                 dlg.setDefaultButton(activeTextEl);
5314             }else{
5315                 var bs = opt.buttons;
5316                 var db = null;
5317                 if(bs && bs.ok){
5318                     db = buttons["ok"];
5319                 }else if(bs && bs.yes){
5320                     db = buttons["yes"];
5321                 }
5322                 dlg.setDefaultButton(db);
5323             }
5324             bwidth = updateButtons(opt.buttons);
5325             this.updateText(opt.msg);
5326             if(opt.cls){
5327                 d.el.addClass(opt.cls);
5328             }
5329             d.proxyDrag = opt.proxyDrag === true;
5330             d.modal = opt.modal !== false;
5331             d.mask = opt.modal !== false ? mask : false;
5332             if(!d.isVisible()){
5333                 // force it to the end of the z-index stack so it gets a cursor in FF
5334                 document.body.appendChild(dlg.el.dom);
5335                 d.animateTarget = null;
5336                 d.show(options.animEl);
5337             }
5338             return this;
5339         },
5340
5341         /**
5342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5344          * and closing the message box when the process is complete.
5345          * @param {String} title The title bar text
5346          * @param {String} msg The message box body text
5347          * @return {Roo.MessageBox} This message box
5348          */
5349         progress : function(title, msg){
5350             this.show({
5351                 title : title,
5352                 msg : msg,
5353                 buttons: false,
5354                 progress:true,
5355                 closable:false,
5356                 minWidth: this.minProgressWidth,
5357                 modal : true
5358             });
5359             return this;
5360         },
5361
5362         /**
5363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5364          * If a callback function is passed it will be called after the user clicks the button, and the
5365          * id of the button that was clicked will be passed as the only parameter to the callback
5366          * (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         alert : function(title, msg, fn, scope)
5374         {
5375             this.show({
5376                 title : title,
5377                 msg : msg,
5378                 buttons: this.OK,
5379                 fn: fn,
5380                 closable : false,
5381                 scope : scope,
5382                 modal : true
5383             });
5384             return this;
5385         },
5386
5387         /**
5388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5390          * You are responsible for closing the message box when the process is complete.
5391          * @param {String} msg The message box body text
5392          * @param {String} title (optional) The title bar text
5393          * @return {Roo.MessageBox} This message box
5394          */
5395         wait : function(msg, title){
5396             this.show({
5397                 title : title,
5398                 msg : msg,
5399                 buttons: false,
5400                 closable:false,
5401                 progress:true,
5402                 modal:true,
5403                 width:300,
5404                 wait:true
5405             });
5406             waitTimer = Roo.TaskMgr.start({
5407                 run: function(i){
5408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5409                 },
5410                 interval: 1000
5411             });
5412             return this;
5413         },
5414
5415         /**
5416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5419          * @param {String} title The title bar text
5420          * @param {String} msg The message box body text
5421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5422          * @param {Object} scope (optional) The scope of the callback function
5423          * @return {Roo.MessageBox} This message box
5424          */
5425         confirm : function(title, msg, fn, scope){
5426             this.show({
5427                 title : title,
5428                 msg : msg,
5429                 buttons: this.YESNO,
5430                 fn: fn,
5431                 scope : scope,
5432                 modal : true
5433             });
5434             return this;
5435         },
5436
5437         /**
5438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5441          * (could also be the top-right close button) and the text that was entered will be passed as the two
5442          * parameters to the callback.
5443          * @param {String} title The title bar text
5444          * @param {String} msg The message box body text
5445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5446          * @param {Object} scope (optional) The scope of the callback function
5447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5449          * @return {Roo.MessageBox} This message box
5450          */
5451         prompt : function(title, msg, fn, scope, multiline){
5452             this.show({
5453                 title : title,
5454                 msg : msg,
5455                 buttons: this.OKCANCEL,
5456                 fn: fn,
5457                 minWidth:250,
5458                 scope : scope,
5459                 prompt:true,
5460                 multiline: multiline,
5461                 modal : true
5462             });
5463             return this;
5464         },
5465
5466         /**
5467          * Button config that displays a single OK button
5468          * @type Object
5469          */
5470         OK : {ok:true},
5471         /**
5472          * Button config that displays Yes and No buttons
5473          * @type Object
5474          */
5475         YESNO : {yes:true, no:true},
5476         /**
5477          * Button config that displays OK and Cancel buttons
5478          * @type Object
5479          */
5480         OKCANCEL : {ok:true, cancel:true},
5481         /**
5482          * Button config that displays Yes, No and Cancel buttons
5483          * @type Object
5484          */
5485         YESNOCANCEL : {yes:true, no:true, cancel:true},
5486
5487         /**
5488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5489          * @type Number
5490          */
5491         defaultTextHeight : 75,
5492         /**
5493          * The maximum width in pixels of the message box (defaults to 600)
5494          * @type Number
5495          */
5496         maxWidth : 600,
5497         /**
5498          * The minimum width in pixels of the message box (defaults to 100)
5499          * @type Number
5500          */
5501         minWidth : 100,
5502         /**
5503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5505          * @type Number
5506          */
5507         minProgressWidth : 250,
5508         /**
5509          * An object containing the default button text strings that can be overriden for localized language support.
5510          * Supported properties are: ok, cancel, yes and no.
5511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5512          * @type Object
5513          */
5514         buttonText : {
5515             ok : "OK",
5516             cancel : "Cancel",
5517             yes : "Yes",
5518             no : "No"
5519         }
5520     };
5521 }();
5522
5523 /**
5524  * Shorthand for {@link Roo.MessageBox}
5525  */
5526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5527 Roo.Msg = Roo.Msg || Roo.MessageBox;
5528 /*
5529  * - LGPL
5530  *
5531  * navbar
5532  * 
5533  */
5534
5535 /**
5536  * @class Roo.bootstrap.nav.Bar
5537  * @extends Roo.bootstrap.Component
5538  * @abstract
5539  * Bootstrap Navbar class
5540
5541  * @constructor
5542  * Create a new Navbar
5543  * @param {Object} config The config object
5544  */
5545
5546
5547 Roo.bootstrap.nav.Bar = function(config){
5548     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5549     this.addEvents({
5550         // raw events
5551         /**
5552          * @event beforetoggle
5553          * Fire before toggle the menu
5554          * @param {Roo.EventObject} e
5555          */
5556         "beforetoggle" : true
5557     });
5558 };
5559
5560 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5561     
5562     
5563    
5564     // private
5565     navItems : false,
5566     loadMask : false,
5567     
5568     
5569     getAutoCreate : function(){
5570         
5571         
5572         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5573         
5574     },
5575     
5576     initEvents :function ()
5577     {
5578         //Roo.log(this.el.select('.navbar-toggle',true));
5579         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5580         
5581         var mark = {
5582             tag: "div",
5583             cls:"x-dlg-mask"
5584         };
5585         
5586         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5587         
5588         var size = this.el.getSize();
5589         this.maskEl.setSize(size.width, size.height);
5590         this.maskEl.enableDisplayMode("block");
5591         this.maskEl.hide();
5592         
5593         if(this.loadMask){
5594             this.maskEl.show();
5595         }
5596     },
5597     
5598     
5599     getChildContainer : function()
5600     {
5601         if (this.el && this.el.select('.collapse').getCount()) {
5602             return this.el.select('.collapse',true).first();
5603         }
5604         
5605         return this.el;
5606     },
5607     
5608     mask : function()
5609     {
5610         this.maskEl.show();
5611     },
5612     
5613     unmask : function()
5614     {
5615         this.maskEl.hide();
5616     },
5617     onToggle : function()
5618     {
5619         
5620         if(this.fireEvent('beforetoggle', this) === false){
5621             return;
5622         }
5623         var ce = this.el.select('.navbar-collapse',true).first();
5624       
5625         if (!ce.hasClass('show')) {
5626            this.expand();
5627         } else {
5628             this.collapse();
5629         }
5630         
5631         
5632     
5633     },
5634     /**
5635      * Expand the navbar pulldown 
5636      */
5637     expand : function ()
5638     {
5639        
5640         var ce = this.el.select('.navbar-collapse',true).first();
5641         if (ce.hasClass('collapsing')) {
5642             return;
5643         }
5644         ce.dom.style.height = '';
5645                // show it...
5646         ce.addClass('in'); // old...
5647         ce.removeClass('collapse');
5648         ce.addClass('show');
5649         var h = ce.getHeight();
5650         Roo.log(h);
5651         ce.removeClass('show');
5652         // at this point we should be able to see it..
5653         ce.addClass('collapsing');
5654         
5655         ce.setHeight(0); // resize it ...
5656         ce.on('transitionend', function() {
5657             //Roo.log('done transition');
5658             ce.removeClass('collapsing');
5659             ce.addClass('show');
5660             ce.removeClass('collapse');
5661
5662             ce.dom.style.height = '';
5663         }, this, { single: true} );
5664         ce.setHeight(h);
5665         ce.dom.scrollTop = 0;
5666     },
5667     /**
5668      * Collapse the navbar pulldown 
5669      */
5670     collapse : function()
5671     {
5672          var ce = this.el.select('.navbar-collapse',true).first();
5673        
5674         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5675             // it's collapsed or collapsing..
5676             return;
5677         }
5678         ce.removeClass('in'); // old...
5679         ce.setHeight(ce.getHeight());
5680         ce.removeClass('show');
5681         ce.addClass('collapsing');
5682         
5683         ce.on('transitionend', function() {
5684             ce.dom.style.height = '';
5685             ce.removeClass('collapsing');
5686             ce.addClass('collapse');
5687         }, this, { single: true} );
5688         ce.setHeight(0);
5689     }
5690     
5691     
5692     
5693 });
5694
5695
5696
5697  
5698
5699  /*
5700  * - LGPL
5701  *
5702  * navbar
5703  * 
5704  */
5705
5706 /**
5707  * @class Roo.bootstrap.nav.Simplebar
5708  * @extends Roo.bootstrap.nav.Bar
5709  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5710  * Bootstrap Sidebar class
5711  *
5712  * @cfg {Boolean} inverse is inverted color
5713  * 
5714  * @cfg {String} type (nav | pills | tabs)
5715  * @cfg {Boolean} arrangement stacked | justified
5716  * @cfg {String} align (left | right) alignment
5717  * 
5718  * @cfg {Boolean} main (true|false) main nav bar? default false
5719  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5720  * 
5721  * @cfg {String} tag (header|footer|nav|div) default is nav 
5722
5723  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5724  * 
5725  * 
5726  * @constructor
5727  * Create a new Sidebar
5728  * @param {Object} config The config object
5729  */
5730
5731
5732 Roo.bootstrap.nav.Simplebar = function(config){
5733     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5734 };
5735
5736 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5737     
5738     inverse: false,
5739     
5740     type: false,
5741     arrangement: '',
5742     align : false,
5743     
5744     weight : 'light',
5745     
5746     main : false,
5747     
5748     
5749     tag : false,
5750     
5751     
5752     getAutoCreate : function(){
5753         
5754         
5755         var cfg = {
5756             tag : this.tag || 'div',
5757             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5758         };
5759         if (['light','white'].indexOf(this.weight) > -1) {
5760             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5761         }
5762         cfg.cls += ' bg-' + this.weight;
5763         
5764         if (this.inverse) {
5765             cfg.cls += ' navbar-inverse';
5766             
5767         }
5768         
5769         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5770         
5771         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5772             return cfg;
5773         }
5774         
5775         
5776     
5777         
5778         cfg.cn = [
5779             {
5780                 cls: 'nav nav-' + this.xtype,
5781                 tag : 'ul'
5782             }
5783         ];
5784         
5785          
5786         this.type = this.type || 'nav';
5787         if (['tabs','pills'].indexOf(this.type) != -1) {
5788             cfg.cn[0].cls += ' nav-' + this.type
5789         
5790         
5791         } else {
5792             if (this.type!=='nav') {
5793                 Roo.log('nav type must be nav/tabs/pills')
5794             }
5795             cfg.cn[0].cls += ' navbar-nav'
5796         }
5797         
5798         
5799         
5800         
5801         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5802             cfg.cn[0].cls += ' nav-' + this.arrangement;
5803         }
5804         
5805         
5806         if (this.align === 'right') {
5807             cfg.cn[0].cls += ' navbar-right';
5808         }
5809         
5810         
5811         
5812         
5813         return cfg;
5814     
5815         
5816     }
5817     
5818     
5819     
5820 });
5821
5822
5823
5824  
5825
5826  
5827        /*
5828  * - LGPL
5829  *
5830  * navbar
5831  * navbar-fixed-top
5832  * navbar-expand-md  fixed-top 
5833  */
5834
5835 /**
5836  * @class Roo.bootstrap.nav.Headerbar
5837  * @extends Roo.bootstrap.nav.Simplebar
5838  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5839  * Bootstrap Sidebar class
5840  *
5841  * @cfg {String} brand what is brand
5842  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5843  * @cfg {String} brand_href href of the brand
5844  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5845  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5846  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5847  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5848  * 
5849  * @constructor
5850  * Create a new Sidebar
5851  * @param {Object} config The config object
5852  */
5853
5854
5855 Roo.bootstrap.nav.Headerbar = function(config){
5856     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5857       
5858 };
5859
5860 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5861     
5862     position: '',
5863     brand: '',
5864     brand_href: false,
5865     srButton : true,
5866     autohide : false,
5867     desktopCenter : false,
5868    
5869     
5870     getAutoCreate : function(){
5871         
5872         var   cfg = {
5873             tag: this.nav || 'nav',
5874             cls: 'navbar navbar-expand-md',
5875             role: 'navigation',
5876             cn: []
5877         };
5878         
5879         var cn = cfg.cn;
5880         if (this.desktopCenter) {
5881             cn.push({cls : 'container', cn : []});
5882             cn = cn[0].cn;
5883         }
5884         
5885         if(this.srButton){
5886             var btn = {
5887                 tag: 'button',
5888                 type: 'button',
5889                 cls: 'navbar-toggle navbar-toggler',
5890                 'data-toggle': 'collapse',
5891                 cn: [
5892                     {
5893                         tag: 'span',
5894                         cls: 'sr-only',
5895                         html: 'Toggle navigation'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar navbar-toggler-icon'
5900                     },
5901                     {
5902                         tag: 'span',
5903                         cls: 'icon-bar'
5904                     },
5905                     {
5906                         tag: 'span',
5907                         cls: 'icon-bar'
5908                     }
5909                 ]
5910             };
5911             
5912             cn.push( Roo.bootstrap.version == 4 ? btn : {
5913                 tag: 'div',
5914                 cls: 'navbar-header',
5915                 cn: [
5916                     btn
5917                 ]
5918             });
5919         }
5920         
5921         cn.push({
5922             tag: 'div',
5923             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5924             cn : []
5925         });
5926         
5927         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5928         
5929         if (['light','white'].indexOf(this.weight) > -1) {
5930             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5931         }
5932         cfg.cls += ' bg-' + this.weight;
5933         
5934         
5935         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5936             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5937             
5938             // tag can override this..
5939             
5940             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5941         }
5942         
5943         if (this.brand !== '') {
5944             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5945             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5946                 tag: 'a',
5947                 href: this.brand_href ? this.brand_href : '#',
5948                 cls: 'navbar-brand',
5949                 cn: [
5950                 this.brand
5951                 ]
5952             });
5953         }
5954         
5955         if(this.main){
5956             cfg.cls += ' main-nav';
5957         }
5958         
5959         
5960         return cfg;
5961
5962         
5963     },
5964     getHeaderChildContainer : function()
5965     {
5966         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5967             return this.el.select('.navbar-header',true).first();
5968         }
5969         
5970         return this.getChildContainer();
5971     },
5972     
5973     getChildContainer : function()
5974     {
5975          
5976         return this.el.select('.roo-navbar-collapse',true).first();
5977          
5978         
5979     },
5980     
5981     initEvents : function()
5982     {
5983         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5984         
5985         if (this.autohide) {
5986             
5987             var prevScroll = 0;
5988             var ft = this.el;
5989             
5990             Roo.get(document).on('scroll',function(e) {
5991                 var ns = Roo.get(document).getScroll().top;
5992                 var os = prevScroll;
5993                 prevScroll = ns;
5994                 
5995                 if(ns > os){
5996                     ft.removeClass('slideDown');
5997                     ft.addClass('slideUp');
5998                     return;
5999                 }
6000                 ft.removeClass('slideUp');
6001                 ft.addClass('slideDown');
6002                  
6003               
6004           },this);
6005         }
6006     }    
6007     
6008 });
6009
6010
6011
6012  
6013
6014  /*
6015  * - LGPL
6016  *
6017  * navbar
6018  * 
6019  */
6020
6021 /**
6022  * @class Roo.bootstrap.nav.Sidebar
6023  * @extends Roo.bootstrap.nav.Bar
6024  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6025  * Bootstrap Sidebar class
6026  * 
6027  * @constructor
6028  * Create a new Sidebar
6029  * @param {Object} config The config object
6030  */
6031
6032
6033 Roo.bootstrap.nav.Sidebar = function(config){
6034     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6035 };
6036
6037 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6038     
6039     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6040     
6041     getAutoCreate : function(){
6042         
6043         
6044         return  {
6045             tag: 'div',
6046             cls: 'sidebar sidebar-nav'
6047         };
6048     
6049         
6050     }
6051     
6052     
6053     
6054 });
6055
6056
6057
6058  
6059
6060  /*
6061  * - LGPL
6062  *
6063  * nav group
6064  * 
6065  */
6066
6067 /**
6068  * @class Roo.bootstrap.nav.Group
6069  * @extends Roo.bootstrap.Component
6070  * @children Roo.bootstrap.nav.Item
6071  * Bootstrap NavGroup class
6072  * @cfg {String} align (left|right)
6073  * @cfg {Boolean} inverse
6074  * @cfg {String} type (nav|pills|tab) default nav
6075  * @cfg {String} navId - reference Id for navbar.
6076  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077  * 
6078  * @constructor
6079  * Create a new nav group
6080  * @param {Object} config The config object
6081  */
6082
6083 Roo.bootstrap.nav.Group = function(config){
6084     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6085     this.navItems = [];
6086    
6087     Roo.bootstrap.nav.Group.register(this);
6088      this.addEvents({
6089         /**
6090              * @event changed
6091              * Fires when the active item changes
6092              * @param {Roo.bootstrap.nav.Group} this
6093              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6095          */
6096         'changed': true
6097      });
6098     
6099 };
6100
6101 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6102     
6103     align: '',
6104     inverse: false,
6105     form: false,
6106     type: 'nav',
6107     navId : '',
6108     // private
6109     pilltype : true,
6110     
6111     navItems : false, 
6112     
6113     getAutoCreate : function()
6114     {
6115         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6116         
6117         cfg = {
6118             tag : 'ul',
6119             cls: 'nav' 
6120         };
6121         if (Roo.bootstrap.version == 4) {
6122             if (['tabs','pills'].indexOf(this.type) != -1) {
6123                 cfg.cls += ' nav-' + this.type; 
6124             } else {
6125                 // trying to remove so header bar can right align top?
6126                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127                     // do not use on header bar... 
6128                     cfg.cls += ' navbar-nav';
6129                 }
6130             }
6131             
6132         } else {
6133             if (['tabs','pills'].indexOf(this.type) != -1) {
6134                 cfg.cls += ' nav-' + this.type
6135             } else {
6136                 if (this.type !== 'nav') {
6137                     Roo.log('nav type must be nav/tabs/pills')
6138                 }
6139                 cfg.cls += ' navbar-nav'
6140             }
6141         }
6142         
6143         if (this.parent() && this.parent().sidebar) {
6144             cfg = {
6145                 tag: 'ul',
6146                 cls: 'dashboard-menu sidebar-menu'
6147             };
6148             
6149             return cfg;
6150         }
6151         
6152         if (this.form === true) {
6153             cfg = {
6154                 tag: 'form',
6155                 cls: 'navbar-form form-inline'
6156             };
6157             //nav navbar-right ml-md-auto
6158             if (this.align === 'right') {
6159                 cfg.cls += ' navbar-right ml-md-auto';
6160             } else {
6161                 cfg.cls += ' navbar-left';
6162             }
6163         }
6164         
6165         if (this.align === 'right') {
6166             cfg.cls += ' navbar-right ml-md-auto';
6167         } else {
6168             cfg.cls += ' mr-auto';
6169         }
6170         
6171         if (this.inverse) {
6172             cfg.cls += ' navbar-inverse';
6173             
6174         }
6175         
6176         
6177         return cfg;
6178     },
6179     /**
6180     * sets the active Navigation item
6181     * @param {Roo.bootstrap.nav.Item} the new current navitem
6182     */
6183     setActiveItem : function(item)
6184     {
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             if (v == item) {
6188                 return ;
6189             }
6190             if (v.isActive()) {
6191                 v.setActive(false, true);
6192                 prev = v;
6193                 
6194             }
6195             
6196         });
6197
6198         item.setActive(true, true);
6199         this.fireEvent('changed', this, item, prev);
6200         
6201         
6202     },
6203     /**
6204     * gets the active Navigation item
6205     * @return {Roo.bootstrap.nav.Item} the current navitem
6206     */
6207     getActive : function()
6208     {
6209         
6210         var prev = false;
6211         Roo.each(this.navItems, function(v){
6212             
6213             if (v.isActive()) {
6214                 prev = v;
6215                 
6216             }
6217             
6218         });
6219         return prev;
6220     },
6221     
6222     indexOfNav : function()
6223     {
6224         
6225         var prev = false;
6226         Roo.each(this.navItems, function(v,i){
6227             
6228             if (v.isActive()) {
6229                 prev = i;
6230                 
6231             }
6232             
6233         });
6234         return prev;
6235     },
6236     /**
6237     * adds a Navigation item
6238     * @param {Roo.bootstrap.nav.Item} the navitem to add
6239     */
6240     addItem : function(cfg)
6241     {
6242         if (this.form && Roo.bootstrap.version == 4) {
6243             cfg.tag = 'div';
6244         }
6245         var cn = new Roo.bootstrap.nav.Item(cfg);
6246         this.register(cn);
6247         cn.parentId = this.id;
6248         cn.onRender(this.el, null);
6249         return cn;
6250     },
6251     /**
6252     * register a Navigation item
6253     * @param {Roo.bootstrap.nav.Item} the navitem to add
6254     */
6255     register : function(item)
6256     {
6257         this.navItems.push( item);
6258         item.navId = this.navId;
6259     
6260     },
6261     
6262     /**
6263     * clear all the Navigation item
6264     */
6265    
6266     clearAll : function()
6267     {
6268         this.navItems = [];
6269         this.el.dom.innerHTML = '';
6270     },
6271     
6272     getNavItem: function(tabId)
6273     {
6274         var ret = false;
6275         Roo.each(this.navItems, function(e) {
6276             if (e.tabId == tabId) {
6277                ret =  e;
6278                return false;
6279             }
6280             return true;
6281             
6282         });
6283         return ret;
6284     },
6285     
6286     setActiveNext : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i > this.navItems.length) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i+1]);
6293     },
6294     setActivePrev : function()
6295     {
6296         var i = this.indexOfNav(this.getActive());
6297         if (i  < 1) {
6298             return;
6299         }
6300         this.setActiveItem(this.navItems[i-1]);
6301     },
6302     clearWasActive : function(except) {
6303         Roo.each(this.navItems, function(e) {
6304             if (e.tabId != except.tabId && e.was_active) {
6305                e.was_active = false;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311     },
6312     getWasActive : function ()
6313     {
6314         var r = false;
6315         Roo.each(this.navItems, function(e) {
6316             if (e.was_active) {
6317                r = e;
6318                return false;
6319             }
6320             return true;
6321             
6322         });
6323         return r;
6324     }
6325     
6326     
6327 });
6328
6329  
6330 Roo.apply(Roo.bootstrap.nav.Group, {
6331     
6332     groups: {},
6333      /**
6334     * register a Navigation Group
6335     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6336     */
6337     register : function(navgrp)
6338     {
6339         this.groups[navgrp.navId] = navgrp;
6340         
6341     },
6342     /**
6343     * fetch a Navigation Group based on the navigation ID
6344     * @param {string} the navgroup to add
6345     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6346     */
6347     get: function(navId) {
6348         if (typeof(this.groups[navId]) == 'undefined') {
6349             return false;
6350             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6351         }
6352         return this.groups[navId] ;
6353     }
6354     
6355     
6356     
6357 });
6358
6359  /**
6360  * @class Roo.bootstrap.nav.Item
6361  * @extends Roo.bootstrap.Component
6362  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6363  * @parent Roo.bootstrap.nav.Group
6364  * @licence LGPL
6365  * Bootstrap Navbar.NavItem class
6366  * 
6367  * @cfg {String} href  link to
6368  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6369  * @cfg {Boolean} button_outline show and outlined button
6370  * @cfg {String} html content of button
6371  * @cfg {String} badge text inside badge
6372  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6373  * @cfg {String} glyphicon DEPRICATED - use fa
6374  * @cfg {String} icon DEPRICATED - use fa
6375  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6376  * @cfg {Boolean} active Is item active
6377  * @cfg {Boolean} disabled Is item disabled
6378  * @cfg {String} linkcls  Link Class
6379  * @cfg {Boolean} preventDefault (true | false) default false
6380  * @cfg {String} tabId the tab that this item activates.
6381  * @cfg {String} tagtype (a|span) render as a href or span?
6382  * @cfg {Boolean} animateRef (true|false) link to element default false  
6383  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6384   
6385  * @constructor
6386  * Create a new Navbar Item
6387  * @param {Object} config The config object
6388  */
6389 Roo.bootstrap.nav.Item = function(config){
6390     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6391     this.addEvents({
6392         // raw events
6393         /**
6394          * @event click
6395          * The raw click event for the entire grid.
6396          * @param {Roo.EventObject} e
6397          */
6398         "click" : true,
6399          /**
6400             * @event changed
6401             * Fires when the active item active state changes
6402             * @param {Roo.bootstrap.nav.Item} this
6403             * @param {boolean} state the new state
6404              
6405          */
6406         'changed': true,
6407         /**
6408             * @event scrollto
6409             * Fires when scroll to element
6410             * @param {Roo.bootstrap.nav.Item} this
6411             * @param {Object} options
6412             * @param {Roo.EventObject} e
6413              
6414          */
6415         'scrollto': true
6416     });
6417    
6418 };
6419
6420 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6421     
6422     href: false,
6423     html: '',
6424     badge: '',
6425     icon: false,
6426     fa : false,
6427     glyphicon: false,
6428     active: false,
6429     preventDefault : false,
6430     tabId : false,
6431     tagtype : 'a',
6432     tag: 'li',
6433     disabled : false,
6434     animateRef : false,
6435     was_active : false,
6436     button_weight : '',
6437     button_outline : false,
6438     linkcls : '',
6439     navLink: false,
6440     
6441     getAutoCreate : function(){
6442          
6443         var cfg = {
6444             tag: this.tag,
6445             cls: 'nav-item'
6446         };
6447         
6448         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6449         
6450         if (this.active) {
6451             cfg.cls +=  ' active' ;
6452         }
6453         if (this.disabled) {
6454             cfg.cls += ' disabled';
6455         }
6456         
6457         // BS4 only?
6458         if (this.button_weight.length) {
6459             cfg.tag = this.href ? 'a' : 'button';
6460             cfg.html = this.html || '';
6461             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6462             if (this.href) {
6463                 cfg.href = this.href;
6464             }
6465             if (this.fa) {
6466                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6467             } else {
6468                 cfg.cls += " nav-html";
6469             }
6470             
6471             // menu .. should add dropdown-menu class - so no need for carat..
6472             
6473             if (this.badge !== '') {
6474                  
6475                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6476             }
6477             return cfg;
6478         }
6479         
6480         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6481             cfg.cn = [
6482                 {
6483                     tag: this.tagtype,
6484                     href : this.href || "#",
6485                     html: this.html || '',
6486                     cls : ''
6487                 }
6488             ];
6489             if (this.tagtype == 'a') {
6490                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6491         
6492             }
6493             if (this.icon) {
6494                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else  if (this.fa) {
6496                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6497             } else if(this.glyphicon) {
6498                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6499             } else {
6500                 cfg.cn[0].cls += " nav-html";
6501             }
6502             
6503             if (this.menu) {
6504                 cfg.cn[0].html += " <span class='caret'></span>";
6505              
6506             }
6507             
6508             if (this.badge !== '') {
6509                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6510             }
6511         }
6512         
6513         
6514         
6515         return cfg;
6516     },
6517     onRender : function(ct, position)
6518     {
6519        // Roo.log("Call onRender: " + this.xtype);
6520         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6521             this.tag = 'div';
6522         }
6523         
6524         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6525         this.navLink = this.el.select('.nav-link',true).first();
6526         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6527         return ret;
6528     },
6529       
6530     
6531     initEvents: function() 
6532     {
6533         if (typeof (this.menu) != 'undefined') {
6534             this.menu.parentType = this.xtype;
6535             this.menu.triggerEl = this.el;
6536             this.menu = this.addxtype(Roo.apply({}, this.menu));
6537         }
6538         
6539         this.el.on('click', this.onClick, this);
6540         
6541         //if(this.tagtype == 'span'){
6542         //    this.el.select('span',true).on('click', this.onClick, this);
6543         //}
6544        
6545         // at this point parent should be available..
6546         this.parent().register(this);
6547     },
6548     
6549     onClick : function(e)
6550     {
6551         if (e.getTarget('.dropdown-menu-item')) {
6552             // did you click on a menu itemm.... - then don't trigger onclick..
6553             return;
6554         }
6555         
6556         if(
6557                 this.preventDefault ||
6558                                 this.href === false ||
6559                 this.href === '#' 
6560         ){
6561             //Roo.log("NavItem - prevent Default?");
6562             e.preventDefault();
6563         }
6564         
6565         if (this.disabled) {
6566             return;
6567         }
6568         
6569         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6570         if (tg && tg.transition) {
6571             Roo.log("waiting for the transitionend");
6572             return;
6573         }
6574         
6575         
6576         
6577         //Roo.log("fire event clicked");
6578         if(this.fireEvent('click', this, e) === false){
6579             return;
6580         };
6581         
6582         if(this.tagtype == 'span'){
6583             return;
6584         }
6585         
6586         //Roo.log(this.href);
6587         var ael = this.el.select('a',true).first();
6588         //Roo.log(ael);
6589         
6590         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6591             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6592             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6593                 return; // ignore... - it's a 'hash' to another page.
6594             }
6595             Roo.log("NavItem - prevent Default?");
6596             e.preventDefault();
6597             this.scrollToElement(e);
6598         }
6599         
6600         
6601         var p =  this.parent();
6602    
6603         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6604             if (typeof(p.setActiveItem) !== 'undefined') {
6605                 p.setActiveItem(this);
6606             }
6607         }
6608         
6609         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6610         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6611             // remove the collapsed menu expand...
6612             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6613         }
6614     },
6615     
6616     isActive: function () {
6617         return this.active
6618     },
6619     setActive : function(state, fire, is_was_active)
6620     {
6621         if (this.active && !state && this.navId) {
6622             this.was_active = true;
6623             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6624             if (nv) {
6625                 nv.clearWasActive(this);
6626             }
6627             
6628         }
6629         this.active = state;
6630         
6631         if (!state ) {
6632             this.el.removeClass('active');
6633             this.navLink ? this.navLink.removeClass('active') : false;
6634         } else if (!this.el.hasClass('active')) {
6635             
6636             this.el.addClass('active');
6637             if (Roo.bootstrap.version == 4 && this.navLink ) {
6638                 this.navLink.addClass('active');
6639             }
6640             
6641         }
6642         if (fire) {
6643             this.fireEvent('changed', this, state);
6644         }
6645         
6646         // show a panel if it's registered and related..
6647         
6648         if (!this.navId || !this.tabId || !state || is_was_active) {
6649             return;
6650         }
6651         
6652         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6653         if (!tg) {
6654             return;
6655         }
6656         var pan = tg.getPanelByName(this.tabId);
6657         if (!pan) {
6658             return;
6659         }
6660         // if we can not flip to new panel - go back to old nav highlight..
6661         if (false == tg.showPanel(pan)) {
6662             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6663             if (nv) {
6664                 var onav = nv.getWasActive();
6665                 if (onav) {
6666                     onav.setActive(true, false, true);
6667                 }
6668             }
6669             
6670         }
6671         
6672         
6673         
6674     },
6675      // this should not be here...
6676     setDisabled : function(state)
6677     {
6678         this.disabled = state;
6679         if (!state ) {
6680             this.el.removeClass('disabled');
6681         } else if (!this.el.hasClass('disabled')) {
6682             this.el.addClass('disabled');
6683         }
6684         
6685     },
6686     
6687     /**
6688      * Fetch the element to display the tooltip on.
6689      * @return {Roo.Element} defaults to this.el
6690      */
6691     tooltipEl : function()
6692     {
6693         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6694     },
6695     
6696     scrollToElement : function(e)
6697     {
6698         var c = document.body;
6699         
6700         /*
6701          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6702          */
6703         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6704             c = document.documentElement;
6705         }
6706         
6707         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6708         
6709         if(!target){
6710             return;
6711         }
6712
6713         var o = target.calcOffsetsTo(c);
6714         
6715         var options = {
6716             target : target,
6717             value : o[1]
6718         };
6719         
6720         this.fireEvent('scrollto', this, options, e);
6721         
6722         Roo.get(c).scrollTo('top', options.value, true);
6723         
6724         return;
6725     },
6726     /**
6727      * Set the HTML (text content) of the item
6728      * @param {string} html  content for the nav item
6729      */
6730     setHtml : function(html)
6731     {
6732         this.html = html;
6733         this.htmlEl.dom.innerHTML = html;
6734         
6735     } 
6736 });
6737  
6738
6739  /*
6740  * - LGPL
6741  *
6742  * sidebar item
6743  *
6744  *  li
6745  *    <span> icon </span>
6746  *    <span> text </span>
6747  *    <span>badge </span>
6748  */
6749
6750 /**
6751  * @class Roo.bootstrap.nav.SidebarItem
6752  * @extends Roo.bootstrap.nav.Item
6753  * Bootstrap Navbar.NavSidebarItem class
6754  * 
6755  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6756  * {Boolean} open is the menu open
6757  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6758  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6759  * {String} buttonSize (sm|md|lg)the extra classes for the button
6760  * {Boolean} showArrow show arrow next to the text (default true)
6761  * @constructor
6762  * Create a new Navbar Button
6763  * @param {Object} config The config object
6764  */
6765 Roo.bootstrap.nav.SidebarItem = function(config){
6766     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6767     this.addEvents({
6768         // raw events
6769         /**
6770          * @event click
6771          * The raw click event for the entire grid.
6772          * @param {Roo.EventObject} e
6773          */
6774         "click" : true,
6775          /**
6776             * @event changed
6777             * Fires when the active item active state changes
6778             * @param {Roo.bootstrap.nav.SidebarItem} this
6779             * @param {boolean} state the new state
6780              
6781          */
6782         'changed': true
6783     });
6784    
6785 };
6786
6787 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6788     
6789     badgeWeight : 'default',
6790     
6791     open: false,
6792     
6793     buttonView : false,
6794     
6795     buttonWeight : 'default',
6796     
6797     buttonSize : 'md',
6798     
6799     showArrow : true,
6800     
6801     getAutoCreate : function(){
6802         
6803         
6804         var a = {
6805                 tag: 'a',
6806                 href : this.href || '#',
6807                 cls: '',
6808                 html : '',
6809                 cn : []
6810         };
6811         
6812         if(this.buttonView){
6813             a = {
6814                 tag: 'button',
6815                 href : this.href || '#',
6816                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6817                 html : this.html,
6818                 cn : []
6819             };
6820         }
6821         
6822         var cfg = {
6823             tag: 'li',
6824             cls: '',
6825             cn: [ a ]
6826         };
6827         
6828         if (this.active) {
6829             cfg.cls += ' active';
6830         }
6831         
6832         if (this.disabled) {
6833             cfg.cls += ' disabled';
6834         }
6835         if (this.open) {
6836             cfg.cls += ' open x-open';
6837         }
6838         // left icon..
6839         if (this.glyphicon || this.icon) {
6840             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6841             a.cn.push({ tag : 'i', cls : c }) ;
6842         }
6843         
6844         if(!this.buttonView){
6845             var span = {
6846                 tag: 'span',
6847                 html : this.html || ''
6848             };
6849
6850             a.cn.push(span);
6851             
6852         }
6853         
6854         if (this.badge !== '') {
6855             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6856         }
6857         
6858         if (this.menu) {
6859             
6860             if(this.showArrow){
6861                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6862             }
6863             
6864             a.cls += ' dropdown-toggle treeview' ;
6865         }
6866         
6867         return cfg;
6868     },
6869     
6870     initEvents : function()
6871     { 
6872         if (typeof (this.menu) != 'undefined') {
6873             this.menu.parentType = this.xtype;
6874             this.menu.triggerEl = this.el;
6875             this.menu = this.addxtype(Roo.apply({}, this.menu));
6876         }
6877         
6878         this.el.on('click', this.onClick, this);
6879         
6880         if(this.badge !== ''){
6881             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6882         }
6883         
6884     },
6885     
6886     onClick : function(e)
6887     {
6888         if(this.disabled){
6889             e.preventDefault();
6890             return;
6891         }
6892         
6893         if(this.preventDefault){
6894             e.preventDefault();
6895         }
6896         
6897         this.fireEvent('click', this, e);
6898     },
6899     
6900     disable : function()
6901     {
6902         this.setDisabled(true);
6903     },
6904     
6905     enable : function()
6906     {
6907         this.setDisabled(false);
6908     },
6909     
6910     setDisabled : function(state)
6911     {
6912         if(this.disabled == state){
6913             return;
6914         }
6915         
6916         this.disabled = state;
6917         
6918         if (state) {
6919             this.el.addClass('disabled');
6920             return;
6921         }
6922         
6923         this.el.removeClass('disabled');
6924         
6925         return;
6926     },
6927     
6928     setActive : function(state)
6929     {
6930         if(this.active == state){
6931             return;
6932         }
6933         
6934         this.active = state;
6935         
6936         if (state) {
6937             this.el.addClass('active');
6938             return;
6939         }
6940         
6941         this.el.removeClass('active');
6942         
6943         return;
6944     },
6945     
6946     isActive: function () 
6947     {
6948         return this.active;
6949     },
6950     
6951     setBadge : function(str)
6952     {
6953         if(!this.badgeEl){
6954             return;
6955         }
6956         
6957         this.badgeEl.dom.innerHTML = str;
6958     }
6959     
6960    
6961      
6962  
6963 });
6964  
6965
6966  /*
6967  * - LGPL
6968  *
6969  * nav progress bar
6970  * 
6971  */
6972
6973 /**
6974  * @class Roo.bootstrap.nav.ProgressBar
6975  * @extends Roo.bootstrap.Component
6976  * @children Roo.bootstrap.nav.ProgressBarItem
6977  * Bootstrap NavProgressBar class
6978  * 
6979  * @constructor
6980  * Create a new nav progress bar - a bar indicating step along a process
6981  * @param {Object} config The config object
6982  */
6983
6984 Roo.bootstrap.nav.ProgressBar = function(config){
6985     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6986
6987     this.bullets = this.bullets || [];
6988    
6989 //    Roo.bootstrap.nav.ProgressBar.register(this);
6990      this.addEvents({
6991         /**
6992              * @event changed
6993              * Fires when the active item changes
6994              * @param {Roo.bootstrap.nav.ProgressBar} this
6995              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6996              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6997          */
6998         'changed': true
6999      });
7000     
7001 };
7002
7003 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7004     /**
7005      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7006      * Bullets for the Nav Progress bar for the toolbar
7007      */
7008     bullets : [],
7009     barItems : [],
7010     
7011     getAutoCreate : function()
7012     {
7013         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7014         
7015         cfg = {
7016             tag : 'div',
7017             cls : 'roo-navigation-bar-group',
7018             cn : [
7019                 {
7020                     tag : 'div',
7021                     cls : 'roo-navigation-top-bar'
7022                 },
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-bullets-bar',
7026                     cn : [
7027                         {
7028                             tag : 'ul',
7029                             cls : 'roo-navigation-bar'
7030                         }
7031                     ]
7032                 },
7033                 
7034                 {
7035                     tag : 'div',
7036                     cls : 'roo-navigation-bottom-bar'
7037                 }
7038             ]
7039             
7040         };
7041         
7042         return cfg;
7043         
7044     },
7045     
7046     initEvents: function() 
7047     {
7048         
7049     },
7050     
7051     onRender : function(ct, position) 
7052     {
7053         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7054         
7055         if(this.bullets.length){
7056             Roo.each(this.bullets, function(b){
7057                this.addItem(b);
7058             }, this);
7059         }
7060         
7061         this.format();
7062         
7063     },
7064     
7065     addItem : function(cfg)
7066     {
7067         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7068         
7069         item.parentId = this.id;
7070         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7071         
7072         if(cfg.html){
7073             var top = new Roo.bootstrap.Element({
7074                 tag : 'div',
7075                 cls : 'roo-navigation-bar-text'
7076             });
7077             
7078             var bottom = new Roo.bootstrap.Element({
7079                 tag : 'div',
7080                 cls : 'roo-navigation-bar-text'
7081             });
7082             
7083             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7084             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7085             
7086             var topText = new Roo.bootstrap.Element({
7087                 tag : 'span',
7088                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7089             });
7090             
7091             var bottomText = new Roo.bootstrap.Element({
7092                 tag : 'span',
7093                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7094             });
7095             
7096             topText.onRender(top.el, null);
7097             bottomText.onRender(bottom.el, null);
7098             
7099             item.topEl = top;
7100             item.bottomEl = bottom;
7101         }
7102         
7103         this.barItems.push(item);
7104         
7105         return item;
7106     },
7107     
7108     getActive : function()
7109     {
7110         var active = false;
7111         
7112         Roo.each(this.barItems, function(v){
7113             
7114             if (!v.isActive()) {
7115                 return;
7116             }
7117             
7118             active = v;
7119             return false;
7120             
7121         });
7122         
7123         return active;
7124     },
7125     
7126     setActiveItem : function(item)
7127     {
7128         var prev = false;
7129         
7130         Roo.each(this.barItems, function(v){
7131             if (v.rid == item.rid) {
7132                 return ;
7133             }
7134             
7135             if (v.isActive()) {
7136                 v.setActive(false);
7137                 prev = v;
7138             }
7139         });
7140
7141         item.setActive(true);
7142         
7143         this.fireEvent('changed', this, item, prev);
7144     },
7145     
7146     getBarItem: function(rid)
7147     {
7148         var ret = false;
7149         
7150         Roo.each(this.barItems, function(e) {
7151             if (e.rid != rid) {
7152                 return;
7153             }
7154             
7155             ret =  e;
7156             return false;
7157         });
7158         
7159         return ret;
7160     },
7161     
7162     indexOfItem : function(item)
7163     {
7164         var index = false;
7165         
7166         Roo.each(this.barItems, function(v, i){
7167             
7168             if (v.rid != item.rid) {
7169                 return;
7170             }
7171             
7172             index = i;
7173             return false
7174         });
7175         
7176         return index;
7177     },
7178     
7179     setActiveNext : function()
7180     {
7181         var i = this.indexOfItem(this.getActive());
7182         
7183         if (i > this.barItems.length) {
7184             return;
7185         }
7186         
7187         this.setActiveItem(this.barItems[i+1]);
7188     },
7189     
7190     setActivePrev : function()
7191     {
7192         var i = this.indexOfItem(this.getActive());
7193         
7194         if (i  < 1) {
7195             return;
7196         }
7197         
7198         this.setActiveItem(this.barItems[i-1]);
7199     },
7200     
7201     format : function()
7202     {
7203         if(!this.barItems.length){
7204             return;
7205         }
7206      
7207         var width = 100 / this.barItems.length;
7208         
7209         Roo.each(this.barItems, function(i){
7210             i.el.setStyle('width', width + '%');
7211             i.topEl.el.setStyle('width', width + '%');
7212             i.bottomEl.el.setStyle('width', width + '%');
7213         }, this);
7214         
7215     }
7216     
7217 });
7218 /*
7219  * - LGPL
7220  *
7221  * Nav Progress Item
7222  * 
7223  */
7224
7225 /**
7226  * @class Roo.bootstrap.nav.ProgressBarItem
7227  * @extends Roo.bootstrap.Component
7228  * Bootstrap NavProgressBarItem class
7229  * @cfg {String} rid the reference id
7230  * @cfg {Boolean} active (true|false) Is item active default false
7231  * @cfg {Boolean} disabled (true|false) Is item active default false
7232  * @cfg {String} html
7233  * @cfg {String} position (top|bottom) text position default bottom
7234  * @cfg {String} icon show icon instead of number
7235  * 
7236  * @constructor
7237  * Create a new NavProgressBarItem
7238  * @param {Object} config The config object
7239  */
7240 Roo.bootstrap.nav.ProgressBarItem = function(config){
7241     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7242     this.addEvents({
7243         // raw events
7244         /**
7245          * @event click
7246          * The raw click event for the entire grid.
7247          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7248          * @param {Roo.EventObject} e
7249          */
7250         "click" : true
7251     });
7252    
7253 };
7254
7255 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7256     
7257     rid : '',
7258     active : false,
7259     disabled : false,
7260     html : '',
7261     position : 'bottom',
7262     icon : false,
7263     
7264     getAutoCreate : function()
7265     {
7266         var iconCls = 'roo-navigation-bar-item-icon';
7267         
7268         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7269         
7270         var cfg = {
7271             tag: 'li',
7272             cls: 'roo-navigation-bar-item',
7273             cn : [
7274                 {
7275                     tag : 'i',
7276                     cls : iconCls
7277                 }
7278             ]
7279         };
7280         
7281         if(this.active){
7282             cfg.cls += ' active';
7283         }
7284         if(this.disabled){
7285             cfg.cls += ' disabled';
7286         }
7287         
7288         return cfg;
7289     },
7290     
7291     disable : function()
7292     {
7293         this.setDisabled(true);
7294     },
7295     
7296     enable : function()
7297     {
7298         this.setDisabled(false);
7299     },
7300     
7301     initEvents: function() 
7302     {
7303         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7304         
7305         this.iconEl.on('click', this.onClick, this);
7306     },
7307     
7308     onClick : function(e)
7309     {
7310         e.preventDefault();
7311         
7312         if(this.disabled){
7313             return;
7314         }
7315         
7316         if(this.fireEvent('click', this, e) === false){
7317             return;
7318         };
7319         
7320         this.parent().setActiveItem(this);
7321     },
7322     
7323     isActive: function () 
7324     {
7325         return this.active;
7326     },
7327     
7328     setActive : function(state)
7329     {
7330         if(this.active == state){
7331             return;
7332         }
7333         
7334         this.active = state;
7335         
7336         if (state) {
7337             this.el.addClass('active');
7338             return;
7339         }
7340         
7341         this.el.removeClass('active');
7342         
7343         return;
7344     },
7345     
7346     setDisabled : function(state)
7347     {
7348         if(this.disabled == state){
7349             return;
7350         }
7351         
7352         this.disabled = state;
7353         
7354         if (state) {
7355             this.el.addClass('disabled');
7356             return;
7357         }
7358         
7359         this.el.removeClass('disabled');
7360     },
7361     
7362     tooltipEl : function()
7363     {
7364         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7365     }
7366 });
7367  
7368
7369  /*
7370  * - LGPL
7371  *
7372  *  Breadcrumb Nav
7373  * 
7374  */
7375 Roo.namespace('Roo.bootstrap.breadcrumb');
7376
7377
7378 /**
7379  * @class Roo.bootstrap.breadcrumb.Nav
7380  * @extends Roo.bootstrap.Component
7381  * Bootstrap Breadcrumb Nav Class
7382  *  
7383  * @children Roo.bootstrap.breadcrumb.Item
7384  * 
7385  * @constructor
7386  * Create a new breadcrumb.Nav
7387  * @param {Object} config The config object
7388  */
7389
7390
7391 Roo.bootstrap.breadcrumb.Nav = function(config){
7392     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7393     
7394     
7395 };
7396
7397 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7398     
7399     getAutoCreate : function()
7400     {
7401
7402         var cfg = {
7403             tag: 'nav',
7404             cn : [
7405                 {
7406                     tag : 'ol',
7407                     cls : 'breadcrumb'
7408                 }
7409             ]
7410             
7411         };
7412           
7413         return cfg;
7414     },
7415     
7416     initEvents: function()
7417     {
7418         this.olEl = this.el.select('ol',true).first();    
7419     },
7420     getChildContainer : function()
7421     {
7422         return this.olEl;  
7423     }
7424     
7425 });
7426
7427  /*
7428  * - LGPL
7429  *
7430  *  Breadcrumb Item
7431  * 
7432  */
7433
7434
7435 /**
7436  * @class Roo.bootstrap.breadcrumb.Nav
7437  * @extends Roo.bootstrap.Component
7438  * @children Roo.bootstrap.Component
7439  * @parent Roo.bootstrap.breadcrumb.Nav
7440  * Bootstrap Breadcrumb Nav Class
7441  *  
7442  * 
7443  * @cfg {String} html the content of the link.
7444  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445  * @cfg {Boolean} active is it active
7446
7447  * 
7448  * @constructor
7449  * Create a new breadcrumb.Nav
7450  * @param {Object} config The config object
7451  */
7452
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7455     this.addEvents({
7456         // img events
7457         /**
7458          * @event click
7459          * The img click event for the img.
7460          * @param {Roo.EventObject} e
7461          */
7462         "click" : true
7463     });
7464     
7465 };
7466
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7468     
7469     href: false,
7470     html : '',
7471     
7472     getAutoCreate : function()
7473     {
7474
7475         var cfg = {
7476             tag: 'li',
7477             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7478         };
7479         if (this.href !== false) {
7480             cfg.cn = [{
7481                 tag : 'a',
7482                 href : this.href,
7483                 html : this.html
7484             }];
7485         } else {
7486             cfg.html = this.html;
7487         }
7488         
7489         return cfg;
7490     },
7491     
7492     initEvents: function()
7493     {
7494         if (this.href) {
7495             this.el.select('a', true).first().on('click',this.onClick, this)
7496         }
7497         
7498     },
7499     onClick : function(e)
7500     {
7501         e.preventDefault();
7502         this.fireEvent('click',this,  e);
7503     }
7504     
7505 });
7506
7507  /*
7508  * - LGPL
7509  *
7510  * row
7511  * 
7512  */
7513
7514 /**
7515  * @class Roo.bootstrap.Row
7516  * @extends Roo.bootstrap.Component
7517  * @children Roo.bootstrap.Component
7518  * Bootstrap Row class (contains columns...)
7519  * 
7520  * @constructor
7521  * Create a new Row
7522  * @param {Object} config The config object
7523  */
7524
7525 Roo.bootstrap.Row = function(config){
7526     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 };
7528
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7530     
7531     getAutoCreate : function(){
7532        return {
7533             cls: 'row clearfix'
7534        };
7535     }
7536     
7537     
7538 });
7539
7540  
7541
7542  /*
7543  * - LGPL
7544  *
7545  * pagination
7546  * 
7547  */
7548
7549 /**
7550  * @class Roo.bootstrap.Pagination
7551  * @extends Roo.bootstrap.Component
7552  * @children Roo.bootstrap.Pagination
7553  * Bootstrap Pagination class
7554  * 
7555  * @cfg {String} size (xs|sm|md|lg|xl)
7556  * @cfg {Boolean} inverse 
7557  * 
7558  * @constructor
7559  * Create a new Pagination
7560  * @param {Object} config The config object
7561  */
7562
7563 Roo.bootstrap.Pagination = function(config){
7564     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 };
7566
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7568     
7569     cls: false,
7570     size: false,
7571     inverse: false,
7572     
7573     getAutoCreate : function(){
7574         var cfg = {
7575             tag: 'ul',
7576                 cls: 'pagination'
7577         };
7578         if (this.inverse) {
7579             cfg.cls += ' inverse';
7580         }
7581         if (this.html) {
7582             cfg.html=this.html;
7583         }
7584         if (this.cls) {
7585             cfg.cls += " " + this.cls;
7586         }
7587         return cfg;
7588     }
7589    
7590 });
7591
7592  
7593
7594  /*
7595  * - LGPL
7596  *
7597  * Pagination item
7598  * 
7599  */
7600
7601
7602 /**
7603  * @class Roo.bootstrap.PaginationItem
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap PaginationItem class
7606  * @cfg {String} html text
7607  * @cfg {String} href the link
7608  * @cfg {Boolean} preventDefault (true | false) default true
7609  * @cfg {Boolean} active (true | false) default false
7610  * @cfg {Boolean} disabled default false
7611  * 
7612  * 
7613  * @constructor
7614  * Create a new PaginationItem
7615  * @param {Object} config The config object
7616  */
7617
7618
7619 Roo.bootstrap.PaginationItem = function(config){
7620     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7621     this.addEvents({
7622         // raw events
7623         /**
7624          * @event click
7625          * The raw click event for the entire grid.
7626          * @param {Roo.EventObject} e
7627          */
7628         "click" : true
7629     });
7630 };
7631
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7633     
7634     href : false,
7635     html : false,
7636     preventDefault: true,
7637     active : false,
7638     cls : false,
7639     disabled: false,
7640     
7641     getAutoCreate : function(){
7642         var cfg= {
7643             tag: 'li',
7644             cn: [
7645                 {
7646                     tag : 'a',
7647                     href : this.href ? this.href : '#',
7648                     html : this.html ? this.html : ''
7649                 }
7650             ]
7651         };
7652         
7653         if(this.cls){
7654             cfg.cls = this.cls;
7655         }
7656         
7657         if(this.disabled){
7658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7659         }
7660         
7661         if(this.active){
7662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7663         }
7664         
7665         return cfg;
7666     },
7667     
7668     initEvents: function() {
7669         
7670         this.el.on('click', this.onClick, this);
7671         
7672     },
7673     onClick : function(e)
7674     {
7675         Roo.log('PaginationItem on click ');
7676         if(this.preventDefault){
7677             e.preventDefault();
7678         }
7679         
7680         if(this.disabled){
7681             return;
7682         }
7683         
7684         this.fireEvent('click', this, e);
7685     }
7686    
7687 });
7688
7689  
7690
7691  /*
7692  * - LGPL
7693  *
7694  * slider
7695  * 
7696  */
7697
7698
7699 /**
7700  * @class Roo.bootstrap.Slider
7701  * @extends Roo.bootstrap.Component
7702  * Bootstrap Slider class
7703  *    
7704  * @constructor
7705  * Create a new Slider
7706  * @param {Object} config The config object
7707  */
7708
7709 Roo.bootstrap.Slider = function(config){
7710     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 };
7712
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7714     
7715     getAutoCreate : function(){
7716         
7717         var cfg = {
7718             tag: 'div',
7719             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7720             cn: [
7721                 {
7722                     tag: 'a',
7723                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7724                 }
7725             ]
7726         };
7727         
7728         return cfg;
7729     }
7730    
7731 });
7732
7733  /*
7734  * Based on:
7735  * Ext JS Library 1.1.1
7736  * Copyright(c) 2006-2007, Ext JS, LLC.
7737  *
7738  * Originally Released Under LGPL - original licence link has changed is not relivant.
7739  *
7740  * Fork - LGPL
7741  * <script type="text/javascript">
7742  */
7743  /**
7744  * @extends Roo.dd.DDProxy
7745  * @class Roo.grid.SplitDragZone
7746  * Support for Column Header resizing
7747  * @constructor
7748  * @param {Object} config
7749  */
7750 // private
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7753     this.grid = grid;
7754     this.view = grid.getView();
7755     this.proxy = this.view.resizeProxy;
7756     Roo.grid.SplitDragZone.superclass.constructor.call(
7757         this,
7758         hd, // ID
7759         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7760         {  // CONFIG
7761             dragElId : Roo.id(this.proxy.dom),
7762             resizeFrame:false
7763         }
7764     );
7765     
7766     this.setHandleElId(Roo.id(hd));
7767     if (hd2 !== false) {
7768         this.setOuterHandleElId(Roo.id(hd2));
7769     }
7770     
7771     this.scroll = false;
7772 };
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774     fly: Roo.Element.fly,
7775
7776     b4StartDrag : function(x, y){
7777         this.view.headersDisabled = true;
7778         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7780         );
7781         this.proxy.setHeight(h);
7782         
7783         // for old system colWidth really stored the actual width?
7784         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785         // which in reality did not work.. - it worked only for fixed sizes
7786         // for resizable we need to use actual sizes.
7787         var w = this.cm.getColumnWidth(this.cellIndex);
7788         if (!this.view.mainWrap) {
7789             // bootstrap.
7790             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7791         }
7792         
7793         
7794         
7795         // this was w-this.grid.minColumnWidth;
7796         // doesnt really make sense? - w = thie curren width or the rendered one?
7797         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798         this.resetConstraints();
7799         this.setXConstraint(minw, 1000);
7800         this.setYConstraint(0, 0);
7801         this.minX = x - minw;
7802         this.maxX = x + 1000;
7803         this.startPos = x;
7804         if (!this.view.mainWrap) { // this is Bootstrap code..
7805             this.getDragEl().style.display='block';
7806         }
7807         
7808         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7809     },
7810
7811
7812     handleMouseDown : function(e){
7813         ev = Roo.EventObject.setEvent(e);
7814         var t = this.fly(ev.getTarget());
7815         if(t.hasClass("x-grid-split")){
7816             this.cellIndex = this.view.getCellIndex(t.dom);
7817             this.split = t.dom;
7818             this.cm = this.grid.colModel;
7819             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7821             }
7822         }
7823     },
7824
7825     endDrag : function(e){
7826         this.view.headersDisabled = false;
7827         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828         var diff = endX - this.startPos;
7829         // 
7830         var w = this.cm.getColumnWidth(this.cellIndex);
7831         if (!this.view.mainWrap) {
7832             w = 0;
7833         }
7834         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835     },
7836
7837     autoOffset : function(){
7838         this.setDelta(0,0);
7839     }
7840 });/*
7841  * Based on:
7842  * Ext JS Library 1.1.1
7843  * Copyright(c) 2006-2007, Ext JS, LLC.
7844  *
7845  * Originally Released Under LGPL - original licence link has changed is not relivant.
7846  *
7847  * Fork - LGPL
7848  * <script type="text/javascript">
7849  */
7850
7851 /**
7852  * @class Roo.grid.AbstractSelectionModel
7853  * @extends Roo.util.Observable
7854  * @abstract
7855  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7856  * implemented by descendant classes.  This class should not be directly instantiated.
7857  * @constructor
7858  */
7859 Roo.grid.AbstractSelectionModel = function(){
7860     this.locked = false;
7861     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 };
7863
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7865     /** @ignore Called by the grid automatically. Do not call directly. */
7866     init : function(grid){
7867         this.grid = grid;
7868         this.initEvents();
7869     },
7870
7871     /**
7872      * Locks the selections.
7873      */
7874     lock : function(){
7875         this.locked = true;
7876     },
7877
7878     /**
7879      * Unlocks the selections.
7880      */
7881     unlock : function(){
7882         this.locked = false;
7883     },
7884
7885     /**
7886      * Returns true if the selections are locked.
7887      * @return {Boolean}
7888      */
7889     isLocked : function(){
7890         return this.locked;
7891     }
7892 });/*
7893  * Based on:
7894  * Ext JS Library 1.1.1
7895  * Copyright(c) 2006-2007, Ext JS, LLC.
7896  *
7897  * Originally Released Under LGPL - original licence link has changed is not relivant.
7898  *
7899  * Fork - LGPL
7900  * <script type="text/javascript">
7901  */
7902 /**
7903  * @extends Roo.grid.AbstractSelectionModel
7904  * @class Roo.grid.RowSelectionModel
7905  * The default SelectionModel used by {@link Roo.grid.Grid}.
7906  * It supports multiple selections and keyboard selection/navigation. 
7907  * @constructor
7908  * @param {Object} config
7909  */
7910 Roo.grid.RowSelectionModel = function(config){
7911     Roo.apply(this, config);
7912     this.selections = new Roo.util.MixedCollection(false, function(o){
7913         return o.id;
7914     });
7915
7916     this.last = false;
7917     this.lastActive = false;
7918
7919     this.addEvents({
7920         /**
7921         * @event selectionchange
7922         * Fires when the selection changes
7923         * @param {SelectionModel} this
7924         */
7925        "selectionchange" : true,
7926        /**
7927         * @event afterselectionchange
7928         * Fires after the selection changes (eg. by key press or clicking)
7929         * @param {SelectionModel} this
7930         */
7931        "afterselectionchange" : true,
7932        /**
7933         * @event beforerowselect
7934         * Fires when a row is selected being selected, return false to cancel.
7935         * @param {SelectionModel} this
7936         * @param {Number} rowIndex The selected index
7937         * @param {Boolean} keepExisting False if other selections will be cleared
7938         */
7939        "beforerowselect" : true,
7940        /**
7941         * @event rowselect
7942         * Fires when a row is selected.
7943         * @param {SelectionModel} this
7944         * @param {Number} rowIndex The selected index
7945         * @param {Roo.data.Record} r The record
7946         */
7947        "rowselect" : true,
7948        /**
7949         * @event rowdeselect
7950         * Fires when a row is deselected.
7951         * @param {SelectionModel} this
7952         * @param {Number} rowIndex The selected index
7953         */
7954         "rowdeselect" : true
7955     });
7956     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957     this.locked = false;
7958 };
7959
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7961     /**
7962      * @cfg {Boolean} singleSelect
7963      * True to allow selection of only one row at a time (defaults to false)
7964      */
7965     singleSelect : false,
7966
7967     // private
7968     initEvents : function(){
7969
7970         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971             this.grid.on("mousedown", this.handleMouseDown, this);
7972         }else{ // allow click to work like normal
7973             this.grid.on("rowclick", this.handleDragableRowClick, this);
7974         }
7975         // bootstrap does not have a view..
7976         var view = this.grid.view ? this.grid.view : this.grid;
7977         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978             "up" : function(e){
7979                 if(!e.shiftKey){
7980                     this.selectPrevious(e.shiftKey);
7981                 }else if(this.last !== false && this.lastActive !== false){
7982                     var last = this.last;
7983                     this.selectRange(this.last,  this.lastActive-1);
7984                     view.focusRow(this.lastActive);
7985                     if(last !== false){
7986                         this.last = last;
7987                     }
7988                 }else{
7989                     this.selectFirstRow();
7990                 }
7991                 this.fireEvent("afterselectionchange", this);
7992             },
7993             "down" : function(e){
7994                 if(!e.shiftKey){
7995                     this.selectNext(e.shiftKey);
7996                 }else if(this.last !== false && this.lastActive !== false){
7997                     var last = this.last;
7998                     this.selectRange(this.last,  this.lastActive+1);
7999                     view.focusRow(this.lastActive);
8000                     if(last !== false){
8001                         this.last = last;
8002                     }
8003                 }else{
8004                     this.selectFirstRow();
8005                 }
8006                 this.fireEvent("afterselectionchange", this);
8007             },
8008             scope: this
8009         });
8010
8011          
8012         view.on("refresh", this.onRefresh, this);
8013         view.on("rowupdated", this.onRowUpdated, this);
8014         view.on("rowremoved", this.onRemove, this);
8015     },
8016
8017     // private
8018     onRefresh : function(){
8019         var ds = this.grid.ds, i, v = this.grid.view;
8020         var s = this.selections;
8021         s.each(function(r){
8022             if((i = ds.indexOfId(r.id)) != -1){
8023                 v.onRowSelect(i);
8024                 s.add(ds.getAt(i)); // updating the selection relate data
8025             }else{
8026                 s.remove(r);
8027             }
8028         });
8029     },
8030
8031     // private
8032     onRemove : function(v, index, r){
8033         this.selections.remove(r);
8034     },
8035
8036     // private
8037     onRowUpdated : function(v, index, r){
8038         if(this.isSelected(r)){
8039             v.onRowSelect(index);
8040         }
8041     },
8042
8043     /**
8044      * Select records.
8045      * @param {Array} records The records to select
8046      * @param {Boolean} keepExisting (optional) True to keep existing selections
8047      */
8048     selectRecords : function(records, keepExisting){
8049         if(!keepExisting){
8050             this.clearSelections();
8051         }
8052         var ds = this.grid.ds;
8053         for(var i = 0, len = records.length; i < len; i++){
8054             this.selectRow(ds.indexOf(records[i]), true);
8055         }
8056     },
8057
8058     /**
8059      * Gets the number of selected rows.
8060      * @return {Number}
8061      */
8062     getCount : function(){
8063         return this.selections.length;
8064     },
8065
8066     /**
8067      * Selects the first row in the grid.
8068      */
8069     selectFirstRow : function(){
8070         this.selectRow(0);
8071     },
8072
8073     /**
8074      * Select the last row.
8075      * @param {Boolean} keepExisting (optional) True to keep existing selections
8076      */
8077     selectLastRow : function(keepExisting){
8078         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8079     },
8080
8081     /**
8082      * Selects the row immediately following the last selected row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectNext : function(keepExisting){
8086         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087             this.selectRow(this.last+1, keepExisting);
8088             var view = this.grid.view ? this.grid.view : this.grid;
8089             view.focusRow(this.last);
8090         }
8091     },
8092
8093     /**
8094      * Selects the row that precedes the last selected row.
8095      * @param {Boolean} keepExisting (optional) True to keep existing selections
8096      */
8097     selectPrevious : function(keepExisting){
8098         if(this.last){
8099             this.selectRow(this.last-1, keepExisting);
8100             var view = this.grid.view ? this.grid.view : this.grid;
8101             view.focusRow(this.last);
8102         }
8103     },
8104
8105     /**
8106      * Returns the selected records
8107      * @return {Array} Array of selected records
8108      */
8109     getSelections : function(){
8110         return [].concat(this.selections.items);
8111     },
8112
8113     /**
8114      * Returns the first selected record.
8115      * @return {Record}
8116      */
8117     getSelected : function(){
8118         return this.selections.itemAt(0);
8119     },
8120
8121
8122     /**
8123      * Clears all selections.
8124      */
8125     clearSelections : function(fast){
8126         if(this.locked) {
8127             return;
8128         }
8129         if(fast !== true){
8130             var ds = this.grid.ds;
8131             var s = this.selections;
8132             s.each(function(r){
8133                 this.deselectRow(ds.indexOfId(r.id));
8134             }, this);
8135             s.clear();
8136         }else{
8137             this.selections.clear();
8138         }
8139         this.last = false;
8140     },
8141
8142
8143     /**
8144      * Selects all rows.
8145      */
8146     selectAll : function(){
8147         if(this.locked) {
8148             return;
8149         }
8150         this.selections.clear();
8151         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152             this.selectRow(i, true);
8153         }
8154     },
8155
8156     /**
8157      * Returns True if there is a selection.
8158      * @return {Boolean}
8159      */
8160     hasSelection : function(){
8161         return this.selections.length > 0;
8162     },
8163
8164     /**
8165      * Returns True if the specified row is selected.
8166      * @param {Number/Record} record The record or index of the record to check
8167      * @return {Boolean}
8168      */
8169     isSelected : function(index){
8170         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171         return (r && this.selections.key(r.id) ? true : false);
8172     },
8173
8174     /**
8175      * Returns True if the specified record id is selected.
8176      * @param {String} id The id of record to check
8177      * @return {Boolean}
8178      */
8179     isIdSelected : function(id){
8180         return (this.selections.key(id) ? true : false);
8181     },
8182
8183     // private
8184     handleMouseDown : function(e, t)
8185     {
8186         var view = this.grid.view ? this.grid.view : this.grid;
8187         var rowIndex;
8188         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189             return;
8190         };
8191         if(e.shiftKey && this.last !== false){
8192             var last = this.last;
8193             this.selectRange(last, rowIndex, e.ctrlKey);
8194             this.last = last; // reset the last
8195             view.focusRow(rowIndex);
8196         }else{
8197             var isSelected = this.isSelected(rowIndex);
8198             if(e.button !== 0 && isSelected){
8199                 view.focusRow(rowIndex);
8200             }else if(e.ctrlKey && isSelected){
8201                 this.deselectRow(rowIndex);
8202             }else if(!isSelected){
8203                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204                 view.focusRow(rowIndex);
8205             }
8206         }
8207         this.fireEvent("afterselectionchange", this);
8208     },
8209     // private
8210     handleDragableRowClick :  function(grid, rowIndex, e) 
8211     {
8212         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213             this.selectRow(rowIndex, false);
8214             var view = this.grid.view ? this.grid.view : this.grid;
8215             view.focusRow(rowIndex);
8216              this.fireEvent("afterselectionchange", this);
8217         }
8218     },
8219     
8220     /**
8221      * Selects multiple rows.
8222      * @param {Array} rows Array of the indexes of the row to select
8223      * @param {Boolean} keepExisting (optional) True to keep existing selections
8224      */
8225     selectRows : function(rows, keepExisting){
8226         if(!keepExisting){
8227             this.clearSelections();
8228         }
8229         for(var i = 0, len = rows.length; i < len; i++){
8230             this.selectRow(rows[i], true);
8231         }
8232     },
8233
8234     /**
8235      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236      * @param {Number} startRow The index of the first row in the range
8237      * @param {Number} endRow The index of the last row in the range
8238      * @param {Boolean} keepExisting (optional) True to retain existing selections
8239      */
8240     selectRange : function(startRow, endRow, keepExisting){
8241         if(this.locked) {
8242             return;
8243         }
8244         if(!keepExisting){
8245             this.clearSelections();
8246         }
8247         if(startRow <= endRow){
8248             for(var i = startRow; i <= endRow; i++){
8249                 this.selectRow(i, true);
8250             }
8251         }else{
8252             for(var i = startRow; i >= endRow; i--){
8253                 this.selectRow(i, true);
8254             }
8255         }
8256     },
8257
8258     /**
8259      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260      * @param {Number} startRow The index of the first row in the range
8261      * @param {Number} endRow The index of the last row in the range
8262      */
8263     deselectRange : function(startRow, endRow, preventViewNotify){
8264         if(this.locked) {
8265             return;
8266         }
8267         for(var i = startRow; i <= endRow; i++){
8268             this.deselectRow(i, preventViewNotify);
8269         }
8270     },
8271
8272     /**
8273      * Selects a row.
8274      * @param {Number} row The index of the row to select
8275      * @param {Boolean} keepExisting (optional) True to keep existing selections
8276      */
8277     selectRow : function(index, keepExisting, preventViewNotify){
8278         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279             return;
8280         }
8281         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282             if(!keepExisting || this.singleSelect){
8283                 this.clearSelections();
8284             }
8285             var r = this.grid.ds.getAt(index);
8286             this.selections.add(r);
8287             this.last = this.lastActive = index;
8288             if(!preventViewNotify){
8289                 var view = this.grid.view ? this.grid.view : this.grid;
8290                 view.onRowSelect(index);
8291             }
8292             this.fireEvent("rowselect", this, index, r);
8293             this.fireEvent("selectionchange", this);
8294         }
8295     },
8296
8297     /**
8298      * Deselects a row.
8299      * @param {Number} row The index of the row to deselect
8300      */
8301     deselectRow : function(index, preventViewNotify){
8302         if(this.locked) {
8303             return;
8304         }
8305         if(this.last == index){
8306             this.last = false;
8307         }
8308         if(this.lastActive == index){
8309             this.lastActive = false;
8310         }
8311         var r = this.grid.ds.getAt(index);
8312         this.selections.remove(r);
8313         if(!preventViewNotify){
8314             var view = this.grid.view ? this.grid.view : this.grid;
8315             view.onRowDeselect(index);
8316         }
8317         this.fireEvent("rowdeselect", this, index);
8318         this.fireEvent("selectionchange", this);
8319     },
8320
8321     // private
8322     restoreLast : function(){
8323         if(this._last){
8324             this.last = this._last;
8325         }
8326     },
8327
8328     // private
8329     acceptsNav : function(row, col, cm){
8330         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8331     },
8332
8333     // private
8334     onEditorKey : function(field, e){
8335         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8336         if(k == e.TAB){
8337             e.stopEvent();
8338             ed.completeEdit();
8339             if(e.shiftKey){
8340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8341             }else{
8342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8343             }
8344         }else if(k == e.ENTER && !e.ctrlKey){
8345             e.stopEvent();
8346             ed.completeEdit();
8347             if(e.shiftKey){
8348                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8349             }else{
8350                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8351             }
8352         }else if(k == e.ESC){
8353             ed.cancelEdit();
8354         }
8355         if(newCell){
8356             g.startEditing(newCell[0], newCell[1]);
8357         }
8358     }
8359 });/*
8360  * Based on:
8361  * Ext JS Library 1.1.1
8362  * Copyright(c) 2006-2007, Ext JS, LLC.
8363  *
8364  * Originally Released Under LGPL - original licence link has changed is not relivant.
8365  *
8366  * Fork - LGPL
8367  * <script type="text/javascript">
8368  */
8369  
8370
8371 /**
8372  * @class Roo.grid.ColumnModel
8373  * @extends Roo.util.Observable
8374  * This is the default implementation of a ColumnModel used by the Grid. It defines
8375  * the columns in the grid.
8376  * <br>Usage:<br>
8377  <pre><code>
8378  var colModel = new Roo.grid.ColumnModel([
8379         {header: "Ticker", width: 60, sortable: true, locked: true},
8380         {header: "Company Name", width: 150, sortable: true},
8381         {header: "Market Cap.", width: 100, sortable: true},
8382         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383         {header: "Employees", width: 100, sortable: true, resizable: false}
8384  ]);
8385  </code></pre>
8386  * <p>
8387  
8388  * The config options listed for this class are options which may appear in each
8389  * individual column definition.
8390  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8391  * @constructor
8392  * @param {Object} config An Array of column config objects. See this class's
8393  * config objects for details.
8394 */
8395 Roo.grid.ColumnModel = function(config){
8396         /**
8397      * The config passed into the constructor
8398      */
8399     this.config = []; //config;
8400     this.lookup = {};
8401
8402     // if no id, create one
8403     // if the column does not have a dataIndex mapping,
8404     // map it to the order it is in the config
8405     for(var i = 0, len = config.length; i < len; i++){
8406         this.addColumn(config[i]);
8407         
8408     }
8409
8410     /**
8411      * The width of columns which have no width specified (defaults to 100)
8412      * @type Number
8413      */
8414     this.defaultWidth = 100;
8415
8416     /**
8417      * Default sortable of columns which have no sortable specified (defaults to false)
8418      * @type Boolean
8419      */
8420     this.defaultSortable = false;
8421
8422     this.addEvents({
8423         /**
8424              * @event widthchange
8425              * Fires when the width of a column changes.
8426              * @param {ColumnModel} this
8427              * @param {Number} columnIndex The column index
8428              * @param {Number} newWidth The new width
8429              */
8430             "widthchange": true,
8431         /**
8432              * @event headerchange
8433              * Fires when the text of a header changes.
8434              * @param {ColumnModel} this
8435              * @param {Number} columnIndex The column index
8436              * @param {Number} newText The new header text
8437              */
8438             "headerchange": true,
8439         /**
8440              * @event hiddenchange
8441              * Fires when a column is hidden or "unhidden".
8442              * @param {ColumnModel} this
8443              * @param {Number} columnIndex The column index
8444              * @param {Boolean} hidden true if hidden, false otherwise
8445              */
8446             "hiddenchange": true,
8447             /**
8448          * @event columnmoved
8449          * Fires when a column is moved.
8450          * @param {ColumnModel} this
8451          * @param {Number} oldIndex
8452          * @param {Number} newIndex
8453          */
8454         "columnmoved" : true,
8455         /**
8456          * @event columlockchange
8457          * Fires when a column's locked state is changed
8458          * @param {ColumnModel} this
8459          * @param {Number} colIndex
8460          * @param {Boolean} locked true if locked
8461          */
8462         "columnlockchange" : true
8463     });
8464     Roo.grid.ColumnModel.superclass.constructor.call(this);
8465 };
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8467     /**
8468      * @cfg {String} header The header text to display in the Grid view.
8469      */
8470         /**
8471      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472      */
8473         /**
8474      * @cfg {String} smHeader Header at Bootsrap Small width
8475      */
8476         /**
8477      * @cfg {String} mdHeader Header at Bootsrap Medium width
8478      */
8479         /**
8480      * @cfg {String} lgHeader Header at Bootsrap Large width
8481      */
8482         /**
8483      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484      */
8485     /**
8486      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8487      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488      * specified, the column's index is used as an index into the Record's data Array.
8489      */
8490     /**
8491      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8492      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493      */
8494     /**
8495      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8496      * Defaults to the value of the {@link #defaultSortable} property.
8497      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498      */
8499     /**
8500      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8501      */
8502     /**
8503      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8504      */
8505     /**
8506      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8507      */
8508     /**
8509      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8510      */
8511     /**
8512      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8513      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516      */
8517        /**
8518      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8519      */
8520     /**
8521      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8522      */
8523     /**
8524      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8525      */
8526     /**
8527      * @cfg {String} cursor (Optional)
8528      */
8529     /**
8530      * @cfg {String} tooltip (Optional)
8531      */
8532     /**
8533      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8534      */
8535     /**
8536      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8537      */
8538     /**
8539      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8540      */
8541     /**
8542      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8543      */
8544         /**
8545      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8546      */
8547     /**
8548      * Returns the id of the column at the specified index.
8549      * @param {Number} index The column index
8550      * @return {String} the id
8551      */
8552     getColumnId : function(index){
8553         return this.config[index].id;
8554     },
8555
8556     /**
8557      * Returns the column for a specified id.
8558      * @param {String} id The column id
8559      * @return {Object} the column
8560      */
8561     getColumnById : function(id){
8562         return this.lookup[id];
8563     },
8564
8565     
8566     /**
8567      * Returns the column Object for a specified dataIndex.
8568      * @param {String} dataIndex The column dataIndex
8569      * @return {Object|Boolean} the column or false if not found
8570      */
8571     getColumnByDataIndex: function(dataIndex){
8572         var index = this.findColumnIndex(dataIndex);
8573         return index > -1 ? this.config[index] : false;
8574     },
8575     
8576     /**
8577      * Returns the index for a specified column id.
8578      * @param {String} id The column id
8579      * @return {Number} the index, or -1 if not found
8580      */
8581     getIndexById : function(id){
8582         for(var i = 0, len = this.config.length; i < len; i++){
8583             if(this.config[i].id == id){
8584                 return i;
8585             }
8586         }
8587         return -1;
8588     },
8589     
8590     /**
8591      * Returns the index for a specified column dataIndex.
8592      * @param {String} dataIndex The column dataIndex
8593      * @return {Number} the index, or -1 if not found
8594      */
8595     
8596     findColumnIndex : function(dataIndex){
8597         for(var i = 0, len = this.config.length; i < len; i++){
8598             if(this.config[i].dataIndex == dataIndex){
8599                 return i;
8600             }
8601         }
8602         return -1;
8603     },
8604     
8605     
8606     moveColumn : function(oldIndex, newIndex){
8607         var c = this.config[oldIndex];
8608         this.config.splice(oldIndex, 1);
8609         this.config.splice(newIndex, 0, c);
8610         this.dataMap = null;
8611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612     },
8613
8614     isLocked : function(colIndex){
8615         return this.config[colIndex].locked === true;
8616     },
8617
8618     setLocked : function(colIndex, value, suppressEvent){
8619         if(this.isLocked(colIndex) == value){
8620             return;
8621         }
8622         this.config[colIndex].locked = value;
8623         if(!suppressEvent){
8624             this.fireEvent("columnlockchange", this, colIndex, value);
8625         }
8626     },
8627
8628     getTotalLockedWidth : function(){
8629         var totalWidth = 0;
8630         for(var i = 0; i < this.config.length; i++){
8631             if(this.isLocked(i) && !this.isHidden(i)){
8632                 this.totalWidth += this.getColumnWidth(i);
8633             }
8634         }
8635         return totalWidth;
8636     },
8637
8638     getLockedCount : function(){
8639         for(var i = 0, len = this.config.length; i < len; i++){
8640             if(!this.isLocked(i)){
8641                 return i;
8642             }
8643         }
8644         
8645         return this.config.length;
8646     },
8647
8648     /**
8649      * Returns the number of columns.
8650      * @return {Number}
8651      */
8652     getColumnCount : function(visibleOnly){
8653         if(visibleOnly === true){
8654             var c = 0;
8655             for(var i = 0, len = this.config.length; i < len; i++){
8656                 if(!this.isHidden(i)){
8657                     c++;
8658                 }
8659             }
8660             return c;
8661         }
8662         return this.config.length;
8663     },
8664
8665     /**
8666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667      * @param {Function} fn
8668      * @param {Object} scope (optional)
8669      * @return {Array} result
8670      */
8671     getColumnsBy : function(fn, scope){
8672         var r = [];
8673         for(var i = 0, len = this.config.length; i < len; i++){
8674             var c = this.config[i];
8675             if(fn.call(scope||this, c, i) === true){
8676                 r[r.length] = c;
8677             }
8678         }
8679         return r;
8680     },
8681
8682     /**
8683      * Returns true if the specified column is sortable.
8684      * @param {Number} col The column index
8685      * @return {Boolean}
8686      */
8687     isSortable : function(col){
8688         if(typeof this.config[col].sortable == "undefined"){
8689             return this.defaultSortable;
8690         }
8691         return this.config[col].sortable;
8692     },
8693
8694     /**
8695      * Returns the rendering (formatting) function defined for the column.
8696      * @param {Number} col The column index.
8697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8698      */
8699     getRenderer : function(col){
8700         if(!this.config[col].renderer){
8701             return Roo.grid.ColumnModel.defaultRenderer;
8702         }
8703         return this.config[col].renderer;
8704     },
8705
8706     /**
8707      * Sets the rendering (formatting) function for a column.
8708      * @param {Number} col The column index
8709      * @param {Function} fn The function to use to process the cell's raw data
8710      * to return HTML markup for the grid view. The render function is called with
8711      * the following parameters:<ul>
8712      * <li>Data value.</li>
8713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714      * <li>css A CSS style string to apply to the table cell.</li>
8715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717      * <li>Row index</li>
8718      * <li>Column index</li>
8719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8720      */
8721     setRenderer : function(col, fn){
8722         this.config[col].renderer = fn;
8723     },
8724
8725     /**
8726      * Returns the width for the specified column.
8727      * @param {Number} col The column index
8728      * @param (optional) {String} gridSize bootstrap width size.
8729      * @return {Number}
8730      */
8731     getColumnWidth : function(col, gridSize)
8732         {
8733                 var cfg = this.config[col];
8734                 
8735                 if (typeof(gridSize) == 'undefined') {
8736                         return cfg.width * 1 || this.defaultWidth;
8737                 }
8738                 if (gridSize === false) { // if we set it..
8739                         return cfg.width || false;
8740                 }
8741                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8742                 
8743                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745                                 continue;
8746                         }
8747                         return cfg[ sizes[i] ];
8748                 }
8749                 return 1;
8750                 
8751     },
8752
8753     /**
8754      * Sets the width for a column.
8755      * @param {Number} col The column index
8756      * @param {Number} width The new width
8757      */
8758     setColumnWidth : function(col, width, suppressEvent){
8759         this.config[col].width = width;
8760         this.totalWidth = null;
8761         if(!suppressEvent){
8762              this.fireEvent("widthchange", this, col, width);
8763         }
8764     },
8765
8766     /**
8767      * Returns the total width of all columns.
8768      * @param {Boolean} includeHidden True to include hidden column widths
8769      * @return {Number}
8770      */
8771     getTotalWidth : function(includeHidden){
8772         if(!this.totalWidth){
8773             this.totalWidth = 0;
8774             for(var i = 0, len = this.config.length; i < len; i++){
8775                 if(includeHidden || !this.isHidden(i)){
8776                     this.totalWidth += this.getColumnWidth(i);
8777                 }
8778             }
8779         }
8780         return this.totalWidth;
8781     },
8782
8783     /**
8784      * Returns the header for the specified column.
8785      * @param {Number} col The column index
8786      * @return {String}
8787      */
8788     getColumnHeader : function(col){
8789         return this.config[col].header;
8790     },
8791
8792     /**
8793      * Sets the header for a column.
8794      * @param {Number} col The column index
8795      * @param {String} header The new header
8796      */
8797     setColumnHeader : function(col, header){
8798         this.config[col].header = header;
8799         this.fireEvent("headerchange", this, col, header);
8800     },
8801
8802     /**
8803      * Returns the tooltip for the specified column.
8804      * @param {Number} col The column index
8805      * @return {String}
8806      */
8807     getColumnTooltip : function(col){
8808             return this.config[col].tooltip;
8809     },
8810     /**
8811      * Sets the tooltip for a column.
8812      * @param {Number} col The column index
8813      * @param {String} tooltip The new tooltip
8814      */
8815     setColumnTooltip : function(col, tooltip){
8816             this.config[col].tooltip = tooltip;
8817     },
8818
8819     /**
8820      * Returns the dataIndex for the specified column.
8821      * @param {Number} col The column index
8822      * @return {Number}
8823      */
8824     getDataIndex : function(col){
8825         return this.config[col].dataIndex;
8826     },
8827
8828     /**
8829      * Sets the dataIndex for a column.
8830      * @param {Number} col The column index
8831      * @param {Number} dataIndex The new dataIndex
8832      */
8833     setDataIndex : function(col, dataIndex){
8834         this.config[col].dataIndex = dataIndex;
8835     },
8836
8837     
8838     
8839     /**
8840      * Returns true if the cell is editable.
8841      * @param {Number} colIndex The column index
8842      * @param {Number} rowIndex The row index - this is nto actually used..?
8843      * @return {Boolean}
8844      */
8845     isCellEditable : function(colIndex, rowIndex){
8846         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8847     },
8848
8849     /**
8850      * Returns the editor defined for the cell/column.
8851      * return false or null to disable editing.
8852      * @param {Number} colIndex The column index
8853      * @param {Number} rowIndex The row index
8854      * @return {Object}
8855      */
8856     getCellEditor : function(colIndex, rowIndex){
8857         return this.config[colIndex].editor;
8858     },
8859
8860     /**
8861      * Sets if a column is editable.
8862      * @param {Number} col The column index
8863      * @param {Boolean} editable True if the column is editable
8864      */
8865     setEditable : function(col, editable){
8866         this.config[col].editable = editable;
8867     },
8868
8869
8870     /**
8871      * Returns true if the column is hidden.
8872      * @param {Number} colIndex The column index
8873      * @return {Boolean}
8874      */
8875     isHidden : function(colIndex){
8876         return this.config[colIndex].hidden;
8877     },
8878
8879
8880     /**
8881      * Returns true if the column width cannot be changed
8882      */
8883     isFixed : function(colIndex){
8884         return this.config[colIndex].fixed;
8885     },
8886
8887     /**
8888      * Returns true if the column can be resized
8889      * @return {Boolean}
8890      */
8891     isResizable : function(colIndex){
8892         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893     },
8894     /**
8895      * Sets if a column is hidden.
8896      * @param {Number} colIndex The column index
8897      * @param {Boolean} hidden True if the column is hidden
8898      */
8899     setHidden : function(colIndex, hidden){
8900         this.config[colIndex].hidden = hidden;
8901         this.totalWidth = null;
8902         this.fireEvent("hiddenchange", this, colIndex, hidden);
8903     },
8904
8905     /**
8906      * Sets the editor for a column.
8907      * @param {Number} col The column index
8908      * @param {Object} editor The editor object
8909      */
8910     setEditor : function(col, editor){
8911         this.config[col].editor = editor;
8912     },
8913     /**
8914      * Add a column (experimental...) - defaults to adding to the end..
8915      * @param {Object} config 
8916     */
8917     addColumn : function(c)
8918     {
8919     
8920         var i = this.config.length;
8921         this.config[i] = c;
8922         
8923         if(typeof c.dataIndex == "undefined"){
8924             c.dataIndex = i;
8925         }
8926         if(typeof c.renderer == "string"){
8927             c.renderer = Roo.util.Format[c.renderer];
8928         }
8929         if(typeof c.id == "undefined"){
8930             c.id = Roo.id();
8931         }
8932         if(c.editor && c.editor.xtype){
8933             c.editor  = Roo.factory(c.editor, Roo.grid);
8934         }
8935         if(c.editor && c.editor.isFormField){
8936             c.editor = new Roo.grid.GridEditor(c.editor);
8937         }
8938         this.lookup[c.id] = c;
8939     }
8940     
8941 });
8942
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8944 {
8945     if(typeof value == "object") {
8946         return value;
8947     }
8948         if(typeof value == "string" && value.length < 1){
8949             return "&#160;";
8950         }
8951     
8952         return String.format("{0}", value);
8953 };
8954
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 /*
8958  * Based on:
8959  * Ext JS Library 1.1.1
8960  * Copyright(c) 2006-2007, Ext JS, LLC.
8961  *
8962  * Originally Released Under LGPL - original licence link has changed is not relivant.
8963  *
8964  * Fork - LGPL
8965  * <script type="text/javascript">
8966  */
8967  
8968 /**
8969  * @class Roo.LoadMask
8970  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8971  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8973  * element's UpdateManager load indicator and will be destroyed after the initial load.
8974  * @constructor
8975  * Create a new LoadMask
8976  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977  * @param {Object} config The config object
8978  */
8979 Roo.LoadMask = function(el, config){
8980     this.el = Roo.get(el);
8981     Roo.apply(this, config);
8982     if(this.store){
8983         this.store.on('beforeload', this.onBeforeLoad, this);
8984         this.store.on('load', this.onLoad, this);
8985         this.store.on('loadexception', this.onLoadException, this);
8986         this.removeMask = false;
8987     }else{
8988         var um = this.el.getUpdateManager();
8989         um.showLoadIndicator = false; // disable the default indicator
8990         um.on('beforeupdate', this.onBeforeLoad, this);
8991         um.on('update', this.onLoad, this);
8992         um.on('failure', this.onLoad, this);
8993         this.removeMask = true;
8994     }
8995 };
8996
8997 Roo.LoadMask.prototype = {
8998     /**
8999      * @cfg {Boolean} removeMask
9000      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9002      */
9003     removeMask : false,
9004     /**
9005      * @cfg {String} msg
9006      * The text to display in a centered loading message box (defaults to 'Loading...')
9007      */
9008     msg : 'Loading...',
9009     /**
9010      * @cfg {String} msgCls
9011      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9012      */
9013     msgCls : 'x-mask-loading',
9014
9015     /**
9016      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9017      * @type Boolean
9018      */
9019     disabled: false,
9020
9021     /**
9022      * Disables the mask to prevent it from being displayed
9023      */
9024     disable : function(){
9025        this.disabled = true;
9026     },
9027
9028     /**
9029      * Enables the mask so that it can be displayed
9030      */
9031     enable : function(){
9032         this.disabled = false;
9033     },
9034     
9035     onLoadException : function()
9036     {
9037         Roo.log(arguments);
9038         
9039         if (typeof(arguments[3]) != 'undefined') {
9040             Roo.MessageBox.alert("Error loading",arguments[3]);
9041         } 
9042         /*
9043         try {
9044             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9046             }   
9047         } catch(e) {
9048             
9049         }
9050         */
9051     
9052         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9053     },
9054     // private
9055     onLoad : function()
9056     {
9057         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9058     },
9059
9060     // private
9061     onBeforeLoad : function(){
9062         if(!this.disabled){
9063             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9064         }
9065     },
9066
9067     // private
9068     destroy : function(){
9069         if(this.store){
9070             this.store.un('beforeload', this.onBeforeLoad, this);
9071             this.store.un('load', this.onLoad, this);
9072             this.store.un('loadexception', this.onLoadException, this);
9073         }else{
9074             var um = this.el.getUpdateManager();
9075             um.un('beforeupdate', this.onBeforeLoad, this);
9076             um.un('update', this.onLoad, this);
9077             um.un('failure', this.onLoad, this);
9078         }
9079     }
9080 };/**
9081  * @class Roo.bootstrap.Table
9082  * @licence LGBL
9083  * @extends Roo.bootstrap.Component
9084  * @children Roo.bootstrap.TableBody
9085  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9086  * Similar to Roo.grid.Grid
9087  * <pre><code>
9088  var table = Roo.factory({
9089     xtype : 'Table',
9090     xns : Roo.bootstrap,
9091     autoSizeColumns: true,
9092     
9093     
9094     store : {
9095         xtype : 'Store',
9096         xns : Roo.data,
9097         remoteSort : true,
9098         sortInfo : { direction : 'ASC', field: 'name' },
9099         proxy : {
9100            xtype : 'HttpProxy',
9101            xns : Roo.data,
9102            method : 'GET',
9103            url : 'https://example.com/some.data.url.json'
9104         },
9105         reader : {
9106            xtype : 'JsonReader',
9107            xns : Roo.data,
9108            fields : [ 'id', 'name', whatever' ],
9109            id : 'id',
9110            root : 'data'
9111         }
9112     },
9113     cm : [
9114         {
9115             xtype : 'ColumnModel',
9116             xns : Roo.grid,
9117             align : 'center',
9118             cursor : 'pointer',
9119             dataIndex : 'is_in_group',
9120             header : "Name",
9121             sortable : true,
9122             renderer : function(v, x , r) {  
9123             
9124                 return String.format("{0}", v)
9125             }
9126             width : 3
9127         } // more columns..
9128     ],
9129     selModel : {
9130         xtype : 'RowSelectionModel',
9131         xns : Roo.bootstrap.Table
9132         // you can add listeners to catch selection change here....
9133     }
9134      
9135
9136  });
9137  // set any options
9138  grid.render(Roo.get("some-div"));
9139 </code></pre>
9140
9141 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9142
9143
9144
9145  *
9146  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147  * @cfg {Roo.data.Store} store The data store to use
9148  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9149  * 
9150  * @cfg {String} cls table class
9151  *
9152  *
9153  * @cfg {string} empty_results  Text to display for no results 
9154  * @cfg {boolean} striped Should the rows be alternative striped
9155  * @cfg {boolean} bordered Add borders to the table
9156  * @cfg {boolean} hover Add hover highlighting
9157  * @cfg {boolean} condensed Format condensed
9158  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9159  *                also adds table-responsive (see bootstrap docs for details)
9160  * @cfg {Boolean} loadMask (true|false) default false
9161  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163  * @cfg {Boolean} rowSelection (true|false) default false
9164  * @cfg {Boolean} cellSelection (true|false) default false
9165  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9166  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9167  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9168  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9169  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     headerShow : true,
9346     enableColumnResize: true,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         
9509         
9510         var cm = this.cm, styles = [];
9511         this.CSS.removeStyleSheet(this.id + '-cssrules');
9512         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9513         // we can honour xs/sm/md/xl  as widths...
9514         // we first have to decide what widht we are currently at...
9515         var sz = Roo.getGridSize();
9516         
9517         var total = 0;
9518         var last = -1;
9519         var cols = []; // visable cols.
9520         var total_abs = 0;
9521         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9522             var w = cm.getColumnWidth(i, false);
9523             if(cm.isHidden(i)){
9524                 cols.push( { rel : false, abs : 0 });
9525                 continue;
9526             }
9527             if (w !== false) {
9528                 cols.push( { rel : false, abs : w });
9529                 total_abs += w;
9530                 last = i; // not really..
9531                 continue;
9532             }
9533             var w = cm.getColumnWidth(i, sz);
9534             if (w > 0) {
9535                 last = i
9536             }
9537             total += w;
9538             cols.push( { rel : w, abs : false });
9539         }
9540         
9541         var avail = this.bodyEl.dom.clientWidth - total_abs;
9542         
9543         var unitWidth = Math.floor(avail / total);
9544         var rem = avail - (unitWidth * total);
9545         
9546         var hidden, width, pos = 0 , splithide , left;
9547         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9548             
9549             hidden = 'display:none;';
9550             left = '';
9551             width  = 'width:0px;';
9552             splithide = '';
9553             if(!cm.isHidden(i)){
9554                 hidden = '';
9555                 
9556                 
9557                 // we can honour xs/sm/md/xl ?
9558                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9559                 if (w===0) {
9560                     hidden = 'display:none;';
9561                 }
9562                 // width should return a small number...
9563                 if (i == last) {
9564                     w+=rem; // add the remaining with..
9565                 }
9566                 pos += w;
9567                 left = "left:" + (pos -4) + "px;";
9568                 width = "width:" + w+ "px;";
9569                 
9570             }
9571             if (this.responsive) {
9572                 width = '';
9573                 left = '';
9574                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9575                 splithide = 'display: none;';
9576             }
9577             
9578             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9579             if (this.headEl) {
9580                 if (i == last) {
9581                     splithide = 'display:none;';
9582                 }
9583                 
9584                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9585                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9586                             // this is the popover version..
9587                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9588                 );
9589             }
9590             
9591         }
9592         //Roo.log(styles.join(''));
9593         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9594         
9595     },
9596     
9597     
9598     
9599     onContextMenu : function(e, t)
9600     {
9601         this.processEvent("contextmenu", e);
9602     },
9603     
9604     processEvent : function(name, e)
9605     {
9606         if (name != 'touchstart' ) {
9607             this.fireEvent(name, e);    
9608         }
9609         
9610         var t = e.getTarget();
9611         
9612         var cell = Roo.get(t);
9613         
9614         if(!cell){
9615             return;
9616         }
9617         
9618         if(cell.findParent('tfoot', false, true)){
9619             return;
9620         }
9621         
9622         if(cell.findParent('thead', false, true)){
9623             
9624             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9625                 cell = Roo.get(t).findParent('th', false, true);
9626                 if (!cell) {
9627                     Roo.log("failed to find th in thead?");
9628                     Roo.log(e.getTarget());
9629                     return;
9630                 }
9631             }
9632             
9633             var cellIndex = cell.dom.cellIndex;
9634             
9635             var ename = name == 'touchstart' ? 'click' : name;
9636             this.fireEvent("header" + ename, this, cellIndex, e);
9637             
9638             return;
9639         }
9640         
9641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9642             cell = Roo.get(t).findParent('td', false, true);
9643             if (!cell) {
9644                 Roo.log("failed to find th in tbody?");
9645                 Roo.log(e.getTarget());
9646                 return;
9647             }
9648         }
9649         
9650         var row = cell.findParent('tr', false, true);
9651         var cellIndex = cell.dom.cellIndex;
9652         var rowIndex = row.dom.rowIndex - 1;
9653         
9654         if(row !== false){
9655             
9656             this.fireEvent("row" + name, this, rowIndex, e);
9657             
9658             if(cell !== false){
9659             
9660                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9661             }
9662         }
9663         
9664     },
9665     
9666     onMouseover : function(e, el)
9667     {
9668         var cell = Roo.get(el);
9669         
9670         if(!cell){
9671             return;
9672         }
9673         
9674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9675             cell = cell.findParent('td', false, true);
9676         }
9677         
9678         var row = cell.findParent('tr', false, true);
9679         var cellIndex = cell.dom.cellIndex;
9680         var rowIndex = row.dom.rowIndex - 1; // start from 0
9681         
9682         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9683         
9684     },
9685     
9686     onMouseout : function(e, el)
9687     {
9688         var cell = Roo.get(el);
9689         
9690         if(!cell){
9691             return;
9692         }
9693         
9694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695             cell = cell.findParent('td', false, true);
9696         }
9697         
9698         var row = cell.findParent('tr', false, true);
9699         var cellIndex = cell.dom.cellIndex;
9700         var rowIndex = row.dom.rowIndex - 1; // start from 0
9701         
9702         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9703         
9704     },
9705     
9706     onClick : function(e, el)
9707     {
9708         var cell = Roo.get(el);
9709         
9710         if(!cell || (!this.cellSelection && !this.rowSelection)){
9711             return;
9712         }
9713         
9714         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715             cell = cell.findParent('td', false, true);
9716         }
9717         
9718         if(!cell || typeof(cell) == 'undefined'){
9719             return;
9720         }
9721         
9722         var row = cell.findParent('tr', false, true);
9723         
9724         if(!row || typeof(row) == 'undefined'){
9725             return;
9726         }
9727         
9728         var cellIndex = cell.dom.cellIndex;
9729         var rowIndex = this.getRowIndex(row);
9730         
9731         // why??? - should these not be based on SelectionModel?
9732         //if(this.cellSelection){
9733             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9734         //}
9735         
9736         //if(this.rowSelection){
9737             this.fireEvent('rowclick', this, row, rowIndex, e);
9738         //}
9739          
9740     },
9741         
9742     onDblClick : function(e,el)
9743     {
9744         var cell = Roo.get(el);
9745         
9746         if(!cell || (!this.cellSelection && !this.rowSelection)){
9747             return;
9748         }
9749         
9750         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9751             cell = cell.findParent('td', false, true);
9752         }
9753         
9754         if(!cell || typeof(cell) == 'undefined'){
9755             return;
9756         }
9757         
9758         var row = cell.findParent('tr', false, true);
9759         
9760         if(!row || typeof(row) == 'undefined'){
9761             return;
9762         }
9763         
9764         var cellIndex = cell.dom.cellIndex;
9765         var rowIndex = this.getRowIndex(row);
9766         
9767         if(this.cellSelection){
9768             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9769         }
9770         
9771         if(this.rowSelection){
9772             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9773         }
9774     },
9775     findRowIndex : function(el)
9776     {
9777         var cell = Roo.get(el);
9778         if(!cell) {
9779             return false;
9780         }
9781         var row = cell.findParent('tr', false, true);
9782         
9783         if(!row || typeof(row) == 'undefined'){
9784             return false;
9785         }
9786         return this.getRowIndex(row);
9787     },
9788     sort : function(e,el)
9789     {
9790         var col = Roo.get(el);
9791         
9792         if(!col.hasClass('sortable')){
9793             return;
9794         }
9795         
9796         var sort = col.attr('sort');
9797         var dir = 'ASC';
9798         
9799         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9800             dir = 'DESC';
9801         }
9802         
9803         this.store.sortInfo = {field : sort, direction : dir};
9804         
9805         if (this.footer) {
9806             Roo.log("calling footer first");
9807             this.footer.onClick('first');
9808         } else {
9809         
9810             this.store.load({ params : { start : 0 } });
9811         }
9812     },
9813     
9814     renderHeader : function()
9815     {
9816         var header = {
9817             tag: 'thead',
9818             cn : []
9819         };
9820         
9821         var cm = this.cm;
9822         this.totalWidth = 0;
9823         
9824         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9825             
9826             var config = cm.config[i];
9827             
9828             var c = {
9829                 tag: 'th',
9830                 cls : 'x-hcol-' + i,
9831                 style : '',
9832                 
9833                 html: cm.getColumnHeader(i)
9834             };
9835             
9836             var tooltip = cm.getColumnTooltip(i);
9837             if (tooltip) {
9838                 c.tooltip = tooltip;
9839             }
9840             
9841             
9842             var hh = '';
9843             
9844             if(typeof(config.sortable) != 'undefined' && config.sortable){
9845                 c.cls += ' sortable';
9846                 c.html = '<i class="fa"></i>' + c.html;
9847             }
9848             
9849             // could use BS4 hidden-..-down 
9850             
9851             if(typeof(config.lgHeader) != 'undefined'){
9852                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9853             }
9854             
9855             if(typeof(config.mdHeader) != 'undefined'){
9856                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9857             }
9858             
9859             if(typeof(config.smHeader) != 'undefined'){
9860                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9861             }
9862             
9863             if(typeof(config.xsHeader) != 'undefined'){
9864                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9865             }
9866             
9867             if(hh.length){
9868                 c.html = hh;
9869             }
9870             
9871             if(typeof(config.tooltip) != 'undefined'){
9872                 c.tooltip = config.tooltip;
9873             }
9874             
9875             if(typeof(config.colspan) != 'undefined'){
9876                 c.colspan = config.colspan;
9877             }
9878             
9879             // hidden is handled by CSS now
9880             
9881             if(typeof(config.dataIndex) != 'undefined'){
9882                 c.sort = config.dataIndex;
9883             }
9884             
9885            
9886             
9887             if(typeof(config.align) != 'undefined' && config.align.length){
9888                 c.style += ' text-align:' + config.align + ';';
9889             }
9890             
9891             /* width is done in CSS
9892              *if(typeof(config.width) != 'undefined'){
9893                 c.style += ' width:' + config.width + 'px;';
9894                 this.totalWidth += config.width;
9895             } else {
9896                 this.totalWidth += 100; // assume minimum of 100 per column?
9897             }
9898             */
9899             
9900             if(typeof(config.cls) != 'undefined'){
9901                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9902             }
9903             // this is the bit that doesnt reall work at all...
9904             
9905             if (this.responsive) {
9906                  
9907             
9908                 ['xs','sm','md','lg'].map(function(size){
9909                     
9910                     if(typeof(config[size]) == 'undefined'){
9911                         return;
9912                     }
9913                      
9914                     if (!config[size]) { // 0 = hidden
9915                         // BS 4 '0' is treated as hide that column and below.
9916                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9917                         return;
9918                     }
9919                     
9920                     c.cls += ' col-' + size + '-' + config[size] + (
9921                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9922                     );
9923                     
9924                     
9925                 });
9926             }
9927             // at the end?
9928             
9929             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9930             
9931             
9932             
9933             
9934             header.cn.push(c)
9935         }
9936         
9937         return header;
9938     },
9939     
9940     renderBody : function()
9941     {
9942         var body = {
9943             tag: 'tbody',
9944             cn : [
9945                 {
9946                     tag: 'tr',
9947                     cn : [
9948                         {
9949                             tag : 'td',
9950                             colspan :  this.cm.getColumnCount()
9951                         }
9952                     ]
9953                 }
9954             ]
9955         };
9956         
9957         return body;
9958     },
9959     
9960     renderFooter : function()
9961     {
9962         var footer = {
9963             tag: 'tfoot',
9964             cn : [
9965                 {
9966                     tag: 'tr',
9967                     cn : [
9968                         {
9969                             tag : 'td',
9970                             colspan :  this.cm.getColumnCount()
9971                         }
9972                     ]
9973                 }
9974             ]
9975         };
9976         
9977         return footer;
9978     },
9979     
9980     
9981     
9982     onLoad : function()
9983     {
9984 //        Roo.log('ds onload');
9985         this.clear();
9986         
9987         var _this = this;
9988         var cm = this.cm;
9989         var ds = this.store;
9990         
9991         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9992             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9993             if (_this.store.sortInfo) {
9994                     
9995                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9996                     e.select('i', true).addClass(['fa-arrow-up']);
9997                 }
9998                 
9999                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10000                     e.select('i', true).addClass(['fa-arrow-down']);
10001                 }
10002             }
10003         });
10004         
10005         var tbody =  this.bodyEl;
10006               
10007         if(ds.getCount() > 0){
10008             ds.data.each(function(d,rowIndex){
10009                 var row =  this.renderRow(cm, ds, rowIndex);
10010                 
10011                 tbody.createChild(row);
10012                 
10013                 var _this = this;
10014                 
10015                 if(row.cellObjects.length){
10016                     Roo.each(row.cellObjects, function(r){
10017                         _this.renderCellObject(r);
10018                     })
10019                 }
10020                 
10021             }, this);
10022         } else if (this.empty_results.length) {
10023             this.el.mask(this.empty_results, 'no-spinner');
10024         }
10025         
10026         var tfoot = this.el.select('tfoot', true).first();
10027         
10028         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10029             
10030             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10031             
10032             var total = this.ds.getTotalCount();
10033             
10034             if(this.footer.pageSize < total){
10035                 this.mainFoot.show();
10036             }
10037         }
10038         
10039         Roo.each(this.el.select('tbody td', true).elements, function(e){
10040             e.on('mouseover', _this.onMouseover, _this);
10041         });
10042         
10043         Roo.each(this.el.select('tbody td', true).elements, function(e){
10044             e.on('mouseout', _this.onMouseout, _this);
10045         });
10046         this.fireEvent('rowsrendered', this);
10047         
10048         this.autoSize();
10049         
10050         this.initCSS(); /// resize cols
10051
10052         
10053     },
10054     
10055     
10056     onUpdate : function(ds,record)
10057     {
10058         this.refreshRow(record);
10059         this.autoSize();
10060     },
10061     
10062     onRemove : function(ds, record, index, isUpdate){
10063         if(isUpdate !== true){
10064             this.fireEvent("beforerowremoved", this, index, record);
10065         }
10066         var bt = this.bodyEl.dom;
10067         
10068         var rows = this.el.select('tbody > tr', true).elements;
10069         
10070         if(typeof(rows[index]) != 'undefined'){
10071             bt.removeChild(rows[index].dom);
10072         }
10073         
10074 //        if(bt.rows[index]){
10075 //            bt.removeChild(bt.rows[index]);
10076 //        }
10077         
10078         if(isUpdate !== true){
10079             //this.stripeRows(index);
10080             //this.syncRowHeights(index, index);
10081             //this.layout();
10082             this.fireEvent("rowremoved", this, index, record);
10083         }
10084     },
10085     
10086     onAdd : function(ds, records, rowIndex)
10087     {
10088         //Roo.log('on Add called');
10089         // - note this does not handle multiple adding very well..
10090         var bt = this.bodyEl.dom;
10091         for (var i =0 ; i < records.length;i++) {
10092             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10093             //Roo.log(records[i]);
10094             //Roo.log(this.store.getAt(rowIndex+i));
10095             this.insertRow(this.store, rowIndex + i, false);
10096             return;
10097         }
10098         
10099     },
10100     
10101     
10102     refreshRow : function(record){
10103         var ds = this.store, index;
10104         if(typeof record == 'number'){
10105             index = record;
10106             record = ds.getAt(index);
10107         }else{
10108             index = ds.indexOf(record);
10109             if (index < 0) {
10110                 return; // should not happen - but seems to 
10111             }
10112         }
10113         this.insertRow(ds, index, true);
10114         this.autoSize();
10115         this.onRemove(ds, record, index+1, true);
10116         this.autoSize();
10117         //this.syncRowHeights(index, index);
10118         //this.layout();
10119         this.fireEvent("rowupdated", this, index, record);
10120     },
10121     // private - called by RowSelection
10122     onRowSelect : function(rowIndex){
10123         var row = this.getRowDom(rowIndex);
10124         row.addClass(['bg-info','info']);
10125     },
10126     // private - called by RowSelection
10127     onRowDeselect : function(rowIndex)
10128     {
10129         if (rowIndex < 0) {
10130             return;
10131         }
10132         var row = this.getRowDom(rowIndex);
10133         row.removeClass(['bg-info','info']);
10134     },
10135       /**
10136      * Focuses the specified row.
10137      * @param {Number} row The row index
10138      */
10139     focusRow : function(row)
10140     {
10141         //Roo.log('GridView.focusRow');
10142         var x = this.bodyEl.dom.scrollLeft;
10143         this.focusCell(row, 0, false);
10144         this.bodyEl.dom.scrollLeft = x;
10145
10146     },
10147      /**
10148      * Focuses the specified cell.
10149      * @param {Number} row The row index
10150      * @param {Number} col The column index
10151      * @param {Boolean} hscroll false to disable horizontal scrolling
10152      */
10153     focusCell : function(row, col, hscroll)
10154     {
10155         //Roo.log('GridView.focusCell');
10156         var el = this.ensureVisible(row, col, hscroll);
10157         // not sure what focusEL achives = it's a <a> pos relative 
10158         //this.focusEl.alignTo(el, "tl-tl");
10159         //if(Roo.isGecko){
10160         //    this.focusEl.focus();
10161         //}else{
10162         //    this.focusEl.focus.defer(1, this.focusEl);
10163         //}
10164     },
10165     
10166      /**
10167      * Scrolls the specified cell into view
10168      * @param {Number} row The row index
10169      * @param {Number} col The column index
10170      * @param {Boolean} hscroll false to disable horizontal scrolling
10171      */
10172     ensureVisible : function(row, col, hscroll)
10173     {
10174         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10175         //return null; //disable for testing.
10176         if(typeof row != "number"){
10177             row = row.rowIndex;
10178         }
10179         if(row < 0 && row >= this.ds.getCount()){
10180             return  null;
10181         }
10182         col = (col !== undefined ? col : 0);
10183         var cm = this.cm;
10184         while(cm.isHidden(col)){
10185             col++;
10186         }
10187
10188         var el = this.getCellDom(row, col);
10189         if(!el){
10190             return null;
10191         }
10192         var c = this.bodyEl.dom;
10193
10194         var ctop = parseInt(el.offsetTop, 10);
10195         var cleft = parseInt(el.offsetLeft, 10);
10196         var cbot = ctop + el.offsetHeight;
10197         var cright = cleft + el.offsetWidth;
10198
10199         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10200         var ch = 0; //?? header is not withing the area?
10201         var stop = parseInt(c.scrollTop, 10);
10202         var sleft = parseInt(c.scrollLeft, 10);
10203         var sbot = stop + ch;
10204         var sright = sleft + c.clientWidth;
10205         /*
10206         Roo.log('GridView.ensureVisible:' +
10207                 ' ctop:' + ctop +
10208                 ' c.clientHeight:' + c.clientHeight +
10209                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10210                 ' stop:' + stop +
10211                 ' cbot:' + cbot +
10212                 ' sbot:' + sbot +
10213                 ' ch:' + ch  
10214                 );
10215         */
10216         if(ctop < stop){
10217             c.scrollTop = ctop;
10218             //Roo.log("set scrolltop to ctop DISABLE?");
10219         }else if(cbot > sbot){
10220             //Roo.log("set scrolltop to cbot-ch");
10221             c.scrollTop = cbot-ch;
10222         }
10223
10224         if(hscroll !== false){
10225             if(cleft < sleft){
10226                 c.scrollLeft = cleft;
10227             }else if(cright > sright){
10228                 c.scrollLeft = cright-c.clientWidth;
10229             }
10230         }
10231
10232         return el;
10233     },
10234     
10235     
10236     insertRow : function(dm, rowIndex, isUpdate){
10237         
10238         if(!isUpdate){
10239             this.fireEvent("beforerowsinserted", this, rowIndex);
10240         }
10241             //var s = this.getScrollState();
10242         var row = this.renderRow(this.cm, this.store, rowIndex);
10243         // insert before rowIndex..
10244         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10245         
10246         var _this = this;
10247                 
10248         if(row.cellObjects.length){
10249             Roo.each(row.cellObjects, function(r){
10250                 _this.renderCellObject(r);
10251             })
10252         }
10253             
10254         if(!isUpdate){
10255             this.fireEvent("rowsinserted", this, rowIndex);
10256             //this.syncRowHeights(firstRow, lastRow);
10257             //this.stripeRows(firstRow);
10258             //this.layout();
10259         }
10260         
10261     },
10262     
10263     
10264     getRowDom : function(rowIndex)
10265     {
10266         var rows = this.el.select('tbody > tr', true).elements;
10267         
10268         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10269         
10270     },
10271     getCellDom : function(rowIndex, colIndex)
10272     {
10273         var row = this.getRowDom(rowIndex);
10274         if (row === false) {
10275             return false;
10276         }
10277         var cols = row.select('td', true).elements;
10278         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10279         
10280     },
10281     
10282     // returns the object tree for a tr..
10283   
10284     
10285     renderRow : function(cm, ds, rowIndex) 
10286     {
10287         var d = ds.getAt(rowIndex);
10288         
10289         var row = {
10290             tag : 'tr',
10291             cls : 'x-row-' + rowIndex,
10292             cn : []
10293         };
10294             
10295         var cellObjects = [];
10296         
10297         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10298             var config = cm.config[i];
10299             
10300             var renderer = cm.getRenderer(i);
10301             var value = '';
10302             var id = false;
10303             
10304             if(typeof(renderer) !== 'undefined'){
10305                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10306             }
10307             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10308             // and are rendered into the cells after the row is rendered - using the id for the element.
10309             
10310             if(typeof(value) === 'object'){
10311                 id = Roo.id();
10312                 cellObjects.push({
10313                     container : id,
10314                     cfg : value 
10315                 })
10316             }
10317             
10318             var rowcfg = {
10319                 record: d,
10320                 rowIndex : rowIndex,
10321                 colIndex : i,
10322                 rowClass : ''
10323             };
10324
10325             this.fireEvent('rowclass', this, rowcfg);
10326             
10327             var td = {
10328                 tag: 'td',
10329                 // this might end up displaying HTML?
10330                 // this is too messy... - better to only do it on columsn you know are going to be too long
10331                 //tooltip : (typeof(value) === 'object') ? '' : value,
10332                 cls : rowcfg.rowClass + ' x-col-' + i,
10333                 style: '',
10334                 html: (typeof(value) === 'object') ? '' : value
10335             };
10336             
10337             if (id) {
10338                 td.id = id;
10339             }
10340             
10341             if(typeof(config.colspan) != 'undefined'){
10342                 td.colspan = config.colspan;
10343             }
10344             
10345             
10346             
10347             if(typeof(config.align) != 'undefined' && config.align.length){
10348                 td.style += ' text-align:' + config.align + ';';
10349             }
10350             if(typeof(config.valign) != 'undefined' && config.valign.length){
10351                 td.style += ' vertical-align:' + config.valign + ';';
10352             }
10353             /*
10354             if(typeof(config.width) != 'undefined'){
10355                 td.style += ' width:' +  config.width + 'px;';
10356             }
10357             */
10358             
10359             if(typeof(config.cursor) != 'undefined'){
10360                 td.style += ' cursor:' +  config.cursor + ';';
10361             }
10362             
10363             if(typeof(config.cls) != 'undefined'){
10364                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10365             }
10366             if (this.responsive) {
10367                 ['xs','sm','md','lg'].map(function(size){
10368                     
10369                     if(typeof(config[size]) == 'undefined'){
10370                         return;
10371                     }
10372                     
10373                     
10374                       
10375                     if (!config[size]) { // 0 = hidden
10376                         // BS 4 '0' is treated as hide that column and below.
10377                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10378                         return;
10379                     }
10380                     
10381                     td.cls += ' col-' + size + '-' + config[size] + (
10382                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10383                     );
10384                      
10385     
10386                 });
10387             }
10388             row.cn.push(td);
10389            
10390         }
10391         
10392         row.cellObjects = cellObjects;
10393         
10394         return row;
10395           
10396     },
10397     
10398     
10399     
10400     onBeforeLoad : function()
10401     {
10402         this.el.unmask(); // if needed.
10403     },
10404      /**
10405      * Remove all rows
10406      */
10407     clear : function()
10408     {
10409         this.el.select('tbody', true).first().dom.innerHTML = '';
10410     },
10411     /**
10412      * Show or hide a row.
10413      * @param {Number} rowIndex to show or hide
10414      * @param {Boolean} state hide
10415      */
10416     setRowVisibility : function(rowIndex, state)
10417     {
10418         var bt = this.bodyEl.dom;
10419         
10420         var rows = this.el.select('tbody > tr', true).elements;
10421         
10422         if(typeof(rows[rowIndex]) == 'undefined'){
10423             return;
10424         }
10425         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10426         
10427     },
10428     
10429     
10430     getSelectionModel : function(){
10431         if(!this.selModel){
10432             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10433         }
10434         return this.selModel;
10435     },
10436     /*
10437      * Render the Roo.bootstrap object from renderder
10438      */
10439     renderCellObject : function(r)
10440     {
10441         var _this = this;
10442         
10443         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10444         
10445         var t = r.cfg.render(r.container);
10446         
10447         if(r.cfg.cn){
10448             Roo.each(r.cfg.cn, function(c){
10449                 var child = {
10450                     container: t.getChildContainer(),
10451                     cfg: c
10452                 };
10453                 _this.renderCellObject(child);
10454             })
10455         }
10456     },
10457     /**
10458      * get the Row Index from a dom element.
10459      * @param {Roo.Element} row The row to look for
10460      * @returns {Number} the row
10461      */
10462     getRowIndex : function(row)
10463     {
10464         var rowIndex = -1;
10465         
10466         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10467             if(el != row){
10468                 return;
10469             }
10470             
10471             rowIndex = index;
10472         });
10473         
10474         return rowIndex;
10475     },
10476     /**
10477      * get the header TH element for columnIndex
10478      * @param {Number} columnIndex
10479      * @returns {Roo.Element}
10480      */
10481     getHeaderIndex: function(colIndex)
10482     {
10483         var cols = this.headEl.select('th', true).elements;
10484         return cols[colIndex]; 
10485     },
10486     /**
10487      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10488      * @param {domElement} cell to look for
10489      * @returns {Number} the column
10490      */
10491     getCellIndex : function(cell)
10492     {
10493         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10494         if(id){
10495             return parseInt(id[1], 10);
10496         }
10497         return 0;
10498     },
10499      /**
10500      * Returns the grid's underlying element = used by panel.Grid
10501      * @return {Element} The element
10502      */
10503     getGridEl : function(){
10504         return this.el;
10505     },
10506      /**
10507      * Forces a resize - used by panel.Grid
10508      * @return {Element} The element
10509      */
10510     autoSize : function()
10511     {
10512         //var ctr = Roo.get(this.container.dom.parentElement);
10513         var ctr = Roo.get(this.el.dom);
10514         
10515         var thd = this.getGridEl().select('thead',true).first();
10516         var tbd = this.getGridEl().select('tbody', true).first();
10517         var tfd = this.getGridEl().select('tfoot', true).first();
10518         
10519         var cw = ctr.getWidth();
10520         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10521         
10522         if (tbd) {
10523             
10524             tbd.setWidth(ctr.getWidth());
10525             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10526             // this needs fixing for various usage - currently only hydra job advers I think..
10527             //tdb.setHeight(
10528             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10529             //); 
10530             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10531             cw -= barsize;
10532         }
10533         cw = Math.max(cw, this.totalWidth);
10534         this.getGridEl().select('tbody tr',true).setWidth(cw);
10535         this.initCSS();
10536         
10537         // resize 'expandable coloumn?
10538         
10539         return; // we doe not have a view in this design..
10540         
10541     },
10542     onBodyScroll: function()
10543     {
10544         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10545         if(this.headEl){
10546             this.headEl.setStyle({
10547                 'position' : 'relative',
10548                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10549             });
10550         }
10551         
10552         if(this.lazyLoad){
10553             
10554             var scrollHeight = this.bodyEl.dom.scrollHeight;
10555             
10556             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10557             
10558             var height = this.bodyEl.getHeight();
10559             
10560             if(scrollHeight - height == scrollTop) {
10561                 
10562                 var total = this.ds.getTotalCount();
10563                 
10564                 if(this.footer.cursor + this.footer.pageSize < total){
10565                     
10566                     this.footer.ds.load({
10567                         params : {
10568                             start : this.footer.cursor + this.footer.pageSize,
10569                             limit : this.footer.pageSize
10570                         },
10571                         add : true
10572                     });
10573                 }
10574             }
10575             
10576         }
10577     },
10578     onColumnSplitterMoved : function(i, diff)
10579     {
10580         this.userResized = true;
10581         
10582         var cm = this.colModel;
10583         
10584         var w = this.getHeaderIndex(i).getWidth() + diff;
10585         
10586         
10587         cm.setColumnWidth(i, w, true);
10588         this.initCSS();
10589         //var cid = cm.getColumnId(i); << not used in this version?
10590        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10591         
10592         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10593         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10594         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10595 */
10596         //this.updateSplitters();
10597         //this.layout(); << ??
10598         this.fireEvent("columnresize", i, w);
10599     },
10600     onHeaderChange : function()
10601     {
10602         var header = this.renderHeader();
10603         var table = this.el.select('table', true).first();
10604         
10605         this.headEl.remove();
10606         this.headEl = table.createChild(header, this.bodyEl, false);
10607         
10608         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10609             e.on('click', this.sort, this);
10610         }, this);
10611         
10612         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10613             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10614         }
10615         
10616     },
10617     
10618     onHiddenChange : function(colModel, colIndex, hidden)
10619     {
10620         /*
10621         this.cm.setHidden()
10622         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10623         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10624         
10625         this.CSS.updateRule(thSelector, "display", "");
10626         this.CSS.updateRule(tdSelector, "display", "");
10627         
10628         if(hidden){
10629             this.CSS.updateRule(thSelector, "display", "none");
10630             this.CSS.updateRule(tdSelector, "display", "none");
10631         }
10632         */
10633         // onload calls initCSS()
10634         this.onHeaderChange();
10635         this.onLoad();
10636     },
10637     
10638     setColumnWidth: function(col_index, width)
10639     {
10640         // width = "md-2 xs-2..."
10641         if(!this.colModel.config[col_index]) {
10642             return;
10643         }
10644         
10645         var w = width.split(" ");
10646         
10647         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10648         
10649         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10650         
10651         
10652         for(var j = 0; j < w.length; j++) {
10653             
10654             if(!w[j]) {
10655                 continue;
10656             }
10657             
10658             var size_cls = w[j].split("-");
10659             
10660             if(!Number.isInteger(size_cls[1] * 1)) {
10661                 continue;
10662             }
10663             
10664             if(!this.colModel.config[col_index][size_cls[0]]) {
10665                 continue;
10666             }
10667             
10668             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10669                 continue;
10670             }
10671             
10672             h_row[0].classList.replace(
10673                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674                 "col-"+size_cls[0]+"-"+size_cls[1]
10675             );
10676             
10677             for(var i = 0; i < rows.length; i++) {
10678                 
10679                 var size_cls = w[j].split("-");
10680                 
10681                 if(!Number.isInteger(size_cls[1] * 1)) {
10682                     continue;
10683                 }
10684                 
10685                 if(!this.colModel.config[col_index][size_cls[0]]) {
10686                     continue;
10687                 }
10688                 
10689                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10690                     continue;
10691                 }
10692                 
10693                 rows[i].classList.replace(
10694                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10695                     "col-"+size_cls[0]+"-"+size_cls[1]
10696                 );
10697             }
10698             
10699             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10700         }
10701     }
10702 });
10703
10704 // currently only used to find the split on drag.. 
10705 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10706
10707 /**
10708  * @depricated
10709 */
10710 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10711 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10712 /*
10713  * - LGPL
10714  *
10715  * table cell
10716  * 
10717  */
10718
10719 /**
10720  * @class Roo.bootstrap.TableCell
10721  * @extends Roo.bootstrap.Component
10722  * @children Roo.bootstrap.Component
10723  * @parent Roo.bootstrap.TableRow
10724  * Bootstrap TableCell class
10725  * 
10726  * @cfg {String} html cell contain text
10727  * @cfg {String} cls cell class
10728  * @cfg {String} tag cell tag (td|th) default td
10729  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10730  * @cfg {String} align Aligns the content in a cell
10731  * @cfg {String} axis Categorizes cells
10732  * @cfg {String} bgcolor Specifies the background color of a cell
10733  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10734  * @cfg {Number} colspan Specifies the number of columns a cell should span
10735  * @cfg {String} headers Specifies one or more header cells a cell is related to
10736  * @cfg {Number} height Sets the height of a cell
10737  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10738  * @cfg {Number} rowspan Sets the number of rows a cell should span
10739  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10740  * @cfg {String} valign Vertical aligns the content in a cell
10741  * @cfg {Number} width Specifies the width of a cell
10742  * 
10743  * @constructor
10744  * Create a new TableCell
10745  * @param {Object} config The config object
10746  */
10747
10748 Roo.bootstrap.TableCell = function(config){
10749     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10750 };
10751
10752 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10753     
10754     html: false,
10755     cls: false,
10756     tag: false,
10757     abbr: false,
10758     align: false,
10759     axis: false,
10760     bgcolor: false,
10761     charoff: false,
10762     colspan: false,
10763     headers: false,
10764     height: false,
10765     nowrap: false,
10766     rowspan: false,
10767     scope: false,
10768     valign: false,
10769     width: false,
10770     
10771     
10772     getAutoCreate : function(){
10773         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10774         
10775         cfg = {
10776             tag: 'td'
10777         };
10778         
10779         if(this.tag){
10780             cfg.tag = this.tag;
10781         }
10782         
10783         if (this.html) {
10784             cfg.html=this.html
10785         }
10786         if (this.cls) {
10787             cfg.cls=this.cls
10788         }
10789         if (this.abbr) {
10790             cfg.abbr=this.abbr
10791         }
10792         if (this.align) {
10793             cfg.align=this.align
10794         }
10795         if (this.axis) {
10796             cfg.axis=this.axis
10797         }
10798         if (this.bgcolor) {
10799             cfg.bgcolor=this.bgcolor
10800         }
10801         if (this.charoff) {
10802             cfg.charoff=this.charoff
10803         }
10804         if (this.colspan) {
10805             cfg.colspan=this.colspan
10806         }
10807         if (this.headers) {
10808             cfg.headers=this.headers
10809         }
10810         if (this.height) {
10811             cfg.height=this.height
10812         }
10813         if (this.nowrap) {
10814             cfg.nowrap=this.nowrap
10815         }
10816         if (this.rowspan) {
10817             cfg.rowspan=this.rowspan
10818         }
10819         if (this.scope) {
10820             cfg.scope=this.scope
10821         }
10822         if (this.valign) {
10823             cfg.valign=this.valign
10824         }
10825         if (this.width) {
10826             cfg.width=this.width
10827         }
10828         
10829         
10830         return cfg;
10831     }
10832    
10833 });
10834
10835  
10836
10837  /*
10838  * - LGPL
10839  *
10840  * table row
10841  * 
10842  */
10843
10844 /**
10845  * @class Roo.bootstrap.TableRow
10846  * @extends Roo.bootstrap.Component
10847  * @children Roo.bootstrap.TableCell
10848  * @parent Roo.bootstrap.TableBody
10849  * Bootstrap TableRow class
10850  * @cfg {String} cls row class
10851  * @cfg {String} align Aligns the content in a table row
10852  * @cfg {String} bgcolor Specifies a background color for a table row
10853  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10854  * @cfg {String} valign Vertical aligns the content in a table row
10855  * 
10856  * @constructor
10857  * Create a new TableRow
10858  * @param {Object} config The config object
10859  */
10860
10861 Roo.bootstrap.TableRow = function(config){
10862     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10863 };
10864
10865 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10866     
10867     cls: false,
10868     align: false,
10869     bgcolor: false,
10870     charoff: false,
10871     valign: false,
10872     
10873     getAutoCreate : function(){
10874         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10875         
10876         cfg = {
10877             tag: 'tr'
10878         };
10879             
10880         if(this.cls){
10881             cfg.cls = this.cls;
10882         }
10883         if(this.align){
10884             cfg.align = this.align;
10885         }
10886         if(this.bgcolor){
10887             cfg.bgcolor = this.bgcolor;
10888         }
10889         if(this.charoff){
10890             cfg.charoff = this.charoff;
10891         }
10892         if(this.valign){
10893             cfg.valign = this.valign;
10894         }
10895         
10896         return cfg;
10897     }
10898    
10899 });
10900
10901  
10902
10903  /*
10904  * - LGPL
10905  *
10906  * table body
10907  * 
10908  */
10909
10910 /**
10911  * @class Roo.bootstrap.TableBody
10912  * @extends Roo.bootstrap.Component
10913  * @children Roo.bootstrap.TableRow
10914  * @parent Roo.bootstrap.Table
10915  * Bootstrap TableBody class
10916  * @cfg {String} cls element class
10917  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10918  * @cfg {String} align Aligns the content inside the element
10919  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10920  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10921  * 
10922  * @constructor
10923  * Create a new TableBody
10924  * @param {Object} config The config object
10925  */
10926
10927 Roo.bootstrap.TableBody = function(config){
10928     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10929 };
10930
10931 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10932     
10933     cls: false,
10934     tag: false,
10935     align: false,
10936     charoff: false,
10937     valign: false,
10938     
10939     getAutoCreate : function(){
10940         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10941         
10942         cfg = {
10943             tag: 'tbody'
10944         };
10945             
10946         if (this.cls) {
10947             cfg.cls=this.cls
10948         }
10949         if(this.tag){
10950             cfg.tag = this.tag;
10951         }
10952         
10953         if(this.align){
10954             cfg.align = this.align;
10955         }
10956         if(this.charoff){
10957             cfg.charoff = this.charoff;
10958         }
10959         if(this.valign){
10960             cfg.valign = this.valign;
10961         }
10962         
10963         return cfg;
10964     }
10965     
10966     
10967 //    initEvents : function()
10968 //    {
10969 //        
10970 //        if(!this.store){
10971 //            return;
10972 //        }
10973 //        
10974 //        this.store = Roo.factory(this.store, Roo.data);
10975 //        this.store.on('load', this.onLoad, this);
10976 //        
10977 //        this.store.load();
10978 //        
10979 //    },
10980 //    
10981 //    onLoad: function () 
10982 //    {   
10983 //        this.fireEvent('load', this);
10984 //    }
10985 //    
10986 //   
10987 });
10988
10989  
10990
10991  /*
10992  * Based on:
10993  * Ext JS Library 1.1.1
10994  * Copyright(c) 2006-2007, Ext JS, LLC.
10995  *
10996  * Originally Released Under LGPL - original licence link has changed is not relivant.
10997  *
10998  * Fork - LGPL
10999  * <script type="text/javascript">
11000  */
11001
11002 // as we use this in bootstrap.
11003 Roo.namespace('Roo.form');
11004  /**
11005  * @class Roo.form.Action
11006  * Internal Class used to handle form actions
11007  * @constructor
11008  * @param {Roo.form.BasicForm} el The form element or its id
11009  * @param {Object} config Configuration options
11010  */
11011
11012  
11013  
11014 // define the action interface
11015 Roo.form.Action = function(form, options){
11016     this.form = form;
11017     this.options = options || {};
11018 };
11019 /**
11020  * Client Validation Failed
11021  * @const 
11022  */
11023 Roo.form.Action.CLIENT_INVALID = 'client';
11024 /**
11025  * Server Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.SERVER_INVALID = 'server';
11029  /**
11030  * Connect to Server Failed
11031  * @const 
11032  */
11033 Roo.form.Action.CONNECT_FAILURE = 'connect';
11034 /**
11035  * Reading Data from Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.LOAD_FAILURE = 'load';
11039
11040 Roo.form.Action.prototype = {
11041     type : 'default',
11042     failureType : undefined,
11043     response : undefined,
11044     result : undefined,
11045
11046     // interface method
11047     run : function(options){
11048
11049     },
11050
11051     // interface method
11052     success : function(response){
11053
11054     },
11055
11056     // interface method
11057     handleResponse : function(response){
11058
11059     },
11060
11061     // default connection failure
11062     failure : function(response){
11063         
11064         this.response = response;
11065         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11066         this.form.afterAction(this, false);
11067     },
11068
11069     processResponse : function(response){
11070         this.response = response;
11071         if(!response.responseText){
11072             return true;
11073         }
11074         this.result = this.handleResponse(response);
11075         return this.result;
11076     },
11077
11078     // utility functions used internally
11079     getUrl : function(appendParams){
11080         var url = this.options.url || this.form.url || this.form.el.dom.action;
11081         if(appendParams){
11082             var p = this.getParams();
11083             if(p){
11084                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11085             }
11086         }
11087         return url;
11088     },
11089
11090     getMethod : function(){
11091         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11092     },
11093
11094     getParams : function(){
11095         var bp = this.form.baseParams;
11096         var p = this.options.params;
11097         if(p){
11098             if(typeof p == "object"){
11099                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11100             }else if(typeof p == 'string' && bp){
11101                 p += '&' + Roo.urlEncode(bp);
11102             }
11103         }else if(bp){
11104             p = Roo.urlEncode(bp);
11105         }
11106         return p;
11107     },
11108
11109     createCallback : function(){
11110         return {
11111             success: this.success,
11112             failure: this.failure,
11113             scope: this,
11114             timeout: (this.form.timeout*1000),
11115             upload: this.form.fileUpload ? this.success : undefined
11116         };
11117     }
11118 };
11119
11120 Roo.form.Action.Submit = function(form, options){
11121     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11122 };
11123
11124 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11125     type : 'submit',
11126
11127     haveProgress : false,
11128     uploadComplete : false,
11129     
11130     // uploadProgress indicator.
11131     uploadProgress : function()
11132     {
11133         if (!this.form.progressUrl) {
11134             return;
11135         }
11136         
11137         if (!this.haveProgress) {
11138             Roo.MessageBox.progress("Uploading", "Uploading");
11139         }
11140         if (this.uploadComplete) {
11141            Roo.MessageBox.hide();
11142            return;
11143         }
11144         
11145         this.haveProgress = true;
11146    
11147         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11148         
11149         var c = new Roo.data.Connection();
11150         c.request({
11151             url : this.form.progressUrl,
11152             params: {
11153                 id : uid
11154             },
11155             method: 'GET',
11156             success : function(req){
11157                //console.log(data);
11158                 var rdata = false;
11159                 var edata;
11160                 try  {
11161                    rdata = Roo.decode(req.responseText)
11162                 } catch (e) {
11163                     Roo.log("Invalid data from server..");
11164                     Roo.log(edata);
11165                     return;
11166                 }
11167                 if (!rdata || !rdata.success) {
11168                     Roo.log(rdata);
11169                     Roo.MessageBox.alert(Roo.encode(rdata));
11170                     return;
11171                 }
11172                 var data = rdata.data;
11173                 
11174                 if (this.uploadComplete) {
11175                    Roo.MessageBox.hide();
11176                    return;
11177                 }
11178                    
11179                 if (data){
11180                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11181                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11182                     );
11183                 }
11184                 this.uploadProgress.defer(2000,this);
11185             },
11186        
11187             failure: function(data) {
11188                 Roo.log('progress url failed ');
11189                 Roo.log(data);
11190             },
11191             scope : this
11192         });
11193            
11194     },
11195     
11196     
11197     run : function()
11198     {
11199         // run get Values on the form, so it syncs any secondary forms.
11200         this.form.getValues();
11201         
11202         var o = this.options;
11203         var method = this.getMethod();
11204         var isPost = method == 'POST';
11205         if(o.clientValidation === false || this.form.isValid()){
11206             
11207             if (this.form.progressUrl) {
11208                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11209                     (new Date() * 1) + '' + Math.random());
11210                     
11211             } 
11212             
11213             
11214             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11215                 form:this.form.el.dom,
11216                 url:this.getUrl(!isPost),
11217                 method: method,
11218                 params:isPost ? this.getParams() : null,
11219                 isUpload: this.form.fileUpload,
11220                 formData : this.form.formData
11221             }));
11222             
11223             this.uploadProgress();
11224
11225         }else if (o.clientValidation !== false){ // client validation failed
11226             this.failureType = Roo.form.Action.CLIENT_INVALID;
11227             this.form.afterAction(this, false);
11228         }
11229     },
11230
11231     success : function(response)
11232     {
11233         this.uploadComplete= true;
11234         if (this.haveProgress) {
11235             Roo.MessageBox.hide();
11236         }
11237         
11238         
11239         var result = this.processResponse(response);
11240         if(result === true || result.success){
11241             this.form.afterAction(this, true);
11242             return;
11243         }
11244         if(result.errors){
11245             this.form.markInvalid(result.errors);
11246             this.failureType = Roo.form.Action.SERVER_INVALID;
11247         }
11248         this.form.afterAction(this, false);
11249     },
11250     failure : function(response)
11251     {
11252         this.uploadComplete= true;
11253         if (this.haveProgress) {
11254             Roo.MessageBox.hide();
11255         }
11256         
11257         this.response = response;
11258         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11259         this.form.afterAction(this, false);
11260     },
11261     
11262     handleResponse : function(response){
11263         if(this.form.errorReader){
11264             var rs = this.form.errorReader.read(response);
11265             var errors = [];
11266             if(rs.records){
11267                 for(var i = 0, len = rs.records.length; i < len; i++) {
11268                     var r = rs.records[i];
11269                     errors[i] = r.data;
11270                 }
11271             }
11272             if(errors.length < 1){
11273                 errors = null;
11274             }
11275             return {
11276                 success : rs.success,
11277                 errors : errors
11278             };
11279         }
11280         var ret = false;
11281         try {
11282             ret = Roo.decode(response.responseText);
11283         } catch (e) {
11284             ret = {
11285                 success: false,
11286                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11287                 errors : []
11288             };
11289         }
11290         return ret;
11291         
11292     }
11293 });
11294
11295
11296 Roo.form.Action.Load = function(form, options){
11297     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11298     this.reader = this.form.reader;
11299 };
11300
11301 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11302     type : 'load',
11303
11304     run : function(){
11305         
11306         Roo.Ajax.request(Roo.apply(
11307                 this.createCallback(), {
11308                     method:this.getMethod(),
11309                     url:this.getUrl(false),
11310                     params:this.getParams()
11311         }));
11312     },
11313
11314     success : function(response){
11315         
11316         var result = this.processResponse(response);
11317         if(result === true || !result.success || !result.data){
11318             this.failureType = Roo.form.Action.LOAD_FAILURE;
11319             this.form.afterAction(this, false);
11320             return;
11321         }
11322         this.form.clearInvalid();
11323         this.form.setValues(result.data);
11324         this.form.afterAction(this, true);
11325     },
11326
11327     handleResponse : function(response){
11328         if(this.form.reader){
11329             var rs = this.form.reader.read(response);
11330             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11331             return {
11332                 success : rs.success,
11333                 data : data
11334             };
11335         }
11336         return Roo.decode(response.responseText);
11337     }
11338 });
11339
11340 Roo.form.Action.ACTION_TYPES = {
11341     'load' : Roo.form.Action.Load,
11342     'submit' : Roo.form.Action.Submit
11343 };/*
11344  * - LGPL
11345  *
11346  * form
11347  *
11348  */
11349
11350 /**
11351  * @class Roo.bootstrap.form.Form
11352  * @extends Roo.bootstrap.Component
11353  * @children Roo.bootstrap.Component
11354  * Bootstrap Form class
11355  * @cfg {String} method  GET | POST (default POST)
11356  * @cfg {String} labelAlign top | left (default top)
11357  * @cfg {String} align left  | right - for navbars
11358  * @cfg {Boolean} loadMask load mask when submit (default true)
11359
11360  *
11361  * @constructor
11362  * Create a new Form
11363  * @param {Object} config The config object
11364  */
11365
11366
11367 Roo.bootstrap.form.Form = function(config){
11368     
11369     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11370     
11371     Roo.bootstrap.form.Form.popover.apply();
11372     
11373     this.addEvents({
11374         /**
11375          * @event clientvalidation
11376          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11377          * @param {Form} this
11378          * @param {Boolean} valid true if the form has passed client-side validation
11379          */
11380         clientvalidation: true,
11381         /**
11382          * @event beforeaction
11383          * Fires before any action is performed. Return false to cancel the action.
11384          * @param {Form} this
11385          * @param {Action} action The action to be performed
11386          */
11387         beforeaction: true,
11388         /**
11389          * @event actionfailed
11390          * Fires when an action fails.
11391          * @param {Form} this
11392          * @param {Action} action The action that failed
11393          */
11394         actionfailed : true,
11395         /**
11396          * @event actioncomplete
11397          * Fires when an action is completed.
11398          * @param {Form} this
11399          * @param {Action} action The action that completed
11400          */
11401         actioncomplete : true
11402     });
11403 };
11404
11405 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11406
11407      /**
11408      * @cfg {String} method
11409      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11410      */
11411     method : 'POST',
11412     /**
11413      * @cfg {String} url
11414      * The URL to use for form actions if one isn't supplied in the action options.
11415      */
11416     /**
11417      * @cfg {Boolean} fileUpload
11418      * Set to true if this form is a file upload.
11419      */
11420
11421     /**
11422      * @cfg {Object} baseParams
11423      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11424      */
11425
11426     /**
11427      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11428      */
11429     timeout: 30,
11430     /**
11431      * @cfg {Sting} align (left|right) for navbar forms
11432      */
11433     align : 'left',
11434
11435     // private
11436     activeAction : null,
11437
11438     /**
11439      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11440      * element by passing it or its id or mask the form itself by passing in true.
11441      * @type Mixed
11442      */
11443     waitMsgTarget : false,
11444
11445     loadMask : true,
11446     
11447     /**
11448      * @cfg {Boolean} errorMask (true|false) default false
11449      */
11450     errorMask : false,
11451     
11452     /**
11453      * @cfg {Number} maskOffset Default 100
11454      */
11455     maskOffset : 100,
11456     
11457     /**
11458      * @cfg {Boolean} maskBody
11459      */
11460     maskBody : false,
11461
11462     getAutoCreate : function(){
11463
11464         var cfg = {
11465             tag: 'form',
11466             method : this.method || 'POST',
11467             id : this.id || Roo.id(),
11468             cls : ''
11469         };
11470         if (this.parent().xtype.match(/^Nav/)) {
11471             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11472
11473         }
11474
11475         if (this.labelAlign == 'left' ) {
11476             cfg.cls += ' form-horizontal';
11477         }
11478
11479
11480         return cfg;
11481     },
11482     initEvents : function()
11483     {
11484         this.el.on('submit', this.onSubmit, this);
11485         // this was added as random key presses on the form where triggering form submit.
11486         this.el.on('keypress', function(e) {
11487             if (e.getCharCode() != 13) {
11488                 return true;
11489             }
11490             // we might need to allow it for textareas.. and some other items.
11491             // check e.getTarget().
11492
11493             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11494                 return true;
11495             }
11496
11497             Roo.log("keypress blocked");
11498
11499             e.preventDefault();
11500             return false;
11501         });
11502         
11503     },
11504     // private
11505     onSubmit : function(e){
11506         e.stopEvent();
11507     },
11508
11509      /**
11510      * Returns true if client-side validation on the form is successful.
11511      * @return Boolean
11512      */
11513     isValid : function(){
11514         var items = this.getItems();
11515         var valid = true;
11516         var target = false;
11517         
11518         items.each(function(f){
11519             
11520             if(f.validate()){
11521                 return;
11522             }
11523             
11524             Roo.log('invalid field: ' + f.name);
11525             
11526             valid = false;
11527
11528             if(!target && f.el.isVisible(true)){
11529                 target = f;
11530             }
11531            
11532         });
11533         
11534         if(this.errorMask && !valid){
11535             Roo.bootstrap.form.Form.popover.mask(this, target);
11536         }
11537         
11538         return valid;
11539     },
11540     
11541     /**
11542      * Returns true if any fields in this form have changed since their original load.
11543      * @return Boolean
11544      */
11545     isDirty : function(){
11546         var dirty = false;
11547         var items = this.getItems();
11548         items.each(function(f){
11549            if(f.isDirty()){
11550                dirty = true;
11551                return false;
11552            }
11553            return true;
11554         });
11555         return dirty;
11556     },
11557      /**
11558      * Performs a predefined action (submit or load) or custom actions you define on this form.
11559      * @param {String} actionName The name of the action type
11560      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11561      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11562      * accept other config options):
11563      * <pre>
11564 Property          Type             Description
11565 ----------------  ---------------  ----------------------------------------------------------------------------------
11566 url               String           The url for the action (defaults to the form's url)
11567 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11568 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11569 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11570                                    validate the form on the client (defaults to false)
11571      * </pre>
11572      * @return {BasicForm} this
11573      */
11574     doAction : function(action, options){
11575         if(typeof action == 'string'){
11576             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11577         }
11578         if(this.fireEvent('beforeaction', this, action) !== false){
11579             this.beforeAction(action);
11580             action.run.defer(100, action);
11581         }
11582         return this;
11583     },
11584
11585     // private
11586     beforeAction : function(action){
11587         var o = action.options;
11588         
11589         if(this.loadMask){
11590             
11591             if(this.maskBody){
11592                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11593             } else {
11594                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11595             }
11596         }
11597         // not really supported yet.. ??
11598
11599         //if(this.waitMsgTarget === true){
11600         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11601         //}else if(this.waitMsgTarget){
11602         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11603         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11604         //}else {
11605         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11606        // }
11607
11608     },
11609
11610     // private
11611     afterAction : function(action, success){
11612         this.activeAction = null;
11613         var o = action.options;
11614
11615         if(this.loadMask){
11616             
11617             if(this.maskBody){
11618                 Roo.get(document.body).unmask();
11619             } else {
11620                 this.el.unmask();
11621             }
11622         }
11623         
11624         //if(this.waitMsgTarget === true){
11625 //            this.el.unmask();
11626         //}else if(this.waitMsgTarget){
11627         //    this.waitMsgTarget.unmask();
11628         //}else{
11629         //    Roo.MessageBox.updateProgress(1);
11630         //    Roo.MessageBox.hide();
11631        // }
11632         //
11633         if(success){
11634             if(o.reset){
11635                 this.reset();
11636             }
11637             Roo.callback(o.success, o.scope, [this, action]);
11638             this.fireEvent('actioncomplete', this, action);
11639
11640         }else{
11641
11642             // failure condition..
11643             // we have a scenario where updates need confirming.
11644             // eg. if a locking scenario exists..
11645             // we look for { errors : { needs_confirm : true }} in the response.
11646             if (
11647                 (typeof(action.result) != 'undefined')  &&
11648                 (typeof(action.result.errors) != 'undefined')  &&
11649                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11650            ){
11651                 var _t = this;
11652                 Roo.log("not supported yet");
11653                  /*
11654
11655                 Roo.MessageBox.confirm(
11656                     "Change requires confirmation",
11657                     action.result.errorMsg,
11658                     function(r) {
11659                         if (r != 'yes') {
11660                             return;
11661                         }
11662                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11663                     }
11664
11665                 );
11666                 */
11667
11668
11669                 return;
11670             }
11671
11672             Roo.callback(o.failure, o.scope, [this, action]);
11673             // show an error message if no failed handler is set..
11674             if (!this.hasListener('actionfailed')) {
11675                 Roo.log("need to add dialog support");
11676                 /*
11677                 Roo.MessageBox.alert("Error",
11678                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11679                         action.result.errorMsg :
11680                         "Saving Failed, please check your entries or try again"
11681                 );
11682                 */
11683             }
11684
11685             this.fireEvent('actionfailed', this, action);
11686         }
11687
11688     },
11689     /**
11690      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11691      * @param {String} id The value to search for
11692      * @return Field
11693      */
11694     findField : function(id){
11695         var items = this.getItems();
11696         var field = items.get(id);
11697         if(!field){
11698              items.each(function(f){
11699                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11700                     field = f;
11701                     return false;
11702                 }
11703                 return true;
11704             });
11705         }
11706         return field || null;
11707     },
11708      /**
11709      * Mark fields in this form invalid in bulk.
11710      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11711      * @return {BasicForm} this
11712      */
11713     markInvalid : function(errors){
11714         if(errors instanceof Array){
11715             for(var i = 0, len = errors.length; i < len; i++){
11716                 var fieldError = errors[i];
11717                 var f = this.findField(fieldError.id);
11718                 if(f){
11719                     f.markInvalid(fieldError.msg);
11720                 }
11721             }
11722         }else{
11723             var field, id;
11724             for(id in errors){
11725                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11726                     field.markInvalid(errors[id]);
11727                 }
11728             }
11729         }
11730         //Roo.each(this.childForms || [], function (f) {
11731         //    f.markInvalid(errors);
11732         //});
11733
11734         return this;
11735     },
11736
11737     /**
11738      * Set values for fields in this form in bulk.
11739      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11740      * @return {BasicForm} this
11741      */
11742     setValues : function(values){
11743         if(values instanceof Array){ // array of objects
11744             for(var i = 0, len = values.length; i < len; i++){
11745                 var v = values[i];
11746                 var f = this.findField(v.id);
11747                 if(f){
11748                     f.setValue(v.value);
11749                     if(this.trackResetOnLoad){
11750                         f.originalValue = f.getValue();
11751                     }
11752                 }
11753             }
11754         }else{ // object hash
11755             var field, id;
11756             for(id in values){
11757                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11758
11759                     if (field.setFromData &&
11760                         field.valueField &&
11761                         field.displayField &&
11762                         // combos' with local stores can
11763                         // be queried via setValue()
11764                         // to set their value..
11765                         (field.store && !field.store.isLocal)
11766                         ) {
11767                         // it's a combo
11768                         var sd = { };
11769                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11770                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11771                         field.setFromData(sd);
11772
11773                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11774                         
11775                         field.setFromData(values);
11776                         
11777                     } else {
11778                         field.setValue(values[id]);
11779                     }
11780
11781
11782                     if(this.trackResetOnLoad){
11783                         field.originalValue = field.getValue();
11784                     }
11785                 }
11786             }
11787         }
11788
11789         //Roo.each(this.childForms || [], function (f) {
11790         //    f.setValues(values);
11791         //});
11792
11793         return this;
11794     },
11795
11796     /**
11797      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11798      * they are returned as an array.
11799      * @param {Boolean} asString
11800      * @return {Object}
11801      */
11802     getValues : function(asString){
11803         //if (this.childForms) {
11804             // copy values from the child forms
11805         //    Roo.each(this.childForms, function (f) {
11806         //        this.setValues(f.getValues());
11807         //    }, this);
11808         //}
11809
11810
11811
11812         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11813         if(asString === true){
11814             return fs;
11815         }
11816         return Roo.urlDecode(fs);
11817     },
11818
11819     /**
11820      * Returns the fields in this form as an object with key/value pairs.
11821      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11822      * @return {Object}
11823      */
11824     getFieldValues : function(with_hidden)
11825     {
11826         var items = this.getItems();
11827         var ret = {};
11828         items.each(function(f){
11829             
11830             if (!f.getName()) {
11831                 return;
11832             }
11833             
11834             var v = f.getValue();
11835             
11836             if (f.inputType =='radio') {
11837                 if (typeof(ret[f.getName()]) == 'undefined') {
11838                     ret[f.getName()] = ''; // empty..
11839                 }
11840
11841                 if (!f.el.dom.checked) {
11842                     return;
11843
11844                 }
11845                 v = f.el.dom.value;
11846
11847             }
11848             
11849             if(f.xtype == 'MoneyField'){
11850                 ret[f.currencyName] = f.getCurrency();
11851             }
11852
11853             // not sure if this supported any more..
11854             if ((typeof(v) == 'object') && f.getRawValue) {
11855                 v = f.getRawValue() ; // dates..
11856             }
11857             // combo boxes where name != hiddenName...
11858             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11859                 ret[f.name] = f.getRawValue();
11860             }
11861             ret[f.getName()] = v;
11862         });
11863
11864         return ret;
11865     },
11866
11867     /**
11868      * Clears all invalid messages in this form.
11869      * @return {BasicForm} this
11870      */
11871     clearInvalid : function(){
11872         var items = this.getItems();
11873
11874         items.each(function(f){
11875            f.clearInvalid();
11876         });
11877
11878         return this;
11879     },
11880
11881     /**
11882      * Resets this form.
11883      * @return {BasicForm} this
11884      */
11885     reset : function(){
11886         var items = this.getItems();
11887         items.each(function(f){
11888             f.reset();
11889         });
11890
11891         Roo.each(this.childForms || [], function (f) {
11892             f.reset();
11893         });
11894
11895
11896         return this;
11897     },
11898     
11899     getItems : function()
11900     {
11901         var r=new Roo.util.MixedCollection(false, function(o){
11902             return o.id || (o.id = Roo.id());
11903         });
11904         var iter = function(el) {
11905             if (el.inputEl) {
11906                 r.add(el);
11907             }
11908             if (!el.items) {
11909                 return;
11910             }
11911             Roo.each(el.items,function(e) {
11912                 iter(e);
11913             });
11914         };
11915
11916         iter(this);
11917         return r;
11918     },
11919     
11920     hideFields : function(items)
11921     {
11922         Roo.each(items, function(i){
11923             
11924             var f = this.findField(i);
11925             
11926             if(!f){
11927                 return;
11928             }
11929             
11930             f.hide();
11931             
11932         }, this);
11933     },
11934     
11935     showFields : function(items)
11936     {
11937         Roo.each(items, function(i){
11938             
11939             var f = this.findField(i);
11940             
11941             if(!f){
11942                 return;
11943             }
11944             
11945             f.show();
11946             
11947         }, this);
11948     }
11949
11950 });
11951
11952 Roo.apply(Roo.bootstrap.form.Form, {
11953     
11954     popover : {
11955         
11956         padding : 5,
11957         
11958         isApplied : false,
11959         
11960         isMasked : false,
11961         
11962         form : false,
11963         
11964         target : false,
11965         
11966         toolTip : false,
11967         
11968         intervalID : false,
11969         
11970         maskEl : false,
11971         
11972         apply : function()
11973         {
11974             if(this.isApplied){
11975                 return;
11976             }
11977             
11978             this.maskEl = {
11979                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11980                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11981                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11982                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11983             };
11984             
11985             this.maskEl.top.enableDisplayMode("block");
11986             this.maskEl.left.enableDisplayMode("block");
11987             this.maskEl.bottom.enableDisplayMode("block");
11988             this.maskEl.right.enableDisplayMode("block");
11989             
11990             this.toolTip = new Roo.bootstrap.Tooltip({
11991                 cls : 'roo-form-error-popover',
11992                 alignment : {
11993                     'left' : ['r-l', [-2,0], 'right'],
11994                     'right' : ['l-r', [2,0], 'left'],
11995                     'bottom' : ['tl-bl', [0,2], 'top'],
11996                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11997                 }
11998             });
11999             
12000             this.toolTip.render(Roo.get(document.body));
12001
12002             this.toolTip.el.enableDisplayMode("block");
12003             
12004             Roo.get(document.body).on('click', function(){
12005                 this.unmask();
12006             }, this);
12007             
12008             Roo.get(document.body).on('touchstart', function(){
12009                 this.unmask();
12010             }, this);
12011             
12012             this.isApplied = true
12013         },
12014         
12015         mask : function(form, target)
12016         {
12017             this.form = form;
12018             
12019             this.target = target;
12020             
12021             if(!this.form.errorMask || !target.el){
12022                 return;
12023             }
12024             
12025             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12026             
12027             Roo.log(scrollable);
12028             
12029             var ot = this.target.el.calcOffsetsTo(scrollable);
12030             
12031             var scrollTo = ot[1] - this.form.maskOffset;
12032             
12033             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12034             
12035             scrollable.scrollTo('top', scrollTo);
12036             
12037             var box = this.target.el.getBox();
12038             Roo.log(box);
12039             var zIndex = Roo.bootstrap.Modal.zIndex++;
12040
12041             
12042             this.maskEl.top.setStyle('position', 'absolute');
12043             this.maskEl.top.setStyle('z-index', zIndex);
12044             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12045             this.maskEl.top.setLeft(0);
12046             this.maskEl.top.setTop(0);
12047             this.maskEl.top.show();
12048             
12049             this.maskEl.left.setStyle('position', 'absolute');
12050             this.maskEl.left.setStyle('z-index', zIndex);
12051             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12052             this.maskEl.left.setLeft(0);
12053             this.maskEl.left.setTop(box.y - this.padding);
12054             this.maskEl.left.show();
12055
12056             this.maskEl.bottom.setStyle('position', 'absolute');
12057             this.maskEl.bottom.setStyle('z-index', zIndex);
12058             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12059             this.maskEl.bottom.setLeft(0);
12060             this.maskEl.bottom.setTop(box.bottom + this.padding);
12061             this.maskEl.bottom.show();
12062
12063             this.maskEl.right.setStyle('position', 'absolute');
12064             this.maskEl.right.setStyle('z-index', zIndex);
12065             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12066             this.maskEl.right.setLeft(box.right + this.padding);
12067             this.maskEl.right.setTop(box.y - this.padding);
12068             this.maskEl.right.show();
12069
12070             this.toolTip.bindEl = this.target.el;
12071
12072             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12073
12074             var tip = this.target.blankText;
12075
12076             if(this.target.getValue() !== '' ) {
12077                 
12078                 if (this.target.invalidText.length) {
12079                     tip = this.target.invalidText;
12080                 } else if (this.target.regexText.length){
12081                     tip = this.target.regexText;
12082                 }
12083             }
12084
12085             this.toolTip.show(tip);
12086
12087             this.intervalID = window.setInterval(function() {
12088                 Roo.bootstrap.form.Form.popover.unmask();
12089             }, 10000);
12090
12091             window.onwheel = function(){ return false;};
12092             
12093             (function(){ this.isMasked = true; }).defer(500, this);
12094             
12095         },
12096         
12097         unmask : function()
12098         {
12099             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12100                 return;
12101             }
12102             
12103             this.maskEl.top.setStyle('position', 'absolute');
12104             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12105             this.maskEl.top.hide();
12106
12107             this.maskEl.left.setStyle('position', 'absolute');
12108             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12109             this.maskEl.left.hide();
12110
12111             this.maskEl.bottom.setStyle('position', 'absolute');
12112             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12113             this.maskEl.bottom.hide();
12114
12115             this.maskEl.right.setStyle('position', 'absolute');
12116             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12117             this.maskEl.right.hide();
12118             
12119             this.toolTip.hide();
12120             
12121             this.toolTip.el.hide();
12122             
12123             window.onwheel = function(){ return true;};
12124             
12125             if(this.intervalID){
12126                 window.clearInterval(this.intervalID);
12127                 this.intervalID = false;
12128             }
12129             
12130             this.isMasked = false;
12131             
12132         }
12133         
12134     }
12135     
12136 });
12137
12138 /*
12139  * Based on:
12140  * Ext JS Library 1.1.1
12141  * Copyright(c) 2006-2007, Ext JS, LLC.
12142  *
12143  * Originally Released Under LGPL - original licence link has changed is not relivant.
12144  *
12145  * Fork - LGPL
12146  * <script type="text/javascript">
12147  */
12148 /**
12149  * @class Roo.form.VTypes
12150  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12151  * @static
12152  */
12153 Roo.form.VTypes = function(){
12154     // closure these in so they are only created once.
12155     var alpha = /^[a-zA-Z_]+$/;
12156     var alphanum = /^[a-zA-Z0-9_]+$/;
12157     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12158     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12159
12160     // All these messages and functions are configurable
12161     return {
12162         /**
12163          * The function used to validate email addresses
12164          * @param {String} value The email address
12165          */
12166         'email' : function(v){
12167             return email.test(v);
12168         },
12169         /**
12170          * The error text to display when the email validation function returns false
12171          * @type String
12172          */
12173         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12174         /**
12175          * The keystroke filter mask to be applied on email input
12176          * @type RegExp
12177          */
12178         'emailMask' : /[a-z0-9_\.\-@]/i,
12179
12180         /**
12181          * The function used to validate URLs
12182          * @param {String} value The URL
12183          */
12184         'url' : function(v){
12185             return url.test(v);
12186         },
12187         /**
12188          * The error text to display when the url validation function returns false
12189          * @type String
12190          */
12191         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12192         
12193         /**
12194          * The function used to validate alpha values
12195          * @param {String} value The value
12196          */
12197         'alpha' : function(v){
12198             return alpha.test(v);
12199         },
12200         /**
12201          * The error text to display when the alpha validation function returns false
12202          * @type String
12203          */
12204         'alphaText' : 'This field should only contain letters and _',
12205         /**
12206          * The keystroke filter mask to be applied on alpha input
12207          * @type RegExp
12208          */
12209         'alphaMask' : /[a-z_]/i,
12210
12211         /**
12212          * The function used to validate alphanumeric values
12213          * @param {String} value The value
12214          */
12215         'alphanum' : function(v){
12216             return alphanum.test(v);
12217         },
12218         /**
12219          * The error text to display when the alphanumeric validation function returns false
12220          * @type String
12221          */
12222         'alphanumText' : 'This field should only contain letters, numbers and _',
12223         /**
12224          * The keystroke filter mask to be applied on alphanumeric input
12225          * @type RegExp
12226          */
12227         'alphanumMask' : /[a-z0-9_]/i
12228     };
12229 }();/*
12230  * - LGPL
12231  *
12232  * Input
12233  * 
12234  */
12235
12236 /**
12237  * @class Roo.bootstrap.form.Input
12238  * @extends Roo.bootstrap.Component
12239  * Bootstrap Input class
12240  * @cfg {Boolean} disabled is it disabled
12241  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12242  * @cfg {String} name name of the input
12243  * @cfg {string} fieldLabel - the label associated
12244  * @cfg {string} placeholder - placeholder to put in text.
12245  * @cfg {string} before - input group add on before
12246  * @cfg {string} after - input group add on after
12247  * @cfg {string} size - (lg|sm) or leave empty..
12248  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12249  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12250  * @cfg {Number} md colspan out of 12 for computer-sized screens
12251  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12252  * @cfg {string} value default value of the input
12253  * @cfg {Number} labelWidth set the width of label 
12254  * @cfg {Number} labellg set the width of label (1-12)
12255  * @cfg {Number} labelmd set the width of label (1-12)
12256  * @cfg {Number} labelsm set the width of label (1-12)
12257  * @cfg {Number} labelxs set the width of label (1-12)
12258  * @cfg {String} labelAlign (top|left)
12259  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12260  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12261  * @cfg {String} indicatorpos (left|right) default left
12262  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12263  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12264  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12265  * @cfg {Roo.bootstrap.Button} before Button to show before
12266  * @cfg {Roo.bootstrap.Button} afterButton to show before
12267  * @cfg {String} align (left|center|right) Default left
12268  * @cfg {Boolean} forceFeedback (true|false) Default false
12269  * 
12270  * @constructor
12271  * Create a new Input
12272  * @param {Object} config The config object
12273  */
12274
12275 Roo.bootstrap.form.Input = function(config){
12276     
12277     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12278     
12279     this.addEvents({
12280         /**
12281          * @event focus
12282          * Fires when this field receives input focus.
12283          * @param {Roo.form.Field} this
12284          */
12285         focus : true,
12286         /**
12287          * @event blur
12288          * Fires when this field loses input focus.
12289          * @param {Roo.form.Field} this
12290          */
12291         blur : true,
12292         /**
12293          * @event specialkey
12294          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12295          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12296          * @param {Roo.form.Field} this
12297          * @param {Roo.EventObject} e The event object
12298          */
12299         specialkey : true,
12300         /**
12301          * @event change
12302          * Fires just before the field blurs if the field value has changed.
12303          * @param {Roo.form.Field} this
12304          * @param {Mixed} newValue The new value
12305          * @param {Mixed} oldValue The original value
12306          */
12307         change : true,
12308         /**
12309          * @event invalid
12310          * Fires after the field has been marked as invalid.
12311          * @param {Roo.form.Field} this
12312          * @param {String} msg The validation message
12313          */
12314         invalid : true,
12315         /**
12316          * @event valid
12317          * Fires after the field has been validated with no errors.
12318          * @param {Roo.form.Field} this
12319          */
12320         valid : true,
12321          /**
12322          * @event keyup
12323          * Fires after the key up
12324          * @param {Roo.form.Field} this
12325          * @param {Roo.EventObject}  e The event Object
12326          */
12327         keyup : true,
12328         /**
12329          * @event paste
12330          * Fires after the user pastes into input
12331          * @param {Roo.form.Field} this
12332          * @param {Roo.EventObject}  e The event Object
12333          */
12334         paste : true
12335     });
12336 };
12337
12338 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12339      /**
12340      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12341       automatic validation (defaults to "keyup").
12342      */
12343     validationEvent : "keyup",
12344      /**
12345      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12346      */
12347     validateOnBlur : true,
12348     /**
12349      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12350      */
12351     validationDelay : 250,
12352      /**
12353      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12354      */
12355     focusClass : "x-form-focus",  // not needed???
12356     
12357        
12358     /**
12359      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12360      */
12361     invalidClass : "has-warning",
12362     
12363     /**
12364      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     validClass : "has-success",
12367     
12368     /**
12369      * @cfg {Boolean} hasFeedback (true|false) default true
12370      */
12371     hasFeedback : true,
12372     
12373     /**
12374      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12375      */
12376     invalidFeedbackClass : "glyphicon-warning-sign",
12377     
12378     /**
12379      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     validFeedbackClass : "glyphicon-ok",
12382     
12383     /**
12384      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12385      */
12386     selectOnFocus : false,
12387     
12388      /**
12389      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12390      */
12391     maskRe : null,
12392        /**
12393      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12394      */
12395     vtype : null,
12396     
12397       /**
12398      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12399      */
12400     disableKeyFilter : false,
12401     
12402        /**
12403      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12404      */
12405     disabled : false,
12406      /**
12407      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12408      */
12409     allowBlank : true,
12410     /**
12411      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12412      */
12413     blankText : "Please complete this mandatory field",
12414     
12415      /**
12416      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12417      */
12418     minLength : 0,
12419     /**
12420      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12421      */
12422     maxLength : Number.MAX_VALUE,
12423     /**
12424      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12425      */
12426     minLengthText : "The minimum length for this field is {0}",
12427     /**
12428      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12429      */
12430     maxLengthText : "The maximum length for this field is {0}",
12431   
12432     
12433     /**
12434      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12435      * If available, this function will be called only after the basic validators all return true, and will be passed the
12436      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12437      */
12438     validator : null,
12439     /**
12440      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12441      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12442      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12443      */
12444     regex : null,
12445     /**
12446      * @cfg {String} regexText -- Depricated - use Invalid Text
12447      */
12448     regexText : "",
12449     
12450     /**
12451      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12452      */
12453     invalidText : "",
12454     
12455     
12456     
12457     autocomplete: false,
12458     
12459     
12460     fieldLabel : '',
12461     inputType : 'text',
12462     
12463     name : false,
12464     placeholder: false,
12465     before : false,
12466     after : false,
12467     size : false,
12468     hasFocus : false,
12469     preventMark: false,
12470     isFormField : true,
12471     value : '',
12472     labelWidth : 2,
12473     labelAlign : false,
12474     readOnly : false,
12475     align : false,
12476     formatedValue : false,
12477     forceFeedback : false,
12478     
12479     indicatorpos : 'left',
12480     
12481     labellg : 0,
12482     labelmd : 0,
12483     labelsm : 0,
12484     labelxs : 0,
12485     
12486     capture : '',
12487     accept : '',
12488     
12489     parentLabelAlign : function()
12490     {
12491         var parent = this;
12492         while (parent.parent()) {
12493             parent = parent.parent();
12494             if (typeof(parent.labelAlign) !='undefined') {
12495                 return parent.labelAlign;
12496             }
12497         }
12498         return 'left';
12499         
12500     },
12501     
12502     getAutoCreate : function()
12503     {
12504         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12505         
12506         var id = Roo.id();
12507         
12508         var cfg = {};
12509         
12510         if(this.inputType != 'hidden'){
12511             cfg.cls = 'form-group' //input-group
12512         }
12513         
12514         var input =  {
12515             tag: 'input',
12516             id : id,
12517             type : this.inputType,
12518             value : this.value,
12519             cls : 'form-control',
12520             placeholder : this.placeholder || '',
12521             autocomplete : this.autocomplete || 'new-password'
12522         };
12523         if (this.inputType == 'file') {
12524             input.style = 'overflow:hidden'; // why not in CSS?
12525         }
12526         
12527         if(this.capture.length){
12528             input.capture = this.capture;
12529         }
12530         
12531         if(this.accept.length){
12532             input.accept = this.accept + "/*";
12533         }
12534         
12535         if(this.align){
12536             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12537         }
12538         
12539         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12540             input.maxLength = this.maxLength;
12541         }
12542         
12543         if (this.disabled) {
12544             input.disabled=true;
12545         }
12546         
12547         if (this.readOnly) {
12548             input.readonly=true;
12549         }
12550         
12551         if (this.name) {
12552             input.name = this.name;
12553         }
12554         
12555         if (this.size) {
12556             input.cls += ' input-' + this.size;
12557         }
12558         
12559         var settings=this;
12560         ['xs','sm','md','lg'].map(function(size){
12561             if (settings[size]) {
12562                 cfg.cls += ' col-' + size + '-' + settings[size];
12563             }
12564         });
12565         
12566         var inputblock = input;
12567         
12568         var feedback = {
12569             tag: 'span',
12570             cls: 'glyphicon form-control-feedback'
12571         };
12572             
12573         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12574             
12575             inputblock = {
12576                 cls : 'has-feedback',
12577                 cn :  [
12578                     input,
12579                     feedback
12580                 ] 
12581             };  
12582         }
12583         
12584         if (this.before || this.after) {
12585             
12586             inputblock = {
12587                 cls : 'input-group',
12588                 cn :  [] 
12589             };
12590             
12591             if (this.before && typeof(this.before) == 'string') {
12592                 
12593                 inputblock.cn.push({
12594                     tag :'span',
12595                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12596                     html : this.before
12597                 });
12598             }
12599             if (this.before && typeof(this.before) == 'object') {
12600                 this.before = Roo.factory(this.before);
12601                 
12602                 inputblock.cn.push({
12603                     tag :'span',
12604                     cls : 'roo-input-before input-group-prepend   input-group-' +
12605                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12606                 });
12607             }
12608             
12609             inputblock.cn.push(input);
12610             
12611             if (this.after && typeof(this.after) == 'string') {
12612                 inputblock.cn.push({
12613                     tag :'span',
12614                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12615                     html : this.after
12616                 });
12617             }
12618             if (this.after && typeof(this.after) == 'object') {
12619                 this.after = Roo.factory(this.after);
12620                 
12621                 inputblock.cn.push({
12622                     tag :'span',
12623                     cls : 'roo-input-after input-group-append  input-group-' +
12624                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12625                 });
12626             }
12627             
12628             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12629                 inputblock.cls += ' has-feedback';
12630                 inputblock.cn.push(feedback);
12631             }
12632         };
12633         var indicator = {
12634             tag : 'i',
12635             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12636             tooltip : 'This field is required'
12637         };
12638         if (this.allowBlank ) {
12639             indicator.style = this.allowBlank ? ' display:none' : '';
12640         }
12641         if (align ==='left' && this.fieldLabel.length) {
12642             
12643             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12644             
12645             cfg.cn = [
12646                 indicator,
12647                 {
12648                     tag: 'label',
12649                     'for' :  id,
12650                     cls : 'control-label col-form-label',
12651                     html : this.fieldLabel
12652
12653                 },
12654                 {
12655                     cls : "", 
12656                     cn: [
12657                         inputblock
12658                     ]
12659                 }
12660             ];
12661             
12662             var labelCfg = cfg.cn[1];
12663             var contentCfg = cfg.cn[2];
12664             
12665             if(this.indicatorpos == 'right'){
12666                 cfg.cn = [
12667                     {
12668                         tag: 'label',
12669                         'for' :  id,
12670                         cls : 'control-label col-form-label',
12671                         cn : [
12672                             {
12673                                 tag : 'span',
12674                                 html : this.fieldLabel
12675                             },
12676                             indicator
12677                         ]
12678                     },
12679                     {
12680                         cls : "",
12681                         cn: [
12682                             inputblock
12683                         ]
12684                     }
12685
12686                 ];
12687                 
12688                 labelCfg = cfg.cn[0];
12689                 contentCfg = cfg.cn[1];
12690             
12691             }
12692             
12693             if(this.labelWidth > 12){
12694                 labelCfg.style = "width: " + this.labelWidth + 'px';
12695             }
12696             
12697             if(this.labelWidth < 13 && this.labelmd == 0){
12698                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12699             }
12700             
12701             if(this.labellg > 0){
12702                 labelCfg.cls += ' col-lg-' + this.labellg;
12703                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12704             }
12705             
12706             if(this.labelmd > 0){
12707                 labelCfg.cls += ' col-md-' + this.labelmd;
12708                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12709             }
12710             
12711             if(this.labelsm > 0){
12712                 labelCfg.cls += ' col-sm-' + this.labelsm;
12713                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12714             }
12715             
12716             if(this.labelxs > 0){
12717                 labelCfg.cls += ' col-xs-' + this.labelxs;
12718                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12719             }
12720             
12721             
12722         } else if ( this.fieldLabel.length) {
12723                 
12724             
12725             
12726             cfg.cn = [
12727                 {
12728                     tag : 'i',
12729                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12730                     tooltip : 'This field is required',
12731                     style : this.allowBlank ? ' display:none' : '' 
12732                 },
12733                 {
12734                     tag: 'label',
12735                    //cls : 'input-group-addon',
12736                     html : this.fieldLabel
12737
12738                 },
12739
12740                inputblock
12741
12742            ];
12743            
12744            if(this.indicatorpos == 'right'){
12745        
12746                 cfg.cn = [
12747                     {
12748                         tag: 'label',
12749                        //cls : 'input-group-addon',
12750                         html : this.fieldLabel
12751
12752                     },
12753                     {
12754                         tag : 'i',
12755                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12756                         tooltip : 'This field is required',
12757                         style : this.allowBlank ? ' display:none' : '' 
12758                     },
12759
12760                    inputblock
12761
12762                ];
12763
12764             }
12765
12766         } else {
12767             
12768             cfg.cn = [
12769
12770                     inputblock
12771
12772             ];
12773                 
12774                 
12775         };
12776         
12777         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12778            cfg.cls += ' navbar-form';
12779         }
12780         
12781         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12782             // on BS4 we do this only if not form 
12783             cfg.cls += ' navbar-form';
12784             cfg.tag = 'li';
12785         }
12786         
12787         return cfg;
12788         
12789     },
12790     /**
12791      * return the real input element.
12792      */
12793     inputEl: function ()
12794     {
12795         return this.el.select('input.form-control',true).first();
12796     },
12797     
12798     tooltipEl : function()
12799     {
12800         return this.inputEl();
12801     },
12802     
12803     indicatorEl : function()
12804     {
12805         if (Roo.bootstrap.version == 4) {
12806             return false; // not enabled in v4 yet.
12807         }
12808         
12809         var indicator = this.el.select('i.roo-required-indicator',true).first();
12810         
12811         if(!indicator){
12812             return false;
12813         }
12814         
12815         return indicator;
12816         
12817     },
12818     
12819     setDisabled : function(v)
12820     {
12821         var i  = this.inputEl().dom;
12822         if (!v) {
12823             i.removeAttribute('disabled');
12824             return;
12825             
12826         }
12827         i.setAttribute('disabled','true');
12828     },
12829     initEvents : function()
12830     {
12831           
12832         this.inputEl().on("keydown" , this.fireKey,  this);
12833         this.inputEl().on("focus", this.onFocus,  this);
12834         this.inputEl().on("blur", this.onBlur,  this);
12835         
12836         this.inputEl().relayEvent('keyup', this);
12837         this.inputEl().relayEvent('paste', this);
12838         
12839         this.indicator = this.indicatorEl();
12840         
12841         if(this.indicator){
12842             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12843         }
12844  
12845         // reference to original value for reset
12846         this.originalValue = this.getValue();
12847         //Roo.form.TextField.superclass.initEvents.call(this);
12848         if(this.validationEvent == 'keyup'){
12849             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12850             this.inputEl().on('keyup', this.filterValidation, this);
12851         }
12852         else if(this.validationEvent !== false){
12853             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12854         }
12855         
12856         if(this.selectOnFocus){
12857             this.on("focus", this.preFocus, this);
12858             
12859         }
12860         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12861             this.inputEl().on("keypress", this.filterKeys, this);
12862         } else {
12863             this.inputEl().relayEvent('keypress', this);
12864         }
12865        /* if(this.grow){
12866             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12867             this.el.on("click", this.autoSize,  this);
12868         }
12869         */
12870         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12871             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12872         }
12873         
12874         if (typeof(this.before) == 'object') {
12875             this.before.render(this.el.select('.roo-input-before',true).first());
12876         }
12877         if (typeof(this.after) == 'object') {
12878             this.after.render(this.el.select('.roo-input-after',true).first());
12879         }
12880         
12881         this.inputEl().on('change', this.onChange, this);
12882         
12883     },
12884     filterValidation : function(e){
12885         if(!e.isNavKeyPress()){
12886             this.validationTask.delay(this.validationDelay);
12887         }
12888     },
12889      /**
12890      * Validates the field value
12891      * @return {Boolean} True if the value is valid, else false
12892      */
12893     validate : function(){
12894         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12895         if(this.disabled || this.validateValue(this.getRawValue())){
12896             this.markValid();
12897             return true;
12898         }
12899         
12900         this.markInvalid();
12901         return false;
12902     },
12903     
12904     
12905     /**
12906      * Validates a value according to the field's validation rules and marks the field as invalid
12907      * if the validation fails
12908      * @param {Mixed} value The value to validate
12909      * @return {Boolean} True if the value is valid, else false
12910      */
12911     validateValue : function(value)
12912     {
12913         if(this.getVisibilityEl().hasClass('hidden')){
12914             return true;
12915         }
12916         
12917         if(value.length < 1)  { // if it's blank
12918             if(this.allowBlank){
12919                 return true;
12920             }
12921             return false;
12922         }
12923         
12924         if(value.length < this.minLength){
12925             return false;
12926         }
12927         if(value.length > this.maxLength){
12928             return false;
12929         }
12930         if(this.vtype){
12931             var vt = Roo.form.VTypes;
12932             if(!vt[this.vtype](value, this)){
12933                 return false;
12934             }
12935         }
12936         if(typeof this.validator == "function"){
12937             var msg = this.validator(value);
12938             if(msg !== true){
12939                 return false;
12940             }
12941             if (typeof(msg) == 'string') {
12942                 this.invalidText = msg;
12943             }
12944         }
12945         
12946         if(this.regex && !this.regex.test(value)){
12947             return false;
12948         }
12949         
12950         return true;
12951     },
12952     
12953      // private
12954     fireKey : function(e){
12955         //Roo.log('field ' + e.getKey());
12956         if(e.isNavKeyPress()){
12957             this.fireEvent("specialkey", this, e);
12958         }
12959     },
12960     focus : function (selectText){
12961         if(this.rendered){
12962             this.inputEl().focus();
12963             if(selectText === true){
12964                 this.inputEl().dom.select();
12965             }
12966         }
12967         return this;
12968     } ,
12969     
12970     onFocus : function(){
12971         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12972            // this.el.addClass(this.focusClass);
12973         }
12974         if(!this.hasFocus){
12975             this.hasFocus = true;
12976             this.startValue = this.getValue();
12977             this.fireEvent("focus", this);
12978         }
12979     },
12980     
12981     beforeBlur : Roo.emptyFn,
12982
12983     
12984     // private
12985     onBlur : function(){
12986         this.beforeBlur();
12987         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12988             //this.el.removeClass(this.focusClass);
12989         }
12990         this.hasFocus = false;
12991         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12992             this.validate();
12993         }
12994         var v = this.getValue();
12995         if(String(v) !== String(this.startValue)){
12996             this.fireEvent('change', this, v, this.startValue);
12997         }
12998         this.fireEvent("blur", this);
12999     },
13000     
13001     onChange : function(e)
13002     {
13003         var v = this.getValue();
13004         if(String(v) !== String(this.startValue)){
13005             this.fireEvent('change', this, v, this.startValue);
13006         }
13007         
13008     },
13009     
13010     /**
13011      * Resets the current field value to the originally loaded value and clears any validation messages
13012      */
13013     reset : function(){
13014         this.setValue(this.originalValue);
13015         this.validate();
13016     },
13017      /**
13018      * Returns the name of the field
13019      * @return {Mixed} name The name field
13020      */
13021     getName: function(){
13022         return this.name;
13023     },
13024      /**
13025      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13026      * @return {Mixed} value The field value
13027      */
13028     getValue : function(){
13029         
13030         var v = this.inputEl().getValue();
13031         
13032         return v;
13033     },
13034     /**
13035      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13036      * @return {Mixed} value The field value
13037      */
13038     getRawValue : function(){
13039         var v = this.inputEl().getValue();
13040         
13041         return v;
13042     },
13043     
13044     /**
13045      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13046      * @param {Mixed} value The value to set
13047      */
13048     setRawValue : function(v){
13049         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13050     },
13051     
13052     selectText : function(start, end){
13053         var v = this.getRawValue();
13054         if(v.length > 0){
13055             start = start === undefined ? 0 : start;
13056             end = end === undefined ? v.length : end;
13057             var d = this.inputEl().dom;
13058             if(d.setSelectionRange){
13059                 d.setSelectionRange(start, end);
13060             }else if(d.createTextRange){
13061                 var range = d.createTextRange();
13062                 range.moveStart("character", start);
13063                 range.moveEnd("character", v.length-end);
13064                 range.select();
13065             }
13066         }
13067     },
13068     
13069     /**
13070      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13071      * @param {Mixed} value The value to set
13072      */
13073     setValue : function(v){
13074         this.value = v;
13075         if(this.rendered){
13076             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13077             this.validate();
13078         }
13079     },
13080     
13081     /*
13082     processValue : function(value){
13083         if(this.stripCharsRe){
13084             var newValue = value.replace(this.stripCharsRe, '');
13085             if(newValue !== value){
13086                 this.setRawValue(newValue);
13087                 return newValue;
13088             }
13089         }
13090         return value;
13091     },
13092   */
13093     preFocus : function(){
13094         
13095         if(this.selectOnFocus){
13096             this.inputEl().dom.select();
13097         }
13098     },
13099     filterKeys : function(e){
13100         var k = e.getKey();
13101         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13102             return;
13103         }
13104         var c = e.getCharCode(), cc = String.fromCharCode(c);
13105         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13106             return;
13107         }
13108         if(!this.maskRe.test(cc)){
13109             e.stopEvent();
13110         }
13111     },
13112      /**
13113      * Clear any invalid styles/messages for this field
13114      */
13115     clearInvalid : function(){
13116         
13117         if(!this.el || this.preventMark){ // not rendered
13118             return;
13119         }
13120         
13121         
13122         this.el.removeClass([this.invalidClass, 'is-invalid']);
13123         
13124         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13125             
13126             var feedback = this.el.select('.form-control-feedback', true).first();
13127             
13128             if(feedback){
13129                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13130             }
13131             
13132         }
13133         
13134         if(this.indicator){
13135             this.indicator.removeClass('visible');
13136             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13137         }
13138         
13139         this.fireEvent('valid', this);
13140     },
13141     
13142      /**
13143      * Mark this field as valid
13144      */
13145     markValid : function()
13146     {
13147         if(!this.el  || this.preventMark){ // not rendered...
13148             return;
13149         }
13150         
13151         this.el.removeClass([this.invalidClass, this.validClass]);
13152         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13153
13154         var feedback = this.el.select('.form-control-feedback', true).first();
13155             
13156         if(feedback){
13157             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13158         }
13159         
13160         if(this.indicator){
13161             this.indicator.removeClass('visible');
13162             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13163         }
13164         
13165         if(this.disabled){
13166             return;
13167         }
13168         
13169            
13170         if(this.allowBlank && !this.getRawValue().length){
13171             return;
13172         }
13173         if (Roo.bootstrap.version == 3) {
13174             this.el.addClass(this.validClass);
13175         } else {
13176             this.inputEl().addClass('is-valid');
13177         }
13178
13179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180             
13181             var feedback = this.el.select('.form-control-feedback', true).first();
13182             
13183             if(feedback){
13184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186             }
13187             
13188         }
13189         
13190         this.fireEvent('valid', this);
13191     },
13192     
13193      /**
13194      * Mark this field as invalid
13195      * @param {String} msg The validation message
13196      */
13197     markInvalid : function(msg)
13198     {
13199         if(!this.el  || this.preventMark){ // not rendered
13200             return;
13201         }
13202         
13203         this.el.removeClass([this.invalidClass, this.validClass]);
13204         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205         
13206         var feedback = this.el.select('.form-control-feedback', true).first();
13207             
13208         if(feedback){
13209             this.el.select('.form-control-feedback', true).first().removeClass(
13210                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13211         }
13212
13213         if(this.disabled){
13214             return;
13215         }
13216         
13217         if(this.allowBlank && !this.getRawValue().length){
13218             return;
13219         }
13220         
13221         if(this.indicator){
13222             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13223             this.indicator.addClass('visible');
13224         }
13225         if (Roo.bootstrap.version == 3) {
13226             this.el.addClass(this.invalidClass);
13227         } else {
13228             this.inputEl().addClass('is-invalid');
13229         }
13230         
13231         
13232         
13233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13234             
13235             var feedback = this.el.select('.form-control-feedback', true).first();
13236             
13237             if(feedback){
13238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13239                 
13240                 if(this.getValue().length || this.forceFeedback){
13241                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13242                 }
13243                 
13244             }
13245             
13246         }
13247         
13248         this.fireEvent('invalid', this, msg);
13249     },
13250     // private
13251     SafariOnKeyDown : function(event)
13252     {
13253         // this is a workaround for a password hang bug on chrome/ webkit.
13254         if (this.inputEl().dom.type != 'password') {
13255             return;
13256         }
13257         
13258         var isSelectAll = false;
13259         
13260         if(this.inputEl().dom.selectionEnd > 0){
13261             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13262         }
13263         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13264             event.preventDefault();
13265             this.setValue('');
13266             return;
13267         }
13268         
13269         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13270             
13271             event.preventDefault();
13272             // this is very hacky as keydown always get's upper case.
13273             //
13274             var cc = String.fromCharCode(event.getCharCode());
13275             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13276             
13277         }
13278     },
13279     adjustWidth : function(tag, w){
13280         tag = tag.toLowerCase();
13281         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13282             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13283                 if(tag == 'input'){
13284                     return w + 2;
13285                 }
13286                 if(tag == 'textarea'){
13287                     return w-2;
13288                 }
13289             }else if(Roo.isOpera){
13290                 if(tag == 'input'){
13291                     return w + 2;
13292                 }
13293                 if(tag == 'textarea'){
13294                     return w-2;
13295                 }
13296             }
13297         }
13298         return w;
13299     },
13300     
13301     setFieldLabel : function(v)
13302     {
13303         if(!this.rendered){
13304             return;
13305         }
13306         
13307         if(this.indicatorEl()){
13308             var ar = this.el.select('label > span',true);
13309             
13310             if (ar.elements.length) {
13311                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312                 this.fieldLabel = v;
13313                 return;
13314             }
13315             
13316             var br = this.el.select('label',true);
13317             
13318             if(br.elements.length) {
13319                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13320                 this.fieldLabel = v;
13321                 return;
13322             }
13323             
13324             Roo.log('Cannot Found any of label > span || label in input');
13325             return;
13326         }
13327         
13328         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13329         this.fieldLabel = v;
13330         
13331         
13332     }
13333 });
13334
13335  
13336 /*
13337  * - LGPL
13338  *
13339  * Input
13340  * 
13341  */
13342
13343 /**
13344  * @class Roo.bootstrap.form.TextArea
13345  * @extends Roo.bootstrap.form.Input
13346  * Bootstrap TextArea class
13347  * @cfg {Number} cols Specifies the visible width of a text area
13348  * @cfg {Number} rows Specifies the visible number of lines in a text area
13349  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13350  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13351  * @cfg {string} html text
13352  * 
13353  * @constructor
13354  * Create a new TextArea
13355  * @param {Object} config The config object
13356  */
13357
13358 Roo.bootstrap.form.TextArea = function(config){
13359     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13360    
13361 };
13362
13363 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13364      
13365     cols : false,
13366     rows : 5,
13367     readOnly : false,
13368     warp : 'soft',
13369     resize : false,
13370     value: false,
13371     html: false,
13372     
13373     getAutoCreate : function(){
13374         
13375         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13376         
13377         var id = Roo.id();
13378         
13379         var cfg = {};
13380         
13381         if(this.inputType != 'hidden'){
13382             cfg.cls = 'form-group' //input-group
13383         }
13384         
13385         var input =  {
13386             tag: 'textarea',
13387             id : id,
13388             warp : this.warp,
13389             rows : this.rows,
13390             value : this.value || '',
13391             html: this.html || '',
13392             cls : 'form-control',
13393             placeholder : this.placeholder || '' 
13394             
13395         };
13396         
13397         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13398             input.maxLength = this.maxLength;
13399         }
13400         
13401         if(this.resize){
13402             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13403         }
13404         
13405         if(this.cols){
13406             input.cols = this.cols;
13407         }
13408         
13409         if (this.readOnly) {
13410             input.readonly = true;
13411         }
13412         
13413         if (this.name) {
13414             input.name = this.name;
13415         }
13416         
13417         if (this.size) {
13418             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13419         }
13420         
13421         var settings=this;
13422         ['xs','sm','md','lg'].map(function(size){
13423             if (settings[size]) {
13424                 cfg.cls += ' col-' + size + '-' + settings[size];
13425             }
13426         });
13427         
13428         var inputblock = input;
13429         
13430         if(this.hasFeedback && !this.allowBlank){
13431             
13432             var feedback = {
13433                 tag: 'span',
13434                 cls: 'glyphicon form-control-feedback'
13435             };
13436
13437             inputblock = {
13438                 cls : 'has-feedback',
13439                 cn :  [
13440                     input,
13441                     feedback
13442                 ] 
13443             };  
13444         }
13445         
13446         
13447         if (this.before || this.after) {
13448             
13449             inputblock = {
13450                 cls : 'input-group',
13451                 cn :  [] 
13452             };
13453             if (this.before) {
13454                 inputblock.cn.push({
13455                     tag :'span',
13456                     cls : 'input-group-addon',
13457                     html : this.before
13458                 });
13459             }
13460             
13461             inputblock.cn.push(input);
13462             
13463             if(this.hasFeedback && !this.allowBlank){
13464                 inputblock.cls += ' has-feedback';
13465                 inputblock.cn.push(feedback);
13466             }
13467             
13468             if (this.after) {
13469                 inputblock.cn.push({
13470                     tag :'span',
13471                     cls : 'input-group-addon',
13472                     html : this.after
13473                 });
13474             }
13475             
13476         }
13477         
13478         if (align ==='left' && this.fieldLabel.length) {
13479             cfg.cn = [
13480                 {
13481                     tag: 'label',
13482                     'for' :  id,
13483                     cls : 'control-label',
13484                     html : this.fieldLabel
13485                 },
13486                 {
13487                     cls : "",
13488                     cn: [
13489                         inputblock
13490                     ]
13491                 }
13492
13493             ];
13494             
13495             if(this.labelWidth > 12){
13496                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13497             }
13498
13499             if(this.labelWidth < 13 && this.labelmd == 0){
13500                 this.labelmd = this.labelWidth;
13501             }
13502
13503             if(this.labellg > 0){
13504                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13505                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13506             }
13507
13508             if(this.labelmd > 0){
13509                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13510                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13511             }
13512
13513             if(this.labelsm > 0){
13514                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13515                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13516             }
13517
13518             if(this.labelxs > 0){
13519                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13520                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13521             }
13522             
13523         } else if ( this.fieldLabel.length) {
13524             cfg.cn = [
13525
13526                {
13527                    tag: 'label',
13528                    //cls : 'input-group-addon',
13529                    html : this.fieldLabel
13530
13531                },
13532
13533                inputblock
13534
13535            ];
13536
13537         } else {
13538
13539             cfg.cn = [
13540
13541                 inputblock
13542
13543             ];
13544                 
13545         }
13546         
13547         if (this.disabled) {
13548             input.disabled=true;
13549         }
13550         
13551         return cfg;
13552         
13553     },
13554     /**
13555      * return the real textarea element.
13556      */
13557     inputEl: function ()
13558     {
13559         return this.el.select('textarea.form-control',true).first();
13560     },
13561     
13562     /**
13563      * Clear any invalid styles/messages for this field
13564      */
13565     clearInvalid : function()
13566     {
13567         
13568         if(!this.el || this.preventMark){ // not rendered
13569             return;
13570         }
13571         
13572         var label = this.el.select('label', true).first();
13573         var icon = this.el.select('i.fa-star', true).first();
13574         
13575         if(label && icon){
13576             icon.remove();
13577         }
13578         this.el.removeClass( this.validClass);
13579         this.inputEl().removeClass('is-invalid');
13580          
13581         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13582             
13583             var feedback = this.el.select('.form-control-feedback', true).first();
13584             
13585             if(feedback){
13586                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13587             }
13588             
13589         }
13590         
13591         this.fireEvent('valid', this);
13592     },
13593     
13594      /**
13595      * Mark this field as valid
13596      */
13597     markValid : function()
13598     {
13599         if(!this.el  || this.preventMark){ // not rendered
13600             return;
13601         }
13602         
13603         this.el.removeClass([this.invalidClass, this.validClass]);
13604         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13605         
13606         var feedback = this.el.select('.form-control-feedback', true).first();
13607             
13608         if(feedback){
13609             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13610         }
13611
13612         if(this.disabled || this.allowBlank){
13613             return;
13614         }
13615         
13616         var label = this.el.select('label', true).first();
13617         var icon = this.el.select('i.fa-star', true).first();
13618         
13619         if(label && icon){
13620             icon.remove();
13621         }
13622         if (Roo.bootstrap.version == 3) {
13623             this.el.addClass(this.validClass);
13624         } else {
13625             this.inputEl().addClass('is-valid');
13626         }
13627         
13628         
13629         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13630             
13631             var feedback = this.el.select('.form-control-feedback', true).first();
13632             
13633             if(feedback){
13634                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13635                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13636             }
13637             
13638         }
13639         
13640         this.fireEvent('valid', this);
13641     },
13642     
13643      /**
13644      * Mark this field as invalid
13645      * @param {String} msg The validation message
13646      */
13647     markInvalid : function(msg)
13648     {
13649         if(!this.el  || this.preventMark){ // not rendered
13650             return;
13651         }
13652         
13653         this.el.removeClass([this.invalidClass, this.validClass]);
13654         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655         
13656         var feedback = this.el.select('.form-control-feedback', true).first();
13657             
13658         if(feedback){
13659             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13660         }
13661
13662         if(this.disabled || this.allowBlank){
13663             return;
13664         }
13665         
13666         var label = this.el.select('label', true).first();
13667         var icon = this.el.select('i.fa-star', true).first();
13668         
13669         if(!this.getValue().length && label && !icon){
13670             this.el.createChild({
13671                 tag : 'i',
13672                 cls : 'text-danger fa fa-lg fa-star',
13673                 tooltip : 'This field is required',
13674                 style : 'margin-right:5px;'
13675             }, label, true);
13676         }
13677         
13678         if (Roo.bootstrap.version == 3) {
13679             this.el.addClass(this.invalidClass);
13680         } else {
13681             this.inputEl().addClass('is-invalid');
13682         }
13683         
13684         // fixme ... this may be depricated need to test..
13685         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13686             
13687             var feedback = this.el.select('.form-control-feedback', true).first();
13688             
13689             if(feedback){
13690                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13691                 
13692                 if(this.getValue().length || this.forceFeedback){
13693                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13694                 }
13695                 
13696             }
13697             
13698         }
13699         
13700         this.fireEvent('invalid', this, msg);
13701     }
13702 });
13703
13704  
13705 /*
13706  * - LGPL
13707  *
13708  * trigger field - base class for combo..
13709  * 
13710  */
13711  
13712 /**
13713  * @class Roo.bootstrap.form.TriggerField
13714  * @extends Roo.bootstrap.form.Input
13715  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718  * for which you can provide a custom implementation.  For example:
13719  * <pre><code>
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13723 </code></pre>
13724  *
13725  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13728  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13730
13731  * @constructor
13732  * Create a new TriggerField.
13733  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734  * to the base TextField)
13735  */
13736 Roo.bootstrap.form.TriggerField = function(config){
13737     this.mimicing = false;
13738     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13739 };
13740
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13742     /**
13743      * @cfg {String} triggerClass A CSS class to apply to the trigger
13744      */
13745      /**
13746      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13747      */
13748     hideTrigger:false,
13749
13750     /**
13751      * @cfg {Boolean} removable (true|false) special filter default false
13752      */
13753     removable : false,
13754     
13755     /** @cfg {Boolean} grow @hide */
13756     /** @cfg {Number} growMin @hide */
13757     /** @cfg {Number} growMax @hide */
13758
13759     /**
13760      * @hide 
13761      * @method
13762      */
13763     autoSize: Roo.emptyFn,
13764     // private
13765     monitorTab : true,
13766     // private
13767     deferHeight : true,
13768
13769     
13770     actionMode : 'wrap',
13771     
13772     caret : false,
13773     
13774     
13775     getAutoCreate : function(){
13776        
13777         var align = this.labelAlign || this.parentLabelAlign();
13778         
13779         var id = Roo.id();
13780         
13781         var cfg = {
13782             cls: 'form-group' //input-group
13783         };
13784         
13785         
13786         var input =  {
13787             tag: 'input',
13788             id : id,
13789             type : this.inputType,
13790             cls : 'form-control',
13791             autocomplete: 'new-password',
13792             placeholder : this.placeholder || '' 
13793             
13794         };
13795         if (this.name) {
13796             input.name = this.name;
13797         }
13798         if (this.size) {
13799             input.cls += ' input-' + this.size;
13800         }
13801         
13802         if (this.disabled) {
13803             input.disabled=true;
13804         }
13805         
13806         var inputblock = input;
13807         
13808         if(this.hasFeedback && !this.allowBlank){
13809             
13810             var feedback = {
13811                 tag: 'span',
13812                 cls: 'glyphicon form-control-feedback'
13813             };
13814             
13815             if(this.removable && !this.editable  ){
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         {
13821                             tag: 'button',
13822                             html : 'x',
13823                             cls : 'roo-combo-removable-btn close'
13824                         },
13825                         feedback
13826                     ] 
13827                 };
13828             } else {
13829                 inputblock = {
13830                     cls : 'has-feedback',
13831                     cn :  [
13832                         inputblock,
13833                         feedback
13834                     ] 
13835                 };
13836             }
13837
13838         } else {
13839             if(this.removable && !this.editable ){
13840                 inputblock = {
13841                     cls : 'roo-removable',
13842                     cn :  [
13843                         inputblock,
13844                         {
13845                             tag: 'button',
13846                             html : 'x',
13847                             cls : 'roo-combo-removable-btn close'
13848                         }
13849                     ] 
13850                 };
13851             }
13852         }
13853         
13854         if (this.before || this.after) {
13855             
13856             inputblock = {
13857                 cls : 'input-group',
13858                 cn :  [] 
13859             };
13860             if (this.before) {
13861                 inputblock.cn.push({
13862                     tag :'span',
13863                     cls : 'input-group-addon input-group-prepend input-group-text',
13864                     html : this.before
13865                 });
13866             }
13867             
13868             inputblock.cn.push(input);
13869             
13870             if(this.hasFeedback && !this.allowBlank){
13871                 inputblock.cls += ' has-feedback';
13872                 inputblock.cn.push(feedback);
13873             }
13874             
13875             if (this.after) {
13876                 inputblock.cn.push({
13877                     tag :'span',
13878                     cls : 'input-group-addon input-group-append input-group-text',
13879                     html : this.after
13880                 });
13881             }
13882             
13883         };
13884         
13885       
13886         
13887         var ibwrap = inputblock;
13888         
13889         if(this.multiple){
13890             ibwrap = {
13891                 tag: 'ul',
13892                 cls: 'roo-select2-choices',
13893                 cn:[
13894                     {
13895                         tag: 'li',
13896                         cls: 'roo-select2-search-field',
13897                         cn: [
13898
13899                             inputblock
13900                         ]
13901                     }
13902                 ]
13903             };
13904                 
13905         }
13906         
13907         var combobox = {
13908             cls: 'roo-select2-container input-group',
13909             cn: [
13910                  {
13911                     tag: 'input',
13912                     type : 'hidden',
13913                     cls: 'form-hidden-field'
13914                 },
13915                 ibwrap
13916             ]
13917         };
13918         
13919         if(!this.multiple && this.showToggleBtn){
13920             
13921             var caret = {
13922                         tag: 'span',
13923                         cls: 'caret'
13924              };
13925             if (this.caret != false) {
13926                 caret = {
13927                      tag: 'i',
13928                      cls: 'fa fa-' + this.caret
13929                 };
13930                 
13931             }
13932             
13933             combobox.cn.push({
13934                 tag :'span',
13935                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13936                 cn : [
13937                     Roo.bootstrap.version == 3 ? caret : '',
13938                     {
13939                         tag: 'span',
13940                         cls: 'combobox-clear',
13941                         cn  : [
13942                             {
13943                                 tag : 'i',
13944                                 cls: 'icon-remove'
13945                             }
13946                         ]
13947                     }
13948                 ]
13949
13950             })
13951         }
13952         
13953         if(this.multiple){
13954             combobox.cls += ' roo-select2-container-multi';
13955         }
13956          var indicator = {
13957             tag : 'i',
13958             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959             tooltip : 'This field is required'
13960         };
13961         if (Roo.bootstrap.version == 4) {
13962             indicator = {
13963                 tag : 'i',
13964                 style : 'display:none'
13965             };
13966         }
13967         
13968         
13969         if (align ==='left' && this.fieldLabel.length) {
13970             
13971             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13972
13973             cfg.cn = [
13974                 indicator,
13975                 {
13976                     tag: 'label',
13977                     'for' :  id,
13978                     cls : 'control-label',
13979                     html : this.fieldLabel
13980
13981                 },
13982                 {
13983                     cls : "", 
13984                     cn: [
13985                         combobox
13986                     ]
13987                 }
13988
13989             ];
13990             
13991             var labelCfg = cfg.cn[1];
13992             var contentCfg = cfg.cn[2];
13993             
13994             if(this.indicatorpos == 'right'){
13995                 cfg.cn = [
13996                     {
13997                         tag: 'label',
13998                         'for' :  id,
13999                         cls : 'control-label',
14000                         cn : [
14001                             {
14002                                 tag : 'span',
14003                                 html : this.fieldLabel
14004                             },
14005                             indicator
14006                         ]
14007                     },
14008                     {
14009                         cls : "", 
14010                         cn: [
14011                             combobox
14012                         ]
14013                     }
14014
14015                 ];
14016                 
14017                 labelCfg = cfg.cn[0];
14018                 contentCfg = cfg.cn[1];
14019             }
14020             
14021             if(this.labelWidth > 12){
14022                 labelCfg.style = "width: " + this.labelWidth + 'px';
14023             }
14024             
14025             if(this.labelWidth < 13 && this.labelmd == 0){
14026                 this.labelmd = this.labelWidth;
14027             }
14028             
14029             if(this.labellg > 0){
14030                 labelCfg.cls += ' col-lg-' + this.labellg;
14031                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14032             }
14033             
14034             if(this.labelmd > 0){
14035                 labelCfg.cls += ' col-md-' + this.labelmd;
14036                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14037             }
14038             
14039             if(this.labelsm > 0){
14040                 labelCfg.cls += ' col-sm-' + this.labelsm;
14041                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14042             }
14043             
14044             if(this.labelxs > 0){
14045                 labelCfg.cls += ' col-xs-' + this.labelxs;
14046                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14047             }
14048             
14049         } else if ( this.fieldLabel.length) {
14050 //                Roo.log(" label");
14051             cfg.cn = [
14052                 indicator,
14053                {
14054                    tag: 'label',
14055                    //cls : 'input-group-addon',
14056                    html : this.fieldLabel
14057
14058                },
14059
14060                combobox
14061
14062             ];
14063             
14064             if(this.indicatorpos == 'right'){
14065                 
14066                 cfg.cn = [
14067                     {
14068                        tag: 'label',
14069                        cn : [
14070                            {
14071                                tag : 'span',
14072                                html : this.fieldLabel
14073                            },
14074                            indicator
14075                        ]
14076
14077                     },
14078                     combobox
14079
14080                 ];
14081
14082             }
14083
14084         } else {
14085             
14086 //                Roo.log(" no label && no align");
14087                 cfg = combobox
14088                      
14089                 
14090         }
14091         
14092         var settings=this;
14093         ['xs','sm','md','lg'].map(function(size){
14094             if (settings[size]) {
14095                 cfg.cls += ' col-' + size + '-' + settings[size];
14096             }
14097         });
14098         
14099         return cfg;
14100         
14101     },
14102     
14103     
14104     
14105     // private
14106     onResize : function(w, h){
14107 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14108 //        if(typeof w == 'number'){
14109 //            var x = w - this.trigger.getWidth();
14110 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14111 //            this.trigger.setStyle('left', x+'px');
14112 //        }
14113     },
14114
14115     // private
14116     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14117
14118     // private
14119     getResizeEl : function(){
14120         return this.inputEl();
14121     },
14122
14123     // private
14124     getPositionEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     alignErrorIcon : function(){
14130         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14131     },
14132
14133     // private
14134     initEvents : function(){
14135         
14136         this.createList();
14137         
14138         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14139         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14140         if(!this.multiple && this.showToggleBtn){
14141             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14142             if(this.hideTrigger){
14143                 this.trigger.setDisplayed(false);
14144             }
14145             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14146         }
14147         
14148         if(this.multiple){
14149             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14150         }
14151         
14152         if(this.removable && !this.editable && !this.tickable){
14153             var close = this.closeTriggerEl();
14154             
14155             if(close){
14156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14157                 close.on('click', this.removeBtnClick, this, close);
14158             }
14159         }
14160         
14161         //this.trigger.addClassOnOver('x-form-trigger-over');
14162         //this.trigger.addClassOnClick('x-form-trigger-click');
14163         
14164         //if(!this.width){
14165         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14166         //}
14167     },
14168     
14169     closeTriggerEl : function()
14170     {
14171         var close = this.el.select('.roo-combo-removable-btn', true).first();
14172         return close ? close : false;
14173     },
14174     
14175     removeBtnClick : function(e, h, el)
14176     {
14177         e.preventDefault();
14178         
14179         if(this.fireEvent("remove", this) !== false){
14180             this.reset();
14181             this.fireEvent("afterremove", this)
14182         }
14183     },
14184     
14185     createList : function()
14186     {
14187         this.list = Roo.get(document.body).createChild({
14188             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14189             cls: 'typeahead typeahead-long dropdown-menu shadow',
14190             style: 'display:none'
14191         });
14192         
14193         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14194         
14195     },
14196
14197     // private
14198     initTrigger : function(){
14199        
14200     },
14201
14202     // private
14203     onDestroy : function(){
14204         if(this.trigger){
14205             this.trigger.removeAllListeners();
14206           //  this.trigger.remove();
14207         }
14208         //if(this.wrap){
14209         //    this.wrap.remove();
14210         //}
14211         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14212     },
14213
14214     // private
14215     onFocus : function(){
14216         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14217         /*
14218         if(!this.mimicing){
14219             this.wrap.addClass('x-trigger-wrap-focus');
14220             this.mimicing = true;
14221             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14222             if(this.monitorTab){
14223                 this.el.on("keydown", this.checkTab, this);
14224             }
14225         }
14226         */
14227     },
14228
14229     // private
14230     checkTab : function(e){
14231         if(e.getKey() == e.TAB){
14232             this.triggerBlur();
14233         }
14234     },
14235
14236     // private
14237     onBlur : function(){
14238         // do nothing
14239     },
14240
14241     // private
14242     mimicBlur : function(e, t){
14243         /*
14244         if(!this.wrap.contains(t) && this.validateBlur()){
14245             this.triggerBlur();
14246         }
14247         */
14248     },
14249
14250     // private
14251     triggerBlur : function(){
14252         this.mimicing = false;
14253         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14254         if(this.monitorTab){
14255             this.el.un("keydown", this.checkTab, this);
14256         }
14257         //this.wrap.removeClass('x-trigger-wrap-focus');
14258         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14259     },
14260
14261     // private
14262     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14263     validateBlur : function(e, t){
14264         return true;
14265     },
14266
14267     // private
14268     onDisable : function(){
14269         this.inputEl().dom.disabled = true;
14270         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14271         //if(this.wrap){
14272         //    this.wrap.addClass('x-item-disabled');
14273         //}
14274     },
14275
14276     // private
14277     onEnable : function(){
14278         this.inputEl().dom.disabled = false;
14279         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14280         //if(this.wrap){
14281         //    this.el.removeClass('x-item-disabled');
14282         //}
14283     },
14284
14285     // private
14286     onShow : function(){
14287         var ae = this.getActionEl();
14288         
14289         if(ae){
14290             ae.dom.style.display = '';
14291             ae.dom.style.visibility = 'visible';
14292         }
14293     },
14294
14295     // private
14296     
14297     onHide : function(){
14298         var ae = this.getActionEl();
14299         ae.dom.style.display = 'none';
14300     },
14301
14302     /**
14303      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14304      * by an implementing function.
14305      * @method
14306      * @param {EventObject} e
14307      */
14308     onTriggerClick : Roo.emptyFn
14309 });
14310  
14311 /*
14312 * Licence: LGPL
14313 */
14314
14315 /**
14316  * @class Roo.bootstrap.form.CardUploader
14317  * @extends Roo.bootstrap.Button
14318  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14319  * @cfg {Number} errorTimeout default 3000
14320  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14321  * @cfg {Array}  html The button text.
14322
14323  *
14324  * @constructor
14325  * Create a new CardUploader
14326  * @param {Object} config The config object
14327  */
14328
14329 Roo.bootstrap.form.CardUploader = function(config){
14330     
14331  
14332     
14333     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14334     
14335     
14336     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14337         return r.data.id
14338      });
14339     
14340      this.addEvents({
14341          // raw events
14342         /**
14343          * @event preview
14344          * When a image is clicked on - and needs to display a slideshow or similar..
14345          * @param {Roo.bootstrap.Card} this
14346          * @param {Object} The image information data 
14347          *
14348          */
14349         'preview' : true,
14350          /**
14351          * @event download
14352          * When a the download link is clicked
14353          * @param {Roo.bootstrap.Card} this
14354          * @param {Object} The image information data  contains 
14355          */
14356         'download' : true
14357         
14358     });
14359 };
14360  
14361 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14362     
14363      
14364     errorTimeout : 3000,
14365      
14366     images : false,
14367    
14368     fileCollection : false,
14369     allowBlank : true,
14370     
14371     getAutoCreate : function()
14372     {
14373         
14374         var cfg =  {
14375             cls :'form-group' ,
14376             cn : [
14377                
14378                 {
14379                     tag: 'label',
14380                    //cls : 'input-group-addon',
14381                     html : this.fieldLabel
14382
14383                 },
14384
14385                 {
14386                     tag: 'input',
14387                     type : 'hidden',
14388                     name : this.name,
14389                     value : this.value,
14390                     cls : 'd-none  form-control'
14391                 },
14392                 
14393                 {
14394                     tag: 'input',
14395                     multiple : 'multiple',
14396                     type : 'file',
14397                     cls : 'd-none  roo-card-upload-selector'
14398                 },
14399                 
14400                 {
14401                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14402                 },
14403                 {
14404                     cls : 'card-columns roo-card-uploader-container'
14405                 }
14406
14407             ]
14408         };
14409            
14410          
14411         return cfg;
14412     },
14413     
14414     getChildContainer : function() /// what children are added to.
14415     {
14416         return this.containerEl;
14417     },
14418    
14419     getButtonContainer : function() /// what children are added to.
14420     {
14421         return this.el.select(".roo-card-uploader-button-container").first();
14422     },
14423    
14424     initEvents : function()
14425     {
14426         
14427         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14428         
14429         var t = this;
14430         this.addxtype({
14431             xns: Roo.bootstrap,
14432
14433             xtype : 'Button',
14434             container_method : 'getButtonContainer' ,            
14435             html :  this.html, // fix changable?
14436             cls : 'w-100 ',
14437             listeners : {
14438                 'click' : function(btn, e) {
14439                     t.onClick(e);
14440                 }
14441             }
14442         });
14443         
14444         
14445         
14446         
14447         this.urlAPI = (window.createObjectURL && window) || 
14448                                 (window.URL && URL.revokeObjectURL && URL) || 
14449                                 (window.webkitURL && webkitURL);
14450                         
14451          
14452          
14453          
14454         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14455         
14456         this.selectorEl.on('change', this.onFileSelected, this);
14457         if (this.images) {
14458             var t = this;
14459             this.images.forEach(function(img) {
14460                 t.addCard(img)
14461             });
14462             this.images = false;
14463         }
14464         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14465          
14466        
14467     },
14468     
14469    
14470     onClick : function(e)
14471     {
14472         e.preventDefault();
14473          
14474         this.selectorEl.dom.click();
14475          
14476     },
14477     
14478     onFileSelected : function(e)
14479     {
14480         e.preventDefault();
14481         
14482         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14483             return;
14484         }
14485         
14486         Roo.each(this.selectorEl.dom.files, function(file){    
14487             this.addFile(file);
14488         }, this);
14489          
14490     },
14491     
14492       
14493     
14494       
14495     
14496     addFile : function(file)
14497     {
14498            
14499         if(typeof(file) === 'string'){
14500             throw "Add file by name?"; // should not happen
14501             return;
14502         }
14503         
14504         if(!file || !this.urlAPI){
14505             return;
14506         }
14507         
14508         // file;
14509         // file.type;
14510         
14511         var _this = this;
14512         
14513         
14514         var url = _this.urlAPI.createObjectURL( file);
14515            
14516         this.addCard({
14517             id : Roo.bootstrap.form.CardUploader.ID--,
14518             is_uploaded : false,
14519             src : url,
14520             srcfile : file,
14521             title : file.name,
14522             mimetype : file.type,
14523             preview : false,
14524             is_deleted : 0
14525         });
14526         
14527     },
14528     
14529     /**
14530      * addCard - add an Attachment to the uploader
14531      * @param data - the data about the image to upload
14532      *
14533      * {
14534           id : 123
14535           title : "Title of file",
14536           is_uploaded : false,
14537           src : "http://.....",
14538           srcfile : { the File upload object },
14539           mimetype : file.type,
14540           preview : false,
14541           is_deleted : 0
14542           .. any other data...
14543         }
14544      *
14545      * 
14546     */
14547     
14548     addCard : function (data)
14549     {
14550         // hidden input element?
14551         // if the file is not an image...
14552         //then we need to use something other that and header_image
14553         var t = this;
14554         //   remove.....
14555         var footer = [
14556             {
14557                 xns : Roo.bootstrap,
14558                 xtype : 'CardFooter',
14559                  items: [
14560                     {
14561                         xns : Roo.bootstrap,
14562                         xtype : 'Element',
14563                         cls : 'd-flex',
14564                         items : [
14565                             
14566                             {
14567                                 xns : Roo.bootstrap,
14568                                 xtype : 'Button',
14569                                 html : String.format("<small>{0}</small>", data.title),
14570                                 cls : 'col-10 text-left',
14571                                 size: 'sm',
14572                                 weight: 'link',
14573                                 fa : 'download',
14574                                 listeners : {
14575                                     click : function() {
14576                                      
14577                                         t.fireEvent( "download", t, data );
14578                                     }
14579                                 }
14580                             },
14581                           
14582                             {
14583                                 xns : Roo.bootstrap,
14584                                 xtype : 'Button',
14585                                 style: 'max-height: 28px; ',
14586                                 size : 'sm',
14587                                 weight: 'danger',
14588                                 cls : 'col-2',
14589                                 fa : 'times',
14590                                 listeners : {
14591                                     click : function() {
14592                                         t.removeCard(data.id)
14593                                     }
14594                                 }
14595                             }
14596                         ]
14597                     }
14598                     
14599                 ] 
14600             }
14601             
14602         ];
14603         
14604         var cn = this.addxtype(
14605             {
14606                  
14607                 xns : Roo.bootstrap,
14608                 xtype : 'Card',
14609                 closeable : true,
14610                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14611                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14612                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14613                 data : data,
14614                 html : false,
14615                  
14616                 items : footer,
14617                 initEvents : function() {
14618                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14619                     var card = this;
14620                     this.imgEl = this.el.select('.card-img-top').first();
14621                     if (this.imgEl) {
14622                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14623                         this.imgEl.set({ 'pointer' : 'cursor' });
14624                                   
14625                     }
14626                     this.getCardFooter().addClass('p-1');
14627                     
14628                   
14629                 }
14630                 
14631             }
14632         );
14633         // dont' really need ot update items.
14634         // this.items.push(cn);
14635         this.fileCollection.add(cn);
14636         
14637         if (!data.srcfile) {
14638             this.updateInput();
14639             return;
14640         }
14641             
14642         var _t = this;
14643         var reader = new FileReader();
14644         reader.addEventListener("load", function() {  
14645             data.srcdata =  reader.result;
14646             _t.updateInput();
14647         });
14648         reader.readAsDataURL(data.srcfile);
14649         
14650         
14651         
14652     },
14653     removeCard : function(id)
14654     {
14655         
14656         var card  = this.fileCollection.get(id);
14657         card.data.is_deleted = 1;
14658         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14659         //this.fileCollection.remove(card);
14660         //this.items = this.items.filter(function(e) { return e != card });
14661         // dont' really need ot update items.
14662         card.el.dom.parentNode.removeChild(card.el.dom);
14663         this.updateInput();
14664
14665         
14666     },
14667     reset: function()
14668     {
14669         this.fileCollection.each(function(card) {
14670             if (card.el.dom && card.el.dom.parentNode) {
14671                 card.el.dom.parentNode.removeChild(card.el.dom);
14672             }
14673         });
14674         this.fileCollection.clear();
14675         this.updateInput();
14676     },
14677     
14678     updateInput : function()
14679     {
14680          var data = [];
14681         this.fileCollection.each(function(e) {
14682             data.push(e.data);
14683             
14684         });
14685         this.inputEl().dom.value = JSON.stringify(data);
14686         
14687         
14688         
14689     }
14690     
14691     
14692 });
14693
14694
14695 Roo.bootstrap.form.CardUploader.ID = -1;/*
14696  * Based on:
14697  * Ext JS Library 1.1.1
14698  * Copyright(c) 2006-2007, Ext JS, LLC.
14699  *
14700  * Originally Released Under LGPL - original licence link has changed is not relivant.
14701  *
14702  * Fork - LGPL
14703  * <script type="text/javascript">
14704  */
14705
14706
14707 /**
14708  * @class Roo.data.SortTypes
14709  * @static
14710  * Defines the default sorting (casting?) comparison functions used when sorting data.
14711  */
14712 Roo.data.SortTypes = {
14713     /**
14714      * Default sort that does nothing
14715      * @param {Mixed} s The value being converted
14716      * @return {Mixed} The comparison value
14717      */
14718     none : function(s){
14719         return s;
14720     },
14721     
14722     /**
14723      * The regular expression used to strip tags
14724      * @type {RegExp}
14725      * @property
14726      */
14727     stripTagsRE : /<\/?[^>]+>/gi,
14728     
14729     /**
14730      * Strips all HTML tags to sort on text only
14731      * @param {Mixed} s The value being converted
14732      * @return {String} The comparison value
14733      */
14734     asText : function(s){
14735         return String(s).replace(this.stripTagsRE, "");
14736     },
14737     
14738     /**
14739      * Strips all HTML tags to sort on text only - Case insensitive
14740      * @param {Mixed} s The value being converted
14741      * @return {String} The comparison value
14742      */
14743     asUCText : function(s){
14744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14745     },
14746     
14747     /**
14748      * Case insensitive string
14749      * @param {Mixed} s The value being converted
14750      * @return {String} The comparison value
14751      */
14752     asUCString : function(s) {
14753         return String(s).toUpperCase();
14754     },
14755     
14756     /**
14757      * Date sorting
14758      * @param {Mixed} s The value being converted
14759      * @return {Number} The comparison value
14760      */
14761     asDate : function(s) {
14762         if(!s){
14763             return 0;
14764         }
14765         if(s instanceof Date){
14766             return s.getTime();
14767         }
14768         return Date.parse(String(s));
14769     },
14770     
14771     /**
14772      * Float sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Float} The comparison value
14775      */
14776     asFloat : function(s) {
14777         var val = parseFloat(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     },
14783     
14784     /**
14785      * Integer sorting
14786      * @param {Mixed} s The value being converted
14787      * @return {Number} The comparison value
14788      */
14789     asInt : function(s) {
14790         var val = parseInt(String(s).replace(/,/g, ""));
14791         if(isNaN(val)) {
14792             val = 0;
14793         }
14794         return val;
14795     }
14796 };/*
14797  * Based on:
14798  * Ext JS Library 1.1.1
14799  * Copyright(c) 2006-2007, Ext JS, LLC.
14800  *
14801  * Originally Released Under LGPL - original licence link has changed is not relivant.
14802  *
14803  * Fork - LGPL
14804  * <script type="text/javascript">
14805  */
14806
14807 /**
14808 * @class Roo.data.Record
14809  * Instances of this class encapsulate both record <em>definition</em> information, and record
14810  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14811  * to access Records cached in an {@link Roo.data.Store} object.<br>
14812  * <p>
14813  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14814  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14815  * objects.<br>
14816  * <p>
14817  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14818  * @constructor
14819  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14820  * {@link #create}. The parameters are the same.
14821  * @param {Array} data An associative Array of data values keyed by the field name.
14822  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14823  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14824  * not specified an integer id is generated.
14825  */
14826 Roo.data.Record = function(data, id){
14827     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14828     this.data = data;
14829 };
14830
14831 /**
14832  * Generate a constructor for a specific record layout.
14833  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14834  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14835  * Each field definition object may contain the following properties: <ul>
14836  * <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,
14837  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14838  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14839  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14840  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14841  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14842  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14843  * this may be omitted.</p></li>
14844  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14845  * <ul><li>auto (Default, implies no conversion)</li>
14846  * <li>string</li>
14847  * <li>int</li>
14848  * <li>float</li>
14849  * <li>boolean</li>
14850  * <li>date</li></ul></p></li>
14851  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14852  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14853  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14854  * by the Reader into an object that will be stored in the Record. It is passed the
14855  * following parameters:<ul>
14856  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14857  * </ul></p></li>
14858  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14859  * </ul>
14860  * <br>usage:<br><pre><code>
14861 var TopicRecord = Roo.data.Record.create(
14862     {name: 'title', mapping: 'topic_title'},
14863     {name: 'author', mapping: 'username'},
14864     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14865     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14866     {name: 'lastPoster', mapping: 'user2'},
14867     {name: 'excerpt', mapping: 'post_text'}
14868 );
14869
14870 var myNewRecord = new TopicRecord({
14871     title: 'Do my job please',
14872     author: 'noobie',
14873     totalPosts: 1,
14874     lastPost: new Date(),
14875     lastPoster: 'Animal',
14876     excerpt: 'No way dude!'
14877 });
14878 myStore.add(myNewRecord);
14879 </code></pre>
14880  * @method create
14881  * @static
14882  */
14883 Roo.data.Record.create = function(o){
14884     var f = function(){
14885         f.superclass.constructor.apply(this, arguments);
14886     };
14887     Roo.extend(f, Roo.data.Record);
14888     var p = f.prototype;
14889     p.fields = new Roo.util.MixedCollection(false, function(field){
14890         return field.name;
14891     });
14892     for(var i = 0, len = o.length; i < len; i++){
14893         p.fields.add(new Roo.data.Field(o[i]));
14894     }
14895     f.getField = function(name){
14896         return p.fields.get(name);  
14897     };
14898     return f;
14899 };
14900
14901 Roo.data.Record.AUTO_ID = 1000;
14902 Roo.data.Record.EDIT = 'edit';
14903 Roo.data.Record.REJECT = 'reject';
14904 Roo.data.Record.COMMIT = 'commit';
14905
14906 Roo.data.Record.prototype = {
14907     /**
14908      * Readonly flag - true if this record has been modified.
14909      * @type Boolean
14910      */
14911     dirty : false,
14912     editing : false,
14913     error: null,
14914     modified: null,
14915
14916     // private
14917     join : function(store){
14918         this.store = store;
14919     },
14920
14921     /**
14922      * Set the named field to the specified value.
14923      * @param {String} name The name of the field to set.
14924      * @param {Object} value The value to set the field to.
14925      */
14926     set : function(name, value){
14927         if(this.data[name] == value){
14928             return;
14929         }
14930         this.dirty = true;
14931         if(!this.modified){
14932             this.modified = {};
14933         }
14934         if(typeof this.modified[name] == 'undefined'){
14935             this.modified[name] = this.data[name];
14936         }
14937         this.data[name] = value;
14938         if(!this.editing && this.store){
14939             this.store.afterEdit(this);
14940         }       
14941     },
14942
14943     /**
14944      * Get the value of the named field.
14945      * @param {String} name The name of the field to get the value of.
14946      * @return {Object} The value of the field.
14947      */
14948     get : function(name){
14949         return this.data[name]; 
14950     },
14951
14952     // private
14953     beginEdit : function(){
14954         this.editing = true;
14955         this.modified = {}; 
14956     },
14957
14958     // private
14959     cancelEdit : function(){
14960         this.editing = false;
14961         delete this.modified;
14962     },
14963
14964     // private
14965     endEdit : function(){
14966         this.editing = false;
14967         if(this.dirty && this.store){
14968             this.store.afterEdit(this);
14969         }
14970     },
14971
14972     /**
14973      * Usually called by the {@link Roo.data.Store} which owns the Record.
14974      * Rejects all changes made to the Record since either creation, or the last commit operation.
14975      * Modified fields are reverted to their original values.
14976      * <p>
14977      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14978      * of reject operations.
14979      */
14980     reject : function(){
14981         var m = this.modified;
14982         for(var n in m){
14983             if(typeof m[n] != "function"){
14984                 this.data[n] = m[n];
14985             }
14986         }
14987         this.dirty = false;
14988         delete this.modified;
14989         this.editing = false;
14990         if(this.store){
14991             this.store.afterReject(this);
14992         }
14993     },
14994
14995     /**
14996      * Usually called by the {@link Roo.data.Store} which owns the Record.
14997      * Commits all changes made to the Record since either creation, or the last commit operation.
14998      * <p>
14999      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15000      * of commit operations.
15001      */
15002     commit : function(){
15003         this.dirty = false;
15004         delete this.modified;
15005         this.editing = false;
15006         if(this.store){
15007             this.store.afterCommit(this);
15008         }
15009     },
15010
15011     // private
15012     hasError : function(){
15013         return this.error != null;
15014     },
15015
15016     // private
15017     clearError : function(){
15018         this.error = null;
15019     },
15020
15021     /**
15022      * Creates a copy of this record.
15023      * @param {String} id (optional) A new record id if you don't want to use this record's id
15024      * @return {Record}
15025      */
15026     copy : function(newId) {
15027         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15028     }
15029 };/*
15030  * Based on:
15031  * Ext JS Library 1.1.1
15032  * Copyright(c) 2006-2007, Ext JS, LLC.
15033  *
15034  * Originally Released Under LGPL - original licence link has changed is not relivant.
15035  *
15036  * Fork - LGPL
15037  * <script type="text/javascript">
15038  */
15039
15040
15041
15042 /**
15043  * @class Roo.data.Store
15044  * @extends Roo.util.Observable
15045  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15046  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15047  * <p>
15048  * 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
15049  * has no knowledge of the format of the data returned by the Proxy.<br>
15050  * <p>
15051  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15052  * instances from the data object. These records are cached and made available through accessor functions.
15053  * @constructor
15054  * Creates a new Store.
15055  * @param {Object} config A config object containing the objects needed for the Store to access data,
15056  * and read the data into Records.
15057  */
15058 Roo.data.Store = function(config){
15059     this.data = new Roo.util.MixedCollection(false);
15060     this.data.getKey = function(o){
15061         return o.id;
15062     };
15063     this.baseParams = {};
15064     // private
15065     this.paramNames = {
15066         "start" : "start",
15067         "limit" : "limit",
15068         "sort" : "sort",
15069         "dir" : "dir",
15070         "multisort" : "_multisort"
15071     };
15072
15073     if(config && config.data){
15074         this.inlineData = config.data;
15075         delete config.data;
15076     }
15077
15078     Roo.apply(this, config);
15079     
15080     if(this.reader){ // reader passed
15081         this.reader = Roo.factory(this.reader, Roo.data);
15082         this.reader.xmodule = this.xmodule || false;
15083         if(!this.recordType){
15084             this.recordType = this.reader.recordType;
15085         }
15086         if(this.reader.onMetaChange){
15087             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15088         }
15089     }
15090
15091     if(this.recordType){
15092         this.fields = this.recordType.prototype.fields;
15093     }
15094     this.modified = [];
15095
15096     this.addEvents({
15097         /**
15098          * @event datachanged
15099          * Fires when the data cache has changed, and a widget which is using this Store
15100          * as a Record cache should refresh its view.
15101          * @param {Store} this
15102          */
15103         datachanged : true,
15104         /**
15105          * @event metachange
15106          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15107          * @param {Store} this
15108          * @param {Object} meta The JSON metadata
15109          */
15110         metachange : true,
15111         /**
15112          * @event add
15113          * Fires when Records have been added to the Store
15114          * @param {Store} this
15115          * @param {Roo.data.Record[]} records The array of Records added
15116          * @param {Number} index The index at which the record(s) were added
15117          */
15118         add : true,
15119         /**
15120          * @event remove
15121          * Fires when a Record has been removed from the Store
15122          * @param {Store} this
15123          * @param {Roo.data.Record} record The Record that was removed
15124          * @param {Number} index The index at which the record was removed
15125          */
15126         remove : true,
15127         /**
15128          * @event update
15129          * Fires when a Record has been updated
15130          * @param {Store} this
15131          * @param {Roo.data.Record} record The Record that was updated
15132          * @param {String} operation The update operation being performed.  Value may be one of:
15133          * <pre><code>
15134  Roo.data.Record.EDIT
15135  Roo.data.Record.REJECT
15136  Roo.data.Record.COMMIT
15137          * </code></pre>
15138          */
15139         update : true,
15140         /**
15141          * @event clear
15142          * Fires when the data cache has been cleared.
15143          * @param {Store} this
15144          */
15145         clear : true,
15146         /**
15147          * @event beforeload
15148          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15149          * the load action will be canceled.
15150          * @param {Store} this
15151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15152          */
15153         beforeload : true,
15154         /**
15155          * @event beforeloadadd
15156          * Fires after a new set of Records has been loaded.
15157          * @param {Store} this
15158          * @param {Roo.data.Record[]} records The Records that were loaded
15159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160          */
15161         beforeloadadd : true,
15162         /**
15163          * @event load
15164          * Fires after a new set of Records has been loaded, before they are added to the store.
15165          * @param {Store} this
15166          * @param {Roo.data.Record[]} records The Records that were loaded
15167          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168          * @params {Object} return from reader
15169          */
15170         load : true,
15171         /**
15172          * @event loadexception
15173          * Fires if an exception occurs in the Proxy during loading.
15174          * Called with the signature of the Proxy's "loadexception" event.
15175          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15176          * 
15177          * @param {Proxy} 
15178          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15179          * @param {Object} load options 
15180          * @param {Object} jsonData from your request (normally this contains the Exception)
15181          */
15182         loadexception : true
15183     });
15184     
15185     if(this.proxy){
15186         this.proxy = Roo.factory(this.proxy, Roo.data);
15187         this.proxy.xmodule = this.xmodule || false;
15188         this.relayEvents(this.proxy,  ["loadexception"]);
15189     }
15190     this.sortToggle = {};
15191     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15192
15193     Roo.data.Store.superclass.constructor.call(this);
15194
15195     if(this.inlineData){
15196         this.loadData(this.inlineData);
15197         delete this.inlineData;
15198     }
15199 };
15200
15201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15202      /**
15203     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15204     * without a remote query - used by combo/forms at present.
15205     */
15206     
15207     /**
15208     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15209     */
15210     /**
15211     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15212     */
15213     /**
15214     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15215     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15216     */
15217     /**
15218     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15219     * on any HTTP request
15220     */
15221     /**
15222     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15223     */
15224     /**
15225     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15226     */
15227     multiSort: false,
15228     /**
15229     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15230     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15231     */
15232     remoteSort : false,
15233
15234     /**
15235     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15236      * loaded or when a record is removed. (defaults to false).
15237     */
15238     pruneModifiedRecords : false,
15239
15240     // private
15241     lastOptions : null,
15242
15243     /**
15244      * Add Records to the Store and fires the add event.
15245      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15246      */
15247     add : function(records){
15248         records = [].concat(records);
15249         for(var i = 0, len = records.length; i < len; i++){
15250             records[i].join(this);
15251         }
15252         var index = this.data.length;
15253         this.data.addAll(records);
15254         this.fireEvent("add", this, records, index);
15255     },
15256
15257     /**
15258      * Remove a Record from the Store and fires the remove event.
15259      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15260      */
15261     remove : function(record){
15262         var index = this.data.indexOf(record);
15263         this.data.removeAt(index);
15264  
15265         if(this.pruneModifiedRecords){
15266             this.modified.remove(record);
15267         }
15268         this.fireEvent("remove", this, record, index);
15269     },
15270
15271     /**
15272      * Remove all Records from the Store and fires the clear event.
15273      */
15274     removeAll : function(){
15275         this.data.clear();
15276         if(this.pruneModifiedRecords){
15277             this.modified = [];
15278         }
15279         this.fireEvent("clear", this);
15280     },
15281
15282     /**
15283      * Inserts Records to the Store at the given index and fires the add event.
15284      * @param {Number} index The start index at which to insert the passed Records.
15285      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15286      */
15287     insert : function(index, records){
15288         records = [].concat(records);
15289         for(var i = 0, len = records.length; i < len; i++){
15290             this.data.insert(index, records[i]);
15291             records[i].join(this);
15292         }
15293         this.fireEvent("add", this, records, index);
15294     },
15295
15296     /**
15297      * Get the index within the cache of the passed Record.
15298      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15299      * @return {Number} The index of the passed Record. Returns -1 if not found.
15300      */
15301     indexOf : function(record){
15302         return this.data.indexOf(record);
15303     },
15304
15305     /**
15306      * Get the index within the cache of the Record with the passed id.
15307      * @param {String} id The id of the Record to find.
15308      * @return {Number} The index of the Record. Returns -1 if not found.
15309      */
15310     indexOfId : function(id){
15311         return this.data.indexOfKey(id);
15312     },
15313
15314     /**
15315      * Get the Record with the specified id.
15316      * @param {String} id The id of the Record to find.
15317      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15318      */
15319     getById : function(id){
15320         return this.data.key(id);
15321     },
15322
15323     /**
15324      * Get the Record at the specified index.
15325      * @param {Number} index The index of the Record to find.
15326      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15327      */
15328     getAt : function(index){
15329         return this.data.itemAt(index);
15330     },
15331
15332     /**
15333      * Returns a range of Records between specified indices.
15334      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15335      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15336      * @return {Roo.data.Record[]} An array of Records
15337      */
15338     getRange : function(start, end){
15339         return this.data.getRange(start, end);
15340     },
15341
15342     // private
15343     storeOptions : function(o){
15344         o = Roo.apply({}, o);
15345         delete o.callback;
15346         delete o.scope;
15347         this.lastOptions = o;
15348     },
15349
15350     /**
15351      * Loads the Record cache from the configured Proxy using the configured Reader.
15352      * <p>
15353      * If using remote paging, then the first load call must specify the <em>start</em>
15354      * and <em>limit</em> properties in the options.params property to establish the initial
15355      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15356      * <p>
15357      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15358      * and this call will return before the new data has been loaded. Perform any post-processing
15359      * in a callback function, or in a "load" event handler.</strong>
15360      * <p>
15361      * @param {Object} options An object containing properties which control loading options:<ul>
15362      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15363      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15364      * passed the following arguments:<ul>
15365      * <li>r : Roo.data.Record[]</li>
15366      * <li>options: Options object from the load call</li>
15367      * <li>success: Boolean success indicator</li></ul></li>
15368      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15369      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15370      * </ul>
15371      */
15372     load : function(options){
15373         options = options || {};
15374         if(this.fireEvent("beforeload", this, options) !== false){
15375             this.storeOptions(options);
15376             var p = Roo.apply(options.params || {}, this.baseParams);
15377             // if meta was not loaded from remote source.. try requesting it.
15378             if (!this.reader.metaFromRemote) {
15379                 p._requestMeta = 1;
15380             }
15381             if(this.sortInfo && this.remoteSort){
15382                 var pn = this.paramNames;
15383                 p[pn["sort"]] = this.sortInfo.field;
15384                 p[pn["dir"]] = this.sortInfo.direction;
15385             }
15386             if (this.multiSort) {
15387                 var pn = this.paramNames;
15388                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15389             }
15390             
15391             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15392         }
15393     },
15394
15395     /**
15396      * Reloads the Record cache from the configured Proxy using the configured Reader and
15397      * the options from the last load operation performed.
15398      * @param {Object} options (optional) An object containing properties which may override the options
15399      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15400      * the most recently used options are reused).
15401      */
15402     reload : function(options){
15403         this.load(Roo.applyIf(options||{}, this.lastOptions));
15404     },
15405
15406     // private
15407     // Called as a callback by the Reader during a load operation.
15408     loadRecords : function(o, options, success){
15409          
15410         if(!o){
15411             if(success !== false){
15412                 this.fireEvent("load", this, [], options, o);
15413             }
15414             if(options.callback){
15415                 options.callback.call(options.scope || this, [], options, false);
15416             }
15417             return;
15418         }
15419         // if data returned failure - throw an exception.
15420         if (o.success === false) {
15421             // show a message if no listener is registered.
15422             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15423                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15424             }
15425             // loadmask wil be hooked into this..
15426             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15427             return;
15428         }
15429         var r = o.records, t = o.totalRecords || r.length;
15430         
15431         this.fireEvent("beforeloadadd", this, r, options, o);
15432         
15433         if(!options || options.add !== true){
15434             if(this.pruneModifiedRecords){
15435                 this.modified = [];
15436             }
15437             for(var i = 0, len = r.length; i < len; i++){
15438                 r[i].join(this);
15439             }
15440             if(this.snapshot){
15441                 this.data = this.snapshot;
15442                 delete this.snapshot;
15443             }
15444             this.data.clear();
15445             this.data.addAll(r);
15446             this.totalLength = t;
15447             this.applySort();
15448             this.fireEvent("datachanged", this);
15449         }else{
15450             this.totalLength = Math.max(t, this.data.length+r.length);
15451             this.add(r);
15452         }
15453         
15454         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15455                 
15456             var e = new Roo.data.Record({});
15457
15458             e.set(this.parent.displayField, this.parent.emptyTitle);
15459             e.set(this.parent.valueField, '');
15460
15461             this.insert(0, e);
15462         }
15463             
15464         this.fireEvent("load", this, r, options, o);
15465         if(options.callback){
15466             options.callback.call(options.scope || this, r, options, true);
15467         }
15468     },
15469
15470
15471     /**
15472      * Loads data from a passed data block. A Reader which understands the format of the data
15473      * must have been configured in the constructor.
15474      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15475      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15476      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15477      */
15478     loadData : function(o, append){
15479         var r = this.reader.readRecords(o);
15480         this.loadRecords(r, {add: append}, true);
15481     },
15482     
15483      /**
15484      * using 'cn' the nested child reader read the child array into it's child stores.
15485      * @param {Object} rec The record with a 'children array
15486      */
15487     loadDataFromChildren : function(rec)
15488     {
15489         this.loadData(this.reader.toLoadData(rec));
15490     },
15491     
15492
15493     /**
15494      * Gets the number of cached records.
15495      * <p>
15496      * <em>If using paging, this may not be the total size of the dataset. If the data object
15497      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15498      * the data set size</em>
15499      */
15500     getCount : function(){
15501         return this.data.length || 0;
15502     },
15503
15504     /**
15505      * Gets the total number of records in the dataset as returned by the server.
15506      * <p>
15507      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15508      * the dataset size</em>
15509      */
15510     getTotalCount : function(){
15511         return this.totalLength || 0;
15512     },
15513
15514     /**
15515      * Returns the sort state of the Store as an object with two properties:
15516      * <pre><code>
15517  field {String} The name of the field by which the Records are sorted
15518  direction {String} The sort order, "ASC" or "DESC"
15519      * </code></pre>
15520      */
15521     getSortState : function(){
15522         return this.sortInfo;
15523     },
15524
15525     // private
15526     applySort : function(){
15527         if(this.sortInfo && !this.remoteSort){
15528             var s = this.sortInfo, f = s.field;
15529             var st = this.fields.get(f).sortType;
15530             var fn = function(r1, r2){
15531                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15532                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15533             };
15534             this.data.sort(s.direction, fn);
15535             if(this.snapshot && this.snapshot != this.data){
15536                 this.snapshot.sort(s.direction, fn);
15537             }
15538         }
15539     },
15540
15541     /**
15542      * Sets the default sort column and order to be used by the next load operation.
15543      * @param {String} fieldName The name of the field to sort by.
15544      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15545      */
15546     setDefaultSort : function(field, dir){
15547         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15548     },
15549
15550     /**
15551      * Sort the Records.
15552      * If remote sorting is used, the sort is performed on the server, and the cache is
15553      * reloaded. If local sorting is used, the cache is sorted internally.
15554      * @param {String} fieldName The name of the field to sort by.
15555      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15556      */
15557     sort : function(fieldName, dir){
15558         var f = this.fields.get(fieldName);
15559         if(!dir){
15560             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15561             
15562             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15563                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15564             }else{
15565                 dir = f.sortDir;
15566             }
15567         }
15568         this.sortToggle[f.name] = dir;
15569         this.sortInfo = {field: f.name, direction: dir};
15570         if(!this.remoteSort){
15571             this.applySort();
15572             this.fireEvent("datachanged", this);
15573         }else{
15574             this.load(this.lastOptions);
15575         }
15576     },
15577
15578     /**
15579      * Calls the specified function for each of the Records in the cache.
15580      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15581      * Returning <em>false</em> aborts and exits the iteration.
15582      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15583      */
15584     each : function(fn, scope){
15585         this.data.each(fn, scope);
15586     },
15587
15588     /**
15589      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15590      * (e.g., during paging).
15591      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15592      */
15593     getModifiedRecords : function(){
15594         return this.modified;
15595     },
15596
15597     // private
15598     createFilterFn : function(property, value, anyMatch){
15599         if(!value.exec){ // not a regex
15600             value = String(value);
15601             if(value.length == 0){
15602                 return false;
15603             }
15604             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15605         }
15606         return function(r){
15607             return value.test(r.data[property]);
15608         };
15609     },
15610
15611     /**
15612      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15613      * @param {String} property A field on your records
15614      * @param {Number} start The record index to start at (defaults to 0)
15615      * @param {Number} end The last record index to include (defaults to length - 1)
15616      * @return {Number} The sum
15617      */
15618     sum : function(property, start, end){
15619         var rs = this.data.items, v = 0;
15620         start = start || 0;
15621         end = (end || end === 0) ? end : rs.length-1;
15622
15623         for(var i = start; i <= end; i++){
15624             v += (rs[i].data[property] || 0);
15625         }
15626         return v;
15627     },
15628
15629     /**
15630      * Filter the records by a specified property.
15631      * @param {String} field A field on your records
15632      * @param {String/RegExp} value Either a string that the field
15633      * should start with or a RegExp to test against the field
15634      * @param {Boolean} anyMatch True to match any part not just the beginning
15635      */
15636     filter : function(property, value, anyMatch){
15637         var fn = this.createFilterFn(property, value, anyMatch);
15638         return fn ? this.filterBy(fn) : this.clearFilter();
15639     },
15640
15641     /**
15642      * Filter by a function. The specified function will be called with each
15643      * record in this data source. If the function returns true the record is included,
15644      * otherwise it is filtered.
15645      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15646      * @param {Object} scope (optional) The scope of the function (defaults to this)
15647      */
15648     filterBy : function(fn, scope){
15649         this.snapshot = this.snapshot || this.data;
15650         this.data = this.queryBy(fn, scope||this);
15651         this.fireEvent("datachanged", this);
15652     },
15653
15654     /**
15655      * Query the records by a specified property.
15656      * @param {String} field A field on your records
15657      * @param {String/RegExp} value Either a string that the field
15658      * should start with or a RegExp to test against the field
15659      * @param {Boolean} anyMatch True to match any part not just the beginning
15660      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15661      */
15662     query : function(property, value, anyMatch){
15663         var fn = this.createFilterFn(property, value, anyMatch);
15664         return fn ? this.queryBy(fn) : this.data.clone();
15665     },
15666
15667     /**
15668      * Query by a function. The specified function will be called with each
15669      * record in this data source. If the function returns true the record is included
15670      * in the results.
15671      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15672      * @param {Object} scope (optional) The scope of the function (defaults to this)
15673       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15674      **/
15675     queryBy : function(fn, scope){
15676         var data = this.snapshot || this.data;
15677         return data.filterBy(fn, scope||this);
15678     },
15679
15680     /**
15681      * Collects unique values for a particular dataIndex from this store.
15682      * @param {String} dataIndex The property to collect
15683      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15684      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15685      * @return {Array} An array of the unique values
15686      **/
15687     collect : function(dataIndex, allowNull, bypassFilter){
15688         var d = (bypassFilter === true && this.snapshot) ?
15689                 this.snapshot.items : this.data.items;
15690         var v, sv, r = [], l = {};
15691         for(var i = 0, len = d.length; i < len; i++){
15692             v = d[i].data[dataIndex];
15693             sv = String(v);
15694             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15695                 l[sv] = true;
15696                 r[r.length] = v;
15697             }
15698         }
15699         return r;
15700     },
15701
15702     /**
15703      * Revert to a view of the Record cache with no filtering applied.
15704      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15705      */
15706     clearFilter : function(suppressEvent){
15707         if(this.snapshot && this.snapshot != this.data){
15708             this.data = this.snapshot;
15709             delete this.snapshot;
15710             if(suppressEvent !== true){
15711                 this.fireEvent("datachanged", this);
15712             }
15713         }
15714     },
15715
15716     // private
15717     afterEdit : function(record){
15718         if(this.modified.indexOf(record) == -1){
15719             this.modified.push(record);
15720         }
15721         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15722     },
15723     
15724     // private
15725     afterReject : function(record){
15726         this.modified.remove(record);
15727         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15728     },
15729
15730     // private
15731     afterCommit : function(record){
15732         this.modified.remove(record);
15733         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15734     },
15735
15736     /**
15737      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15738      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15739      */
15740     commitChanges : function(){
15741         var m = this.modified.slice(0);
15742         this.modified = [];
15743         for(var i = 0, len = m.length; i < len; i++){
15744             m[i].commit();
15745         }
15746     },
15747
15748     /**
15749      * Cancel outstanding changes on all changed records.
15750      */
15751     rejectChanges : function(){
15752         var m = this.modified.slice(0);
15753         this.modified = [];
15754         for(var i = 0, len = m.length; i < len; i++){
15755             m[i].reject();
15756         }
15757     },
15758
15759     onMetaChange : function(meta, rtype, o){
15760         this.recordType = rtype;
15761         this.fields = rtype.prototype.fields;
15762         delete this.snapshot;
15763         this.sortInfo = meta.sortInfo || this.sortInfo;
15764         this.modified = [];
15765         this.fireEvent('metachange', this, this.reader.meta);
15766     },
15767     
15768     moveIndex : function(data, type)
15769     {
15770         var index = this.indexOf(data);
15771         
15772         var newIndex = index + type;
15773         
15774         this.remove(data);
15775         
15776         this.insert(newIndex, data);
15777         
15778     }
15779 });/*
15780  * Based on:
15781  * Ext JS Library 1.1.1
15782  * Copyright(c) 2006-2007, Ext JS, LLC.
15783  *
15784  * Originally Released Under LGPL - original licence link has changed is not relivant.
15785  *
15786  * Fork - LGPL
15787  * <script type="text/javascript">
15788  */
15789
15790 /**
15791  * @class Roo.data.SimpleStore
15792  * @extends Roo.data.Store
15793  * Small helper class to make creating Stores from Array data easier.
15794  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15795  * @cfg {Array} fields An array of field definition objects, or field name strings.
15796  * @cfg {Object} an existing reader (eg. copied from another store)
15797  * @cfg {Array} data The multi-dimensional array of data
15798  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15799  * @cfg {Roo.data.Reader} reader  [not-required] 
15800  * @constructor
15801  * @param {Object} config
15802  */
15803 Roo.data.SimpleStore = function(config)
15804 {
15805     Roo.data.SimpleStore.superclass.constructor.call(this, {
15806         isLocal : true,
15807         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15808                 id: config.id
15809             },
15810             Roo.data.Record.create(config.fields)
15811         ),
15812         proxy : new Roo.data.MemoryProxy(config.data)
15813     });
15814     this.load();
15815 };
15816 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827 /**
15828 /**
15829  * @extends Roo.data.Store
15830  * @class Roo.data.JsonStore
15831  * Small helper class to make creating Stores for JSON data easier. <br/>
15832 <pre><code>
15833 var store = new Roo.data.JsonStore({
15834     url: 'get-images.php',
15835     root: 'images',
15836     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15837 });
15838 </code></pre>
15839  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15840  * JsonReader and HttpProxy (unless inline data is provided).</b>
15841  * @cfg {Array} fields An array of field definition objects, or field name strings.
15842  * @constructor
15843  * @param {Object} config
15844  */
15845 Roo.data.JsonStore = function(c){
15846     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15847         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15848         reader: new Roo.data.JsonReader(c, c.fields)
15849     }));
15850 };
15851 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15852  * Based on:
15853  * Ext JS Library 1.1.1
15854  * Copyright(c) 2006-2007, Ext JS, LLC.
15855  *
15856  * Originally Released Under LGPL - original licence link has changed is not relivant.
15857  *
15858  * Fork - LGPL
15859  * <script type="text/javascript">
15860  */
15861
15862  
15863 Roo.data.Field = function(config){
15864     if(typeof config == "string"){
15865         config = {name: config};
15866     }
15867     Roo.apply(this, config);
15868     
15869     if(!this.type){
15870         this.type = "auto";
15871     }
15872     
15873     var st = Roo.data.SortTypes;
15874     // named sortTypes are supported, here we look them up
15875     if(typeof this.sortType == "string"){
15876         this.sortType = st[this.sortType];
15877     }
15878     
15879     // set default sortType for strings and dates
15880     if(!this.sortType){
15881         switch(this.type){
15882             case "string":
15883                 this.sortType = st.asUCString;
15884                 break;
15885             case "date":
15886                 this.sortType = st.asDate;
15887                 break;
15888             default:
15889                 this.sortType = st.none;
15890         }
15891     }
15892
15893     // define once
15894     var stripRe = /[\$,%]/g;
15895
15896     // prebuilt conversion function for this field, instead of
15897     // switching every time we're reading a value
15898     if(!this.convert){
15899         var cv, dateFormat = this.dateFormat;
15900         switch(this.type){
15901             case "":
15902             case "auto":
15903             case undefined:
15904                 cv = function(v){ return v; };
15905                 break;
15906             case "string":
15907                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15908                 break;
15909             case "int":
15910                 cv = function(v){
15911                     return v !== undefined && v !== null && v !== '' ?
15912                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15913                     };
15914                 break;
15915             case "float":
15916                 cv = function(v){
15917                     return v !== undefined && v !== null && v !== '' ?
15918                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15919                     };
15920                 break;
15921             case "bool":
15922             case "boolean":
15923                 cv = function(v){ return v === true || v === "true" || v == 1; };
15924                 break;
15925             case "date":
15926                 cv = function(v){
15927                     if(!v){
15928                         return '';
15929                     }
15930                     if(v instanceof Date){
15931                         return v;
15932                     }
15933                     if(dateFormat){
15934                         if(dateFormat == "timestamp"){
15935                             return new Date(v*1000);
15936                         }
15937                         return Date.parseDate(v, dateFormat);
15938                     }
15939                     var parsed = Date.parse(v);
15940                     return parsed ? new Date(parsed) : null;
15941                 };
15942              break;
15943             
15944         }
15945         this.convert = cv;
15946     }
15947 };
15948
15949 Roo.data.Field.prototype = {
15950     dateFormat: null,
15951     defaultValue: "",
15952     mapping: null,
15953     sortType : null,
15954     sortDir : "ASC"
15955 };/*
15956  * Based on:
15957  * Ext JS Library 1.1.1
15958  * Copyright(c) 2006-2007, Ext JS, LLC.
15959  *
15960  * Originally Released Under LGPL - original licence link has changed is not relivant.
15961  *
15962  * Fork - LGPL
15963  * <script type="text/javascript">
15964  */
15965  
15966 // Base class for reading structured data from a data source.  This class is intended to be
15967 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15968
15969 /**
15970  * @class Roo.data.DataReader
15971  * @abstract
15972  * Base class for reading structured data from a data source.  This class is intended to be
15973  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15974  */
15975
15976 Roo.data.DataReader = function(meta, recordType){
15977     
15978     this.meta = meta;
15979     
15980     this.recordType = recordType instanceof Array ? 
15981         Roo.data.Record.create(recordType) : recordType;
15982 };
15983
15984 Roo.data.DataReader.prototype = {
15985     
15986     
15987     readerType : 'Data',
15988      /**
15989      * Create an empty record
15990      * @param {Object} data (optional) - overlay some values
15991      * @return {Roo.data.Record} record created.
15992      */
15993     newRow :  function(d) {
15994         var da =  {};
15995         this.recordType.prototype.fields.each(function(c) {
15996             switch( c.type) {
15997                 case 'int' : da[c.name] = 0; break;
15998                 case 'date' : da[c.name] = new Date(); break;
15999                 case 'float' : da[c.name] = 0.0; break;
16000                 case 'boolean' : da[c.name] = false; break;
16001                 default : da[c.name] = ""; break;
16002             }
16003             
16004         });
16005         return new this.recordType(Roo.apply(da, d));
16006     }
16007     
16008     
16009 };/*
16010  * Based on:
16011  * Ext JS Library 1.1.1
16012  * Copyright(c) 2006-2007, Ext JS, LLC.
16013  *
16014  * Originally Released Under LGPL - original licence link has changed is not relivant.
16015  *
16016  * Fork - LGPL
16017  * <script type="text/javascript">
16018  */
16019
16020 /**
16021  * @class Roo.data.DataProxy
16022  * @extends Roo.util.Observable
16023  * @abstract
16024  * This class is an abstract base class for implementations which provide retrieval of
16025  * unformatted data objects.<br>
16026  * <p>
16027  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16028  * (of the appropriate type which knows how to parse the data object) to provide a block of
16029  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16030  * <p>
16031  * Custom implementations must implement the load method as described in
16032  * {@link Roo.data.HttpProxy#load}.
16033  */
16034 Roo.data.DataProxy = function(){
16035     this.addEvents({
16036         /**
16037          * @event beforeload
16038          * Fires before a network request is made to retrieve a data object.
16039          * @param {Object} This DataProxy object.
16040          * @param {Object} params The params parameter to the load function.
16041          */
16042         beforeload : true,
16043         /**
16044          * @event load
16045          * Fires before the load method's callback is called.
16046          * @param {Object} This DataProxy object.
16047          * @param {Object} o The data object.
16048          * @param {Object} arg The callback argument object passed to the load function.
16049          */
16050         load : true,
16051         /**
16052          * @event loadexception
16053          * Fires if an Exception occurs during data retrieval.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} o The data object.
16056          * @param {Object} arg The callback argument object passed to the load function.
16057          * @param {Object} e The Exception.
16058          */
16059         loadexception : true
16060     });
16061     Roo.data.DataProxy.superclass.constructor.call(this);
16062 };
16063
16064 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16065
16066     /**
16067      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16068      */
16069 /*
16070  * Based on:
16071  * Ext JS Library 1.1.1
16072  * Copyright(c) 2006-2007, Ext JS, LLC.
16073  *
16074  * Originally Released Under LGPL - original licence link has changed is not relivant.
16075  *
16076  * Fork - LGPL
16077  * <script type="text/javascript">
16078  */
16079 /**
16080  * @class Roo.data.MemoryProxy
16081  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16082  * to the Reader when its load method is called.
16083  * @constructor
16084  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16085  */
16086 Roo.data.MemoryProxy = function(data){
16087     if (data.data) {
16088         data = data.data;
16089     }
16090     Roo.data.MemoryProxy.superclass.constructor.call(this);
16091     this.data = data;
16092 };
16093
16094 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16095     
16096     /**
16097      * Load data from the requested source (in this case an in-memory
16098      * data object passed to the constructor), read the data object into
16099      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16100      * process that block using the passed callback.
16101      * @param {Object} params This parameter is not used by the MemoryProxy class.
16102      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16103      * object into a block of Roo.data.Records.
16104      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16105      * The function must be passed <ul>
16106      * <li>The Record block object</li>
16107      * <li>The "arg" argument from the load function</li>
16108      * <li>A boolean success indicator</li>
16109      * </ul>
16110      * @param {Object} scope The scope in which to call the callback
16111      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16112      */
16113     load : function(params, reader, callback, scope, arg){
16114         params = params || {};
16115         var result;
16116         try {
16117             result = reader.readRecords(params.data ? params.data :this.data);
16118         }catch(e){
16119             this.fireEvent("loadexception", this, arg, null, e);
16120             callback.call(scope, null, arg, false);
16121             return;
16122         }
16123         callback.call(scope, result, arg, true);
16124     },
16125     
16126     // private
16127     update : function(params, records){
16128         
16129     }
16130 });/*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140 /**
16141  * @class Roo.data.HttpProxy
16142  * @extends Roo.data.DataProxy
16143  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16144  * configured to reference a certain URL.<br><br>
16145  * <p>
16146  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16147  * from which the running page was served.<br><br>
16148  * <p>
16149  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16150  * <p>
16151  * Be aware that to enable the browser to parse an XML document, the server must set
16152  * the Content-Type header in the HTTP response to "text/xml".
16153  * @constructor
16154  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16155  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16156  * will be used to make the request.
16157  */
16158 Roo.data.HttpProxy = function(conn){
16159     Roo.data.HttpProxy.superclass.constructor.call(this);
16160     // is conn a conn config or a real conn?
16161     this.conn = conn;
16162     this.useAjax = !conn || !conn.events;
16163   
16164 };
16165
16166 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16167     // thse are take from connection...
16168     
16169     /**
16170      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16171      */
16172     /**
16173      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16174      * extra parameters to each request made by this object. (defaults to undefined)
16175      */
16176     /**
16177      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16178      *  to each request made by this object. (defaults to undefined)
16179      */
16180     /**
16181      * @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)
16182      */
16183     /**
16184      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16185      */
16186      /**
16187      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16188      * @type Boolean
16189      */
16190   
16191
16192     /**
16193      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16194      * @type Boolean
16195      */
16196     /**
16197      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16198      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16199      * a finer-grained basis than the DataProxy events.
16200      */
16201     getConnection : function(){
16202         return this.useAjax ? Roo.Ajax : this.conn;
16203     },
16204
16205     /**
16206      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16207      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16208      * process that block using the passed callback.
16209      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16210      * for the request to the remote server.
16211      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16212      * object into a block of Roo.data.Records.
16213      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16214      * The function must be passed <ul>
16215      * <li>The Record block object</li>
16216      * <li>The "arg" argument from the load function</li>
16217      * <li>A boolean success indicator</li>
16218      * </ul>
16219      * @param {Object} scope The scope in which to call the callback
16220      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16221      */
16222     load : function(params, reader, callback, scope, arg){
16223         if(this.fireEvent("beforeload", this, params) !== false){
16224             var  o = {
16225                 params : params || {},
16226                 request: {
16227                     callback : callback,
16228                     scope : scope,
16229                     arg : arg
16230                 },
16231                 reader: reader,
16232                 callback : this.loadResponse,
16233                 scope: this
16234             };
16235             if(this.useAjax){
16236                 Roo.applyIf(o, this.conn);
16237                 if(this.activeRequest){
16238                     Roo.Ajax.abort(this.activeRequest);
16239                 }
16240                 this.activeRequest = Roo.Ajax.request(o);
16241             }else{
16242                 this.conn.request(o);
16243             }
16244         }else{
16245             callback.call(scope||this, null, arg, false);
16246         }
16247     },
16248
16249     // private
16250     loadResponse : function(o, success, response){
16251         delete this.activeRequest;
16252         if(!success){
16253             this.fireEvent("loadexception", this, o, response);
16254             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16255             return;
16256         }
16257         var result;
16258         try {
16259             result = o.reader.read(response);
16260         }catch(e){
16261             o.success = false;
16262             o.raw = { errorMsg : response.responseText };
16263             this.fireEvent("loadexception", this, o, response, e);
16264             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16265             return;
16266         }
16267         
16268         this.fireEvent("load", this, o, o.request.arg);
16269         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16270     },
16271
16272     // private
16273     update : function(dataSet){
16274
16275     },
16276
16277     // private
16278     updateResponse : function(dataSet){
16279
16280     }
16281 });/*
16282  * Based on:
16283  * Ext JS Library 1.1.1
16284  * Copyright(c) 2006-2007, Ext JS, LLC.
16285  *
16286  * Originally Released Under LGPL - original licence link has changed is not relivant.
16287  *
16288  * Fork - LGPL
16289  * <script type="text/javascript">
16290  */
16291
16292 /**
16293  * @class Roo.data.ScriptTagProxy
16294  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16295  * other than the originating domain of the running page.<br><br>
16296  * <p>
16297  * <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
16298  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16299  * <p>
16300  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16301  * source code that is used as the source inside a &lt;script> tag.<br><br>
16302  * <p>
16303  * In order for the browser to process the returned data, the server must wrap the data object
16304  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16305  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16306  * depending on whether the callback name was passed:
16307  * <p>
16308  * <pre><code>
16309 boolean scriptTag = false;
16310 String cb = request.getParameter("callback");
16311 if (cb != null) {
16312     scriptTag = true;
16313     response.setContentType("text/javascript");
16314 } else {
16315     response.setContentType("application/x-json");
16316 }
16317 Writer out = response.getWriter();
16318 if (scriptTag) {
16319     out.write(cb + "(");
16320 }
16321 out.print(dataBlock.toJsonString());
16322 if (scriptTag) {
16323     out.write(");");
16324 }
16325 </pre></code>
16326  *
16327  * @constructor
16328  * @param {Object} config A configuration object.
16329  */
16330 Roo.data.ScriptTagProxy = function(config){
16331     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16332     Roo.apply(this, config);
16333     this.head = document.getElementsByTagName("head")[0];
16334 };
16335
16336 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16337
16338 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16339     /**
16340      * @cfg {String} url The URL from which to request the data object.
16341      */
16342     /**
16343      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16344      */
16345     timeout : 30000,
16346     /**
16347      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16348      * the server the name of the callback function set up by the load call to process the returned data object.
16349      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16350      * javascript output which calls this named function passing the data object as its only parameter.
16351      */
16352     callbackParam : "callback",
16353     /**
16354      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16355      * name to the request.
16356      */
16357     nocache : true,
16358
16359     /**
16360      * Load data from the configured URL, read the data object into
16361      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16362      * process that block using the passed callback.
16363      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16364      * for the request to the remote server.
16365      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16366      * object into a block of Roo.data.Records.
16367      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16368      * The function must be passed <ul>
16369      * <li>The Record block object</li>
16370      * <li>The "arg" argument from the load function</li>
16371      * <li>A boolean success indicator</li>
16372      * </ul>
16373      * @param {Object} scope The scope in which to call the callback
16374      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16375      */
16376     load : function(params, reader, callback, scope, arg){
16377         if(this.fireEvent("beforeload", this, params) !== false){
16378
16379             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16380
16381             var url = this.url;
16382             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16383             if(this.nocache){
16384                 url += "&_dc=" + (new Date().getTime());
16385             }
16386             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16387             var trans = {
16388                 id : transId,
16389                 cb : "stcCallback"+transId,
16390                 scriptId : "stcScript"+transId,
16391                 params : params,
16392                 arg : arg,
16393                 url : url,
16394                 callback : callback,
16395                 scope : scope,
16396                 reader : reader
16397             };
16398             var conn = this;
16399
16400             window[trans.cb] = function(o){
16401                 conn.handleResponse(o, trans);
16402             };
16403
16404             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16405
16406             if(this.autoAbort !== false){
16407                 this.abort();
16408             }
16409
16410             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16411
16412             var script = document.createElement("script");
16413             script.setAttribute("src", url);
16414             script.setAttribute("type", "text/javascript");
16415             script.setAttribute("id", trans.scriptId);
16416             this.head.appendChild(script);
16417
16418             this.trans = trans;
16419         }else{
16420             callback.call(scope||this, null, arg, false);
16421         }
16422     },
16423
16424     // private
16425     isLoading : function(){
16426         return this.trans ? true : false;
16427     },
16428
16429     /**
16430      * Abort the current server request.
16431      */
16432     abort : function(){
16433         if(this.isLoading()){
16434             this.destroyTrans(this.trans);
16435         }
16436     },
16437
16438     // private
16439     destroyTrans : function(trans, isLoaded){
16440         this.head.removeChild(document.getElementById(trans.scriptId));
16441         clearTimeout(trans.timeoutId);
16442         if(isLoaded){
16443             window[trans.cb] = undefined;
16444             try{
16445                 delete window[trans.cb];
16446             }catch(e){}
16447         }else{
16448             // if hasn't been loaded, wait for load to remove it to prevent script error
16449             window[trans.cb] = function(){
16450                 window[trans.cb] = undefined;
16451                 try{
16452                     delete window[trans.cb];
16453                 }catch(e){}
16454             };
16455         }
16456     },
16457
16458     // private
16459     handleResponse : function(o, trans){
16460         this.trans = false;
16461         this.destroyTrans(trans, true);
16462         var result;
16463         try {
16464             result = trans.reader.readRecords(o);
16465         }catch(e){
16466             this.fireEvent("loadexception", this, o, trans.arg, e);
16467             trans.callback.call(trans.scope||window, null, trans.arg, false);
16468             return;
16469         }
16470         this.fireEvent("load", this, o, trans.arg);
16471         trans.callback.call(trans.scope||window, result, trans.arg, true);
16472     },
16473
16474     // private
16475     handleFailure : function(trans){
16476         this.trans = false;
16477         this.destroyTrans(trans, false);
16478         this.fireEvent("loadexception", this, null, trans.arg);
16479         trans.callback.call(trans.scope||window, null, trans.arg, false);
16480     }
16481 });/*
16482  * Based on:
16483  * Ext JS Library 1.1.1
16484  * Copyright(c) 2006-2007, Ext JS, LLC.
16485  *
16486  * Originally Released Under LGPL - original licence link has changed is not relivant.
16487  *
16488  * Fork - LGPL
16489  * <script type="text/javascript">
16490  */
16491
16492 /**
16493  * @class Roo.data.JsonReader
16494  * @extends Roo.data.DataReader
16495  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16496  * based on mappings in a provided Roo.data.Record constructor.
16497  * 
16498  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16499  * in the reply previously. 
16500  * 
16501  * <p>
16502  * Example code:
16503  * <pre><code>
16504 var RecordDef = Roo.data.Record.create([
16505     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16506     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16507 ]);
16508 var myReader = new Roo.data.JsonReader({
16509     totalProperty: "results",    // The property which contains the total dataset size (optional)
16510     root: "rows",                // The property which contains an Array of row objects
16511     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16512 }, RecordDef);
16513 </code></pre>
16514  * <p>
16515  * This would consume a JSON file like this:
16516  * <pre><code>
16517 { 'results': 2, 'rows': [
16518     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16519     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16520 }
16521 </code></pre>
16522  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16523  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16524  * paged from the remote server.
16525  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16526  * @cfg {String} root name of the property which contains the Array of row objects.
16527  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16528  * @cfg {Array} fields Array of field definition objects
16529  * @constructor
16530  * Create a new JsonReader
16531  * @param {Object} meta Metadata configuration options
16532  * @param {Object} recordType Either an Array of field definition objects,
16533  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16534  */
16535 Roo.data.JsonReader = function(meta, recordType){
16536     
16537     meta = meta || {};
16538     // set some defaults:
16539     Roo.applyIf(meta, {
16540         totalProperty: 'total',
16541         successProperty : 'success',
16542         root : 'data',
16543         id : 'id'
16544     });
16545     
16546     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16547 };
16548 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16549     
16550     readerType : 'Json',
16551     
16552     /**
16553      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16554      * Used by Store query builder to append _requestMeta to params.
16555      * 
16556      */
16557     metaFromRemote : false,
16558     /**
16559      * This method is only used by a DataProxy which has retrieved data from a remote server.
16560      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16561      * @return {Object} data A data block which is used by an Roo.data.Store object as
16562      * a cache of Roo.data.Records.
16563      */
16564     read : function(response){
16565         var json = response.responseText;
16566        
16567         var o = /* eval:var:o */ eval("("+json+")");
16568         if(!o) {
16569             throw {message: "JsonReader.read: Json object not found"};
16570         }
16571         
16572         if(o.metaData){
16573             
16574             delete this.ef;
16575             this.metaFromRemote = true;
16576             this.meta = o.metaData;
16577             this.recordType = Roo.data.Record.create(o.metaData.fields);
16578             this.onMetaChange(this.meta, this.recordType, o);
16579         }
16580         return this.readRecords(o);
16581     },
16582
16583     // private function a store will implement
16584     onMetaChange : function(meta, recordType, o){
16585
16586     },
16587
16588     /**
16589          * @ignore
16590          */
16591     simpleAccess: function(obj, subsc) {
16592         return obj[subsc];
16593     },
16594
16595         /**
16596          * @ignore
16597          */
16598     getJsonAccessor: function(){
16599         var re = /[\[\.]/;
16600         return function(expr) {
16601             try {
16602                 return(re.test(expr))
16603                     ? new Function("obj", "return obj." + expr)
16604                     : function(obj){
16605                         return obj[expr];
16606                     };
16607             } catch(e){}
16608             return Roo.emptyFn;
16609         };
16610     }(),
16611
16612     /**
16613      * Create a data block containing Roo.data.Records from an XML document.
16614      * @param {Object} o An object which contains an Array of row objects in the property specified
16615      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16616      * which contains the total size of the dataset.
16617      * @return {Object} data A data block which is used by an Roo.data.Store object as
16618      * a cache of Roo.data.Records.
16619      */
16620     readRecords : function(o){
16621         /**
16622          * After any data loads, the raw JSON data is available for further custom processing.
16623          * @type Object
16624          */
16625         this.o = o;
16626         var s = this.meta, Record = this.recordType,
16627             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16628
16629 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16630         if (!this.ef) {
16631             if(s.totalProperty) {
16632                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16633                 }
16634                 if(s.successProperty) {
16635                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16636                 }
16637                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16638                 if (s.id) {
16639                         var g = this.getJsonAccessor(s.id);
16640                         this.getId = function(rec) {
16641                                 var r = g(rec);  
16642                                 return (r === undefined || r === "") ? null : r;
16643                         };
16644                 } else {
16645                         this.getId = function(){return null;};
16646                 }
16647             this.ef = [];
16648             for(var jj = 0; jj < fl; jj++){
16649                 f = fi[jj];
16650                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16651                 this.ef[jj] = this.getJsonAccessor(map);
16652             }
16653         }
16654
16655         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16656         if(s.totalProperty){
16657             var vt = parseInt(this.getTotal(o), 10);
16658             if(!isNaN(vt)){
16659                 totalRecords = vt;
16660             }
16661         }
16662         if(s.successProperty){
16663             var vs = this.getSuccess(o);
16664             if(vs === false || vs === 'false'){
16665                 success = false;
16666             }
16667         }
16668         var records = [];
16669         for(var i = 0; i < c; i++){
16670             var n = root[i];
16671             var values = {};
16672             var id = this.getId(n);
16673             for(var j = 0; j < fl; j++){
16674                 f = fi[j];
16675                                 var v = this.ef[j](n);
16676                                 if (!f.convert) {
16677                                         Roo.log('missing convert for ' + f.name);
16678                                         Roo.log(f);
16679                                         continue;
16680                                 }
16681                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16682             }
16683                         if (!Record) {
16684                                 return {
16685                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16686                                         success : false,
16687                                         records : [],
16688                                         totalRecords : 0
16689                                 };
16690                         }
16691             var record = new Record(values, id);
16692             record.json = n;
16693             records[i] = record;
16694         }
16695         return {
16696             raw : o,
16697             success : success,
16698             records : records,
16699             totalRecords : totalRecords
16700         };
16701     },
16702     // used when loading children.. @see loadDataFromChildren
16703     toLoadData: function(rec)
16704     {
16705         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16706         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16707         return { data : data, total : data.length };
16708         
16709     }
16710 });/*
16711  * Based on:
16712  * Ext JS Library 1.1.1
16713  * Copyright(c) 2006-2007, Ext JS, LLC.
16714  *
16715  * Originally Released Under LGPL - original licence link has changed is not relivant.
16716  *
16717  * Fork - LGPL
16718  * <script type="text/javascript">
16719  */
16720
16721 /**
16722  * @class Roo.data.ArrayReader
16723  * @extends Roo.data.DataReader
16724  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16725  * Each element of that Array represents a row of data fields. The
16726  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16727  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16728  * <p>
16729  * Example code:.
16730  * <pre><code>
16731 var RecordDef = Roo.data.Record.create([
16732     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16733     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16734 ]);
16735 var myReader = new Roo.data.ArrayReader({
16736     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16737 }, RecordDef);
16738 </code></pre>
16739  * <p>
16740  * This would consume an Array like this:
16741  * <pre><code>
16742 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16743   </code></pre>
16744  
16745  * @constructor
16746  * Create a new JsonReader
16747  * @param {Object} meta Metadata configuration options.
16748  * @param {Object|Array} recordType Either an Array of field definition objects
16749  * 
16750  * @cfg {Array} fields Array of field definition objects
16751  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16752  * as specified to {@link Roo.data.Record#create},
16753  * or an {@link Roo.data.Record} object
16754  *
16755  * 
16756  * created using {@link Roo.data.Record#create}.
16757  */
16758 Roo.data.ArrayReader = function(meta, recordType)
16759 {    
16760     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16761 };
16762
16763 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16764     
16765       /**
16766      * Create a data block containing Roo.data.Records from an XML document.
16767      * @param {Object} o An Array of row objects which represents the dataset.
16768      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16769      * a cache of Roo.data.Records.
16770      */
16771     readRecords : function(o)
16772     {
16773         var sid = this.meta ? this.meta.id : null;
16774         var recordType = this.recordType, fields = recordType.prototype.fields;
16775         var records = [];
16776         var root = o;
16777         for(var i = 0; i < root.length; i++){
16778             var n = root[i];
16779             var values = {};
16780             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16781             for(var j = 0, jlen = fields.length; j < jlen; j++){
16782                 var f = fields.items[j];
16783                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16784                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16785                 v = f.convert(v);
16786                 values[f.name] = v;
16787             }
16788             var record = new recordType(values, id);
16789             record.json = n;
16790             records[records.length] = record;
16791         }
16792         return {
16793             records : records,
16794             totalRecords : records.length
16795         };
16796     },
16797     // used when loading children.. @see loadDataFromChildren
16798     toLoadData: function(rec)
16799     {
16800         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16801         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16802         
16803     }
16804     
16805     
16806 });/*
16807  * - LGPL
16808  * * 
16809  */
16810
16811 /**
16812  * @class Roo.bootstrap.form.ComboBox
16813  * @extends Roo.bootstrap.form.TriggerField
16814  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16815  * @cfg {Boolean} append (true|false) default false
16816  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16817  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16818  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16819  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16820  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16821  * @cfg {Boolean} animate default true
16822  * @cfg {Boolean} emptyResultText only for touch device
16823  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16824  * @cfg {String} emptyTitle default ''
16825  * @cfg {Number} width fixed with? experimental
16826  * @constructor
16827  * Create a new ComboBox.
16828  * @param {Object} config Configuration options
16829  */
16830 Roo.bootstrap.form.ComboBox = function(config){
16831     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16832     this.addEvents({
16833         /**
16834          * @event expand
16835          * Fires when the dropdown list is expanded
16836         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16837         */
16838         'expand' : true,
16839         /**
16840          * @event collapse
16841          * Fires when the dropdown list is collapsed
16842         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16843         */
16844         'collapse' : true,
16845         /**
16846          * @event beforeselect
16847          * Fires before a list item is selected. Return false to cancel the selection.
16848         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16849         * @param {Roo.data.Record} record The data record returned from the underlying store
16850         * @param {Number} index The index of the selected item in the dropdown list
16851         */
16852         'beforeselect' : true,
16853         /**
16854          * @event select
16855          * Fires when a list item is selected
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16858         * @param {Number} index The index of the selected item in the dropdown list
16859         */
16860         'select' : true,
16861         /**
16862          * @event beforequery
16863          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16864          * The event object passed has these properties:
16865         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866         * @param {String} query The query
16867         * @param {Boolean} forceAll true to force "all" query
16868         * @param {Boolean} cancel true to cancel the query
16869         * @param {Object} e The query event object
16870         */
16871         'beforequery': true,
16872          /**
16873          * @event add
16874          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16875         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876         */
16877         'add' : true,
16878         /**
16879          * @event edit
16880          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16881         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16882         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16883         */
16884         'edit' : true,
16885         /**
16886          * @event remove
16887          * Fires when the remove value from the combobox array
16888         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16889         */
16890         'remove' : true,
16891         /**
16892          * @event afterremove
16893          * Fires when the remove value from the combobox array
16894         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895         */
16896         'afterremove' : true,
16897         /**
16898          * @event specialfilter
16899          * Fires when specialfilter
16900             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901             */
16902         'specialfilter' : true,
16903         /**
16904          * @event tick
16905          * Fires when tick the element
16906             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16907             */
16908         'tick' : true,
16909         /**
16910          * @event touchviewdisplay
16911          * Fires when touch view require special display (default is using displayField)
16912             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913             * @param {Object} cfg set html .
16914             */
16915         'touchviewdisplay' : true
16916         
16917     });
16918     
16919     this.item = [];
16920     this.tickItems = [];
16921     
16922     this.selectedIndex = -1;
16923     if(this.mode == 'local'){
16924         if(config.queryDelay === undefined){
16925             this.queryDelay = 10;
16926         }
16927         if(config.minChars === undefined){
16928             this.minChars = 0;
16929         }
16930     }
16931 };
16932
16933 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16934      
16935     /**
16936      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16937      * rendering into an Roo.Editor, defaults to false)
16938      */
16939     /**
16940      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16941      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16942      */
16943     /**
16944      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16945      */
16946     /**
16947      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16948      * the dropdown list (defaults to undefined, with no header element)
16949      */
16950
16951      /**
16952      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16953      */
16954      
16955      /**
16956      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16957      */
16958     listWidth: undefined,
16959     /**
16960      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16961      * mode = 'remote' or 'text' if mode = 'local')
16962      */
16963     displayField: undefined,
16964     
16965     /**
16966      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16967      * mode = 'remote' or 'value' if mode = 'local'). 
16968      * Note: use of a valueField requires the user make a selection
16969      * in order for a value to be mapped.
16970      */
16971     valueField: undefined,
16972     /**
16973      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16974      */
16975     modalTitle : '',
16976     
16977     /**
16978      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16979      * field's data value (defaults to the underlying DOM element's name)
16980      */
16981     hiddenName: undefined,
16982     /**
16983      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16984      */
16985     listClass: '',
16986     /**
16987      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16988      */
16989     selectedClass: 'active',
16990     
16991     /**
16992      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16993      */
16994     shadow:'sides',
16995     /**
16996      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16997      * anchor positions (defaults to 'tl-bl')
16998      */
16999     listAlign: 'tl-bl?',
17000     /**
17001      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17002      */
17003     maxHeight: 300,
17004     /**
17005      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17006      * query specified by the allQuery config option (defaults to 'query')
17007      */
17008     triggerAction: 'query',
17009     /**
17010      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17011      * (defaults to 4, does not apply if editable = false)
17012      */
17013     minChars : 4,
17014     /**
17015      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17016      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17017      */
17018     typeAhead: false,
17019     /**
17020      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17021      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17022      */
17023     queryDelay: 500,
17024     /**
17025      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17026      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17027      */
17028     pageSize: 0,
17029     /**
17030      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17031      * when editable = true (defaults to false)
17032      */
17033     selectOnFocus:false,
17034     /**
17035      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17036      */
17037     queryParam: 'query',
17038     /**
17039      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17040      * when mode = 'remote' (defaults to 'Loading...')
17041      */
17042     loadingText: 'Loading...',
17043     /**
17044      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17045      */
17046     resizable: false,
17047     /**
17048      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17049      */
17050     handleHeight : 8,
17051     /**
17052      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17053      * traditional select (defaults to true)
17054      */
17055     editable: true,
17056     /**
17057      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17058      */
17059     allQuery: '',
17060     /**
17061      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17062      */
17063     mode: 'remote',
17064     /**
17065      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17066      * listWidth has a higher value)
17067      */
17068     minListWidth : 70,
17069     /**
17070      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17071      * allow the user to set arbitrary text into the field (defaults to false)
17072      */
17073     forceSelection:false,
17074     /**
17075      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17076      * if typeAhead = true (defaults to 250)
17077      */
17078     typeAheadDelay : 250,
17079     /**
17080      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17081      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17082      */
17083     valueNotFoundText : undefined,
17084     /**
17085      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17086      */
17087     blockFocus : false,
17088     
17089     /**
17090      * @cfg {Boolean} disableClear Disable showing of clear button.
17091      */
17092     disableClear : false,
17093     /**
17094      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17095      */
17096     alwaysQuery : false,
17097     
17098     /**
17099      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17100      */
17101     multiple : false,
17102     
17103     /**
17104      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17105      */
17106     invalidClass : "has-warning",
17107     
17108     /**
17109      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17110      */
17111     validClass : "has-success",
17112     
17113     /**
17114      * @cfg {Boolean} specialFilter (true|false) special filter default false
17115      */
17116     specialFilter : false,
17117     
17118     /**
17119      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17120      */
17121     mobileTouchView : true,
17122     
17123     /**
17124      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17125      */
17126     useNativeIOS : false,
17127     
17128     /**
17129      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17130      */
17131     mobile_restrict_height : false,
17132     
17133     ios_options : false,
17134     
17135     //private
17136     addicon : false,
17137     editicon: false,
17138     
17139     page: 0,
17140     hasQuery: false,
17141     append: false,
17142     loadNext: false,
17143     autoFocus : true,
17144     tickable : false,
17145     btnPosition : 'right',
17146     triggerList : true,
17147     showToggleBtn : true,
17148     animate : true,
17149     emptyResultText: 'Empty',
17150     triggerText : 'Select',
17151     emptyTitle : '',
17152     width : false,
17153     
17154     // element that contains real text value.. (when hidden is used..)
17155     
17156     getAutoCreate : function()
17157     {   
17158         var cfg = false;
17159         //render
17160         /*
17161          * Render classic select for iso
17162          */
17163         
17164         if(Roo.isIOS && this.useNativeIOS){
17165             cfg = this.getAutoCreateNativeIOS();
17166             return cfg;
17167         }
17168         
17169         /*
17170          * Touch Devices
17171          */
17172         
17173         if(Roo.isTouch && this.mobileTouchView){
17174             cfg = this.getAutoCreateTouchView();
17175             return cfg;;
17176         }
17177         
17178         /*
17179          *  Normal ComboBox
17180          */
17181         if(!this.tickable){
17182             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17183             return cfg;
17184         }
17185         
17186         /*
17187          *  ComboBox with tickable selections
17188          */
17189              
17190         var align = this.labelAlign || this.parentLabelAlign();
17191         
17192         cfg = {
17193             cls : 'form-group roo-combobox-tickable' //input-group
17194         };
17195         
17196         var btn_text_select = '';
17197         var btn_text_done = '';
17198         var btn_text_cancel = '';
17199         
17200         if (this.btn_text_show) {
17201             btn_text_select = 'Select';
17202             btn_text_done = 'Done';
17203             btn_text_cancel = 'Cancel'; 
17204         }
17205         
17206         var buttons = {
17207             tag : 'div',
17208             cls : 'tickable-buttons',
17209             cn : [
17210                 {
17211                     tag : 'button',
17212                     type : 'button',
17213                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17214                     //html : this.triggerText
17215                     html: btn_text_select
17216                 },
17217                 {
17218                     tag : 'button',
17219                     type : 'button',
17220                     name : 'ok',
17221                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17222                     //html : 'Done'
17223                     html: btn_text_done
17224                 },
17225                 {
17226                     tag : 'button',
17227                     type : 'button',
17228                     name : 'cancel',
17229                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17230                     //html : 'Cancel'
17231                     html: btn_text_cancel
17232                 }
17233             ]
17234         };
17235         
17236         if(this.editable){
17237             buttons.cn.unshift({
17238                 tag: 'input',
17239                 cls: 'roo-select2-search-field-input'
17240             });
17241         }
17242         
17243         var _this = this;
17244         
17245         Roo.each(buttons.cn, function(c){
17246             if (_this.size) {
17247                 c.cls += ' btn-' + _this.size;
17248             }
17249
17250             if (_this.disabled) {
17251                 c.disabled = true;
17252             }
17253         });
17254         
17255         var box = {
17256             tag: 'div',
17257             style : 'display: contents',
17258             cn: [
17259                 {
17260                     tag: 'input',
17261                     type : 'hidden',
17262                     cls: 'form-hidden-field'
17263                 },
17264                 {
17265                     tag: 'ul',
17266                     cls: 'roo-select2-choices',
17267                     cn:[
17268                         {
17269                             tag: 'li',
17270                             cls: 'roo-select2-search-field',
17271                             cn: [
17272                                 buttons
17273                             ]
17274                         }
17275                     ]
17276                 }
17277             ]
17278         };
17279         
17280         var combobox = {
17281             cls: 'roo-select2-container input-group roo-select2-container-multi',
17282             cn: [
17283                 
17284                 box
17285 //                {
17286 //                    tag: 'ul',
17287 //                    cls: 'typeahead typeahead-long dropdown-menu',
17288 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17289 //                }
17290             ]
17291         };
17292         
17293         if(this.hasFeedback && !this.allowBlank){
17294             
17295             var feedback = {
17296                 tag: 'span',
17297                 cls: 'glyphicon form-control-feedback'
17298             };
17299
17300             combobox.cn.push(feedback);
17301         }
17302         
17303         
17304         
17305         var indicator = {
17306             tag : 'i',
17307             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17308             tooltip : 'This field is required'
17309         };
17310         if (Roo.bootstrap.version == 4) {
17311             indicator = {
17312                 tag : 'i',
17313                 style : 'display:none'
17314             };
17315         }
17316         if (align ==='left' && this.fieldLabel.length) {
17317             
17318             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17319             
17320             cfg.cn = [
17321                 indicator,
17322                 {
17323                     tag: 'label',
17324                     'for' :  id,
17325                     cls : 'control-label col-form-label',
17326                     html : this.fieldLabel
17327
17328                 },
17329                 {
17330                     cls : "", 
17331                     cn: [
17332                         combobox
17333                     ]
17334                 }
17335
17336             ];
17337             
17338             var labelCfg = cfg.cn[1];
17339             var contentCfg = cfg.cn[2];
17340             
17341
17342             if(this.indicatorpos == 'right'){
17343                 
17344                 cfg.cn = [
17345                     {
17346                         tag: 'label',
17347                         'for' :  id,
17348                         cls : 'control-label col-form-label',
17349                         cn : [
17350                             {
17351                                 tag : 'span',
17352                                 html : this.fieldLabel
17353                             },
17354                             indicator
17355                         ]
17356                     },
17357                     {
17358                         cls : "",
17359                         cn: [
17360                             combobox
17361                         ]
17362                     }
17363
17364                 ];
17365                 
17366                 
17367                 
17368                 labelCfg = cfg.cn[0];
17369                 contentCfg = cfg.cn[1];
17370             
17371             }
17372             
17373             if(this.labelWidth > 12){
17374                 labelCfg.style = "width: " + this.labelWidth + 'px';
17375             }
17376             if(this.width * 1 > 0){
17377                 contentCfg.style = "width: " + this.width + 'px';
17378             }
17379             if(this.labelWidth < 13 && this.labelmd == 0){
17380                 this.labelmd = this.labelWidth;
17381             }
17382             
17383             if(this.labellg > 0){
17384                 labelCfg.cls += ' col-lg-' + this.labellg;
17385                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17386             }
17387             
17388             if(this.labelmd > 0){
17389                 labelCfg.cls += ' col-md-' + this.labelmd;
17390                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17391             }
17392             
17393             if(this.labelsm > 0){
17394                 labelCfg.cls += ' col-sm-' + this.labelsm;
17395                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17396             }
17397             
17398             if(this.labelxs > 0){
17399                 labelCfg.cls += ' col-xs-' + this.labelxs;
17400                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17401             }
17402                 
17403                 
17404         } else if ( this.fieldLabel.length) {
17405 //                Roo.log(" label");
17406                  cfg.cn = [
17407                    indicator,
17408                     {
17409                         tag: 'label',
17410                         //cls : 'input-group-addon',
17411                         html : this.fieldLabel
17412                     },
17413                     combobox
17414                 ];
17415                 
17416                 if(this.indicatorpos == 'right'){
17417                     cfg.cn = [
17418                         {
17419                             tag: 'label',
17420                             //cls : 'input-group-addon',
17421                             html : this.fieldLabel
17422                         },
17423                         indicator,
17424                         combobox
17425                     ];
17426                     
17427                 }
17428
17429         } else {
17430             
17431 //                Roo.log(" no label && no align");
17432                 cfg = combobox
17433                      
17434                 
17435         }
17436          
17437         var settings=this;
17438         ['xs','sm','md','lg'].map(function(size){
17439             if (settings[size]) {
17440                 cfg.cls += ' col-' + size + '-' + settings[size];
17441             }
17442         });
17443         
17444         return cfg;
17445         
17446     },
17447     
17448     _initEventsCalled : false,
17449     
17450     // private
17451     initEvents: function()
17452     {   
17453         if (this._initEventsCalled) { // as we call render... prevent looping...
17454             return;
17455         }
17456         this._initEventsCalled = true;
17457         
17458         if (!this.store) {
17459             throw "can not find store for combo";
17460         }
17461         
17462         this.indicator = this.indicatorEl();
17463         
17464         this.store = Roo.factory(this.store, Roo.data);
17465         this.store.parent = this;
17466         
17467         // if we are building from html. then this element is so complex, that we can not really
17468         // use the rendered HTML.
17469         // so we have to trash and replace the previous code.
17470         if (Roo.XComponent.build_from_html) {
17471             // remove this element....
17472             var e = this.el.dom, k=0;
17473             while (e ) { e = e.previousSibling;  ++k;}
17474
17475             this.el.remove();
17476             
17477             this.el=false;
17478             this.rendered = false;
17479             
17480             this.render(this.parent().getChildContainer(true), k);
17481         }
17482         
17483         if(Roo.isIOS && this.useNativeIOS){
17484             this.initIOSView();
17485             return;
17486         }
17487         
17488         /*
17489          * Touch Devices
17490          */
17491         
17492         if(Roo.isTouch && this.mobileTouchView){
17493             this.initTouchView();
17494             return;
17495         }
17496         
17497         if(this.tickable){
17498             this.initTickableEvents();
17499             return;
17500         }
17501         
17502         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17503         
17504         if(this.hiddenName){
17505             
17506             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17507             
17508             this.hiddenField.dom.value =
17509                 this.hiddenValue !== undefined ? this.hiddenValue :
17510                 this.value !== undefined ? this.value : '';
17511
17512             // prevent input submission
17513             this.el.dom.removeAttribute('name');
17514             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17515              
17516              
17517         }
17518         //if(Roo.isGecko){
17519         //    this.el.dom.setAttribute('autocomplete', 'off');
17520         //}
17521         
17522         var cls = 'x-combo-list';
17523         
17524         //this.list = new Roo.Layer({
17525         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17526         //});
17527         
17528         var _this = this;
17529         
17530         (function(){
17531             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17532             _this.list.setWidth(lw);
17533         }).defer(100);
17534         
17535         this.list.on('mouseover', this.onViewOver, this);
17536         this.list.on('mousemove', this.onViewMove, this);
17537         this.list.on('scroll', this.onViewScroll, this);
17538         
17539         /*
17540         this.list.swallowEvent('mousewheel');
17541         this.assetHeight = 0;
17542
17543         if(this.title){
17544             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17545             this.assetHeight += this.header.getHeight();
17546         }
17547
17548         this.innerList = this.list.createChild({cls:cls+'-inner'});
17549         this.innerList.on('mouseover', this.onViewOver, this);
17550         this.innerList.on('mousemove', this.onViewMove, this);
17551         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17552         
17553         if(this.allowBlank && !this.pageSize && !this.disableClear){
17554             this.footer = this.list.createChild({cls:cls+'-ft'});
17555             this.pageTb = new Roo.Toolbar(this.footer);
17556            
17557         }
17558         if(this.pageSize){
17559             this.footer = this.list.createChild({cls:cls+'-ft'});
17560             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17561                     {pageSize: this.pageSize});
17562             
17563         }
17564         
17565         if (this.pageTb && this.allowBlank && !this.disableClear) {
17566             var _this = this;
17567             this.pageTb.add(new Roo.Toolbar.Fill(), {
17568                 cls: 'x-btn-icon x-btn-clear',
17569                 text: '&#160;',
17570                 handler: function()
17571                 {
17572                     _this.collapse();
17573                     _this.clearValue();
17574                     _this.onSelect(false, -1);
17575                 }
17576             });
17577         }
17578         if (this.footer) {
17579             this.assetHeight += this.footer.getHeight();
17580         }
17581         */
17582             
17583         if(!this.tpl){
17584             this.tpl = Roo.bootstrap.version == 4 ?
17585                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17586                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17587         }
17588
17589         this.view = new Roo.View(this.list, this.tpl, {
17590             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17591         });
17592         //this.view.wrapEl.setDisplayed(false);
17593         this.view.on('click', this.onViewClick, this);
17594         
17595         
17596         this.store.on('beforeload', this.onBeforeLoad, this);
17597         this.store.on('load', this.onLoad, this);
17598         this.store.on('loadexception', this.onLoadException, this);
17599         /*
17600         if(this.resizable){
17601             this.resizer = new Roo.Resizable(this.list,  {
17602                pinned:true, handles:'se'
17603             });
17604             this.resizer.on('resize', function(r, w, h){
17605                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17606                 this.listWidth = w;
17607                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17608                 this.restrictHeight();
17609             }, this);
17610             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17611         }
17612         */
17613         if(!this.editable){
17614             this.editable = true;
17615             this.setEditable(false);
17616         }
17617         
17618         /*
17619         
17620         if (typeof(this.events.add.listeners) != 'undefined') {
17621             
17622             this.addicon = this.wrap.createChild(
17623                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17624        
17625             this.addicon.on('click', function(e) {
17626                 this.fireEvent('add', this);
17627             }, this);
17628         }
17629         if (typeof(this.events.edit.listeners) != 'undefined') {
17630             
17631             this.editicon = this.wrap.createChild(
17632                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17633             if (this.addicon) {
17634                 this.editicon.setStyle('margin-left', '40px');
17635             }
17636             this.editicon.on('click', function(e) {
17637                 
17638                 // we fire even  if inothing is selected..
17639                 this.fireEvent('edit', this, this.lastData );
17640                 
17641             }, this);
17642         }
17643         */
17644         
17645         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17646             "up" : function(e){
17647                 this.inKeyMode = true;
17648                 this.selectPrev();
17649             },
17650
17651             "down" : function(e){
17652                 if(!this.isExpanded()){
17653                     this.onTriggerClick();
17654                 }else{
17655                     this.inKeyMode = true;
17656                     this.selectNext();
17657                 }
17658             },
17659
17660             "enter" : function(e){
17661 //                this.onViewClick();
17662                 //return true;
17663                 this.collapse();
17664                 
17665                 if(this.fireEvent("specialkey", this, e)){
17666                     this.onViewClick(false);
17667                 }
17668                 
17669                 return true;
17670             },
17671
17672             "esc" : function(e){
17673                 this.collapse();
17674             },
17675
17676             "tab" : function(e){
17677                 this.collapse();
17678                 
17679                 if(this.fireEvent("specialkey", this, e)){
17680                     this.onViewClick(false);
17681                 }
17682                 
17683                 return true;
17684             },
17685
17686             scope : this,
17687
17688             doRelay : function(foo, bar, hname){
17689                 if(hname == 'down' || this.scope.isExpanded()){
17690                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17691                 }
17692                 return true;
17693             },
17694
17695             forceKeyDown: true
17696         });
17697         
17698         
17699         this.queryDelay = Math.max(this.queryDelay || 10,
17700                 this.mode == 'local' ? 10 : 250);
17701         
17702         
17703         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17704         
17705         if(this.typeAhead){
17706             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17707         }
17708         if(this.editable !== false){
17709             this.inputEl().on("keyup", this.onKeyUp, this);
17710         }
17711         if(this.forceSelection){
17712             this.inputEl().on('blur', this.doForce, this);
17713         }
17714         
17715         if(this.multiple){
17716             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17717             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17718         }
17719     },
17720     
17721     initTickableEvents: function()
17722     {   
17723         this.createList();
17724         
17725         if(this.hiddenName){
17726             
17727             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17728             
17729             this.hiddenField.dom.value =
17730                 this.hiddenValue !== undefined ? this.hiddenValue :
17731                 this.value !== undefined ? this.value : '';
17732
17733             // prevent input submission
17734             this.el.dom.removeAttribute('name');
17735             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17736              
17737              
17738         }
17739         
17740 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17741         
17742         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17743         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17744         if(this.triggerList){
17745             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17746         }
17747          
17748         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17749         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17750         
17751         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17752         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17753         
17754         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17755         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17756         
17757         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17758         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17759         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17760         
17761         this.okBtn.hide();
17762         this.cancelBtn.hide();
17763         
17764         var _this = this;
17765         
17766         (function(){
17767             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17768             _this.list.setWidth(lw);
17769         }).defer(100);
17770         
17771         this.list.on('mouseover', this.onViewOver, this);
17772         this.list.on('mousemove', this.onViewMove, this);
17773         
17774         this.list.on('scroll', this.onViewScroll, this);
17775         
17776         if(!this.tpl){
17777             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17778                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17779         }
17780
17781         this.view = new Roo.View(this.list, this.tpl, {
17782             singleSelect:true,
17783             tickable:true,
17784             parent:this,
17785             store: this.store,
17786             selectedClass: this.selectedClass
17787         });
17788         
17789         //this.view.wrapEl.setDisplayed(false);
17790         this.view.on('click', this.onViewClick, this);
17791         
17792         
17793         
17794         this.store.on('beforeload', this.onBeforeLoad, this);
17795         this.store.on('load', this.onLoad, this);
17796         this.store.on('loadexception', this.onLoadException, this);
17797         
17798         if(this.editable){
17799             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17800                 "up" : function(e){
17801                     this.inKeyMode = true;
17802                     this.selectPrev();
17803                 },
17804
17805                 "down" : function(e){
17806                     this.inKeyMode = true;
17807                     this.selectNext();
17808                 },
17809
17810                 "enter" : function(e){
17811                     if(this.fireEvent("specialkey", this, e)){
17812                         this.onViewClick(false);
17813                     }
17814                     
17815                     return true;
17816                 },
17817
17818                 "esc" : function(e){
17819                     this.onTickableFooterButtonClick(e, false, false);
17820                 },
17821
17822                 "tab" : function(e){
17823                     this.fireEvent("specialkey", this, e);
17824                     
17825                     this.onTickableFooterButtonClick(e, false, false);
17826                     
17827                     return true;
17828                 },
17829
17830                 scope : this,
17831
17832                 doRelay : function(e, fn, key){
17833                     if(this.scope.isExpanded()){
17834                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17835                     }
17836                     return true;
17837                 },
17838
17839                 forceKeyDown: true
17840             });
17841         }
17842         
17843         this.queryDelay = Math.max(this.queryDelay || 10,
17844                 this.mode == 'local' ? 10 : 250);
17845         
17846         
17847         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17848         
17849         if(this.typeAhead){
17850             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17851         }
17852         
17853         if(this.editable !== false){
17854             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17855         }
17856         
17857         this.indicator = this.indicatorEl();
17858         
17859         if(this.indicator){
17860             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17861             this.indicator.hide();
17862         }
17863         
17864     },
17865
17866     onDestroy : function(){
17867         if(this.view){
17868             this.view.setStore(null);
17869             this.view.el.removeAllListeners();
17870             this.view.el.remove();
17871             this.view.purgeListeners();
17872         }
17873         if(this.list){
17874             this.list.dom.innerHTML  = '';
17875         }
17876         
17877         if(this.store){
17878             this.store.un('beforeload', this.onBeforeLoad, this);
17879             this.store.un('load', this.onLoad, this);
17880             this.store.un('loadexception', this.onLoadException, this);
17881         }
17882         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17883     },
17884
17885     // private
17886     fireKey : function(e){
17887         if(e.isNavKeyPress() && !this.list.isVisible()){
17888             this.fireEvent("specialkey", this, e);
17889         }
17890     },
17891
17892     // private
17893     onResize: function(w, h)
17894     {
17895         
17896         
17897 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17898 //        
17899 //        if(typeof w != 'number'){
17900 //            // we do not handle it!?!?
17901 //            return;
17902 //        }
17903 //        var tw = this.trigger.getWidth();
17904 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17905 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17906 //        var x = w - tw;
17907 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17908 //            
17909 //        //this.trigger.setStyle('left', x+'px');
17910 //        
17911 //        if(this.list && this.listWidth === undefined){
17912 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17913 //            this.list.setWidth(lw);
17914 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17915 //        }
17916         
17917     
17918         
17919     },
17920
17921     /**
17922      * Allow or prevent the user from directly editing the field text.  If false is passed,
17923      * the user will only be able to select from the items defined in the dropdown list.  This method
17924      * is the runtime equivalent of setting the 'editable' config option at config time.
17925      * @param {Boolean} value True to allow the user to directly edit the field text
17926      */
17927     setEditable : function(value){
17928         if(value == this.editable){
17929             return;
17930         }
17931         this.editable = value;
17932         if(!value){
17933             this.inputEl().dom.setAttribute('readOnly', true);
17934             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17935             this.inputEl().addClass('x-combo-noedit');
17936         }else{
17937             this.inputEl().dom.removeAttribute('readOnly');
17938             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17939             this.inputEl().removeClass('x-combo-noedit');
17940         }
17941     },
17942
17943     // private
17944     
17945     onBeforeLoad : function(combo,opts){
17946         if(!this.hasFocus){
17947             return;
17948         }
17949          if (!opts.add) {
17950             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17951          }
17952         this.restrictHeight();
17953         this.selectedIndex = -1;
17954     },
17955
17956     // private
17957     onLoad : function(){
17958         
17959         this.hasQuery = false;
17960         
17961         if(!this.hasFocus){
17962             return;
17963         }
17964         
17965         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17966             this.loading.hide();
17967         }
17968         
17969         if(this.store.getCount() > 0){
17970             
17971             this.expand();
17972             this.restrictHeight();
17973             if(this.lastQuery == this.allQuery){
17974                 if(this.editable && !this.tickable){
17975                     this.inputEl().dom.select();
17976                 }
17977                 
17978                 if(
17979                     !this.selectByValue(this.value, true) &&
17980                     this.autoFocus && 
17981                     (
17982                         !this.store.lastOptions ||
17983                         typeof(this.store.lastOptions.add) == 'undefined' || 
17984                         this.store.lastOptions.add != true
17985                     )
17986                 ){
17987                     this.select(0, true);
17988                 }
17989             }else{
17990                 if(this.autoFocus){
17991                     this.selectNext();
17992                 }
17993                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17994                     this.taTask.delay(this.typeAheadDelay);
17995                 }
17996             }
17997         }else{
17998             this.onEmptyResults();
17999         }
18000         
18001         //this.el.focus();
18002     },
18003     // private
18004     onLoadException : function()
18005     {
18006         this.hasQuery = false;
18007         
18008         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18009             this.loading.hide();
18010         }
18011         
18012         if(this.tickable && this.editable){
18013             return;
18014         }
18015         
18016         this.collapse();
18017         // only causes errors at present
18018         //Roo.log(this.store.reader.jsonData);
18019         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18020             // fixme
18021             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18022         //}
18023         
18024         
18025     },
18026     // private
18027     onTypeAhead : function(){
18028         if(this.store.getCount() > 0){
18029             var r = this.store.getAt(0);
18030             var newValue = r.data[this.displayField];
18031             var len = newValue.length;
18032             var selStart = this.getRawValue().length;
18033             
18034             if(selStart != len){
18035                 this.setRawValue(newValue);
18036                 this.selectText(selStart, newValue.length);
18037             }
18038         }
18039     },
18040
18041     // private
18042     onSelect : function(record, index){
18043         
18044         if(this.fireEvent('beforeselect', this, record, index) !== false){
18045         
18046             this.setFromData(index > -1 ? record.data : false);
18047             
18048             this.collapse();
18049             this.fireEvent('select', this, record, index);
18050         }
18051     },
18052
18053     /**
18054      * Returns the currently selected field value or empty string if no value is set.
18055      * @return {String} value The selected value
18056      */
18057     getValue : function()
18058     {
18059         if(Roo.isIOS && this.useNativeIOS){
18060             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18061         }
18062         
18063         if(this.multiple){
18064             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18065         }
18066         
18067         if(this.valueField){
18068             return typeof this.value != 'undefined' ? this.value : '';
18069         }else{
18070             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18071         }
18072     },
18073     
18074     getRawValue : function()
18075     {
18076         if(Roo.isIOS && this.useNativeIOS){
18077             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18078         }
18079         
18080         var v = this.inputEl().getValue();
18081         
18082         return v;
18083     },
18084
18085     /**
18086      * Clears any text/value currently set in the field
18087      */
18088     clearValue : function(){
18089         
18090         if(this.hiddenField){
18091             this.hiddenField.dom.value = '';
18092         }
18093         this.value = '';
18094         this.setRawValue('');
18095         this.lastSelectionText = '';
18096         this.lastData = false;
18097         
18098         var close = this.closeTriggerEl();
18099         
18100         if(close){
18101             close.hide();
18102         }
18103         
18104         this.validate();
18105         
18106     },
18107
18108     /**
18109      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18110      * will be displayed in the field.  If the value does not match the data value of an existing item,
18111      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18112      * Otherwise the field will be blank (although the value will still be set).
18113      * @param {String} value The value to match
18114      */
18115     setValue : function(v)
18116     {
18117         if(Roo.isIOS && this.useNativeIOS){
18118             this.setIOSValue(v);
18119             return;
18120         }
18121         
18122         if(this.multiple){
18123             this.syncValue();
18124             return;
18125         }
18126         
18127         var text = v;
18128         if(this.valueField){
18129             var r = this.findRecord(this.valueField, v);
18130             if(r){
18131                 text = r.data[this.displayField];
18132             }else if(this.valueNotFoundText !== undefined){
18133                 text = this.valueNotFoundText;
18134             }
18135         }
18136         this.lastSelectionText = text;
18137         if(this.hiddenField){
18138             this.hiddenField.dom.value = v;
18139         }
18140         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18141         this.value = v;
18142         
18143         var close = this.closeTriggerEl();
18144         
18145         if(close){
18146             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18147         }
18148         
18149         this.validate();
18150     },
18151     /**
18152      * @property {Object} the last set data for the element
18153      */
18154     
18155     lastData : false,
18156     /**
18157      * Sets the value of the field based on a object which is related to the record format for the store.
18158      * @param {Object} value the value to set as. or false on reset?
18159      */
18160     setFromData : function(o){
18161         
18162         if(this.multiple){
18163             this.addItem(o);
18164             return;
18165         }
18166             
18167         var dv = ''; // display value
18168         var vv = ''; // value value..
18169         this.lastData = o;
18170         if (this.displayField) {
18171             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18172         } else {
18173             // this is an error condition!!!
18174             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18175         }
18176         
18177         if(this.valueField){
18178             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18179         }
18180         
18181         var close = this.closeTriggerEl();
18182         
18183         if(close){
18184             if(dv.length || vv * 1 > 0){
18185                 close.show() ;
18186                 this.blockFocus=true;
18187             } else {
18188                 close.hide();
18189             }             
18190         }
18191         
18192         if(this.hiddenField){
18193             this.hiddenField.dom.value = vv;
18194             
18195             this.lastSelectionText = dv;
18196             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18197             this.value = vv;
18198             return;
18199         }
18200         // no hidden field.. - we store the value in 'value', but still display
18201         // display field!!!!
18202         this.lastSelectionText = dv;
18203         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18204         this.value = vv;
18205         
18206         
18207         
18208     },
18209     // private
18210     reset : function(){
18211         // overridden so that last data is reset..
18212         
18213         if(this.multiple){
18214             this.clearItem();
18215             return;
18216         }
18217         
18218         this.setValue(this.originalValue);
18219         //this.clearInvalid();
18220         this.lastData = false;
18221         if (this.view) {
18222             this.view.clearSelections();
18223         }
18224         
18225         this.validate();
18226     },
18227     // private
18228     findRecord : function(prop, value){
18229         var record;
18230         if(this.store.getCount() > 0){
18231             this.store.each(function(r){
18232                 if(r.data[prop] == value){
18233                     record = r;
18234                     return false;
18235                 }
18236                 return true;
18237             });
18238         }
18239         return record;
18240     },
18241     
18242     getName: function()
18243     {
18244         // returns hidden if it's set..
18245         if (!this.rendered) {return ''};
18246         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18247         
18248     },
18249     // private
18250     onViewMove : function(e, t){
18251         this.inKeyMode = false;
18252     },
18253
18254     // private
18255     onViewOver : function(e, t){
18256         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18257             return;
18258         }
18259         var item = this.view.findItemFromChild(t);
18260         
18261         if(item){
18262             var index = this.view.indexOf(item);
18263             this.select(index, false);
18264         }
18265     },
18266
18267     // private
18268     onViewClick : function(view, doFocus, el, e)
18269     {
18270         var index = this.view.getSelectedIndexes()[0];
18271         
18272         var r = this.store.getAt(index);
18273         
18274         if(this.tickable){
18275             
18276             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18277                 return;
18278             }
18279             
18280             var rm = false;
18281             var _this = this;
18282             
18283             Roo.each(this.tickItems, function(v,k){
18284                 
18285                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18286                     Roo.log(v);
18287                     _this.tickItems.splice(k, 1);
18288                     
18289                     if(typeof(e) == 'undefined' && view == false){
18290                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18291                     }
18292                     
18293                     rm = true;
18294                     return;
18295                 }
18296             });
18297             
18298             if(rm){
18299                 return;
18300             }
18301             
18302             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18303                 this.tickItems.push(r.data);
18304             }
18305             
18306             if(typeof(e) == 'undefined' && view == false){
18307                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18308             }
18309                     
18310             return;
18311         }
18312         
18313         if(r){
18314             this.onSelect(r, index);
18315         }
18316         if(doFocus !== false && !this.blockFocus){
18317             this.inputEl().focus();
18318         }
18319     },
18320
18321     // private
18322     restrictHeight : function(){
18323         //this.innerList.dom.style.height = '';
18324         //var inner = this.innerList.dom;
18325         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18326         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18327         //this.list.beginUpdate();
18328         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18329         this.list.alignTo(this.inputEl(), this.listAlign);
18330         this.list.alignTo(this.inputEl(), this.listAlign);
18331         //this.list.endUpdate();
18332     },
18333
18334     // private
18335     onEmptyResults : function(){
18336         
18337         if(this.tickable && this.editable){
18338             this.hasFocus = false;
18339             this.restrictHeight();
18340             return;
18341         }
18342         
18343         this.collapse();
18344     },
18345
18346     /**
18347      * Returns true if the dropdown list is expanded, else false.
18348      */
18349     isExpanded : function(){
18350         return this.list.isVisible();
18351     },
18352
18353     /**
18354      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18355      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18356      * @param {String} value The data value of the item to select
18357      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18358      * selected item if it is not currently in view (defaults to true)
18359      * @return {Boolean} True if the value matched an item in the list, else false
18360      */
18361     selectByValue : function(v, scrollIntoView){
18362         if(v !== undefined && v !== null){
18363             var r = this.findRecord(this.valueField || this.displayField, v);
18364             if(r){
18365                 this.select(this.store.indexOf(r), scrollIntoView);
18366                 return true;
18367             }
18368         }
18369         return false;
18370     },
18371
18372     /**
18373      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18374      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18375      * @param {Number} index The zero-based index of the list item to select
18376      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18377      * selected item if it is not currently in view (defaults to true)
18378      */
18379     select : function(index, scrollIntoView){
18380         this.selectedIndex = index;
18381         this.view.select(index);
18382         if(scrollIntoView !== false){
18383             var el = this.view.getNode(index);
18384             /*
18385              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18386              */
18387             if(el){
18388                 this.list.scrollChildIntoView(el, false);
18389             }
18390         }
18391     },
18392
18393     // private
18394     selectNext : function(){
18395         var ct = this.store.getCount();
18396         if(ct > 0){
18397             if(this.selectedIndex == -1){
18398                 this.select(0);
18399             }else if(this.selectedIndex < ct-1){
18400                 this.select(this.selectedIndex+1);
18401             }
18402         }
18403     },
18404
18405     // private
18406     selectPrev : function(){
18407         var ct = this.store.getCount();
18408         if(ct > 0){
18409             if(this.selectedIndex == -1){
18410                 this.select(0);
18411             }else if(this.selectedIndex != 0){
18412                 this.select(this.selectedIndex-1);
18413             }
18414         }
18415     },
18416
18417     // private
18418     onKeyUp : function(e){
18419         if(this.editable !== false && !e.isSpecialKey()){
18420             this.lastKey = e.getKey();
18421             this.dqTask.delay(this.queryDelay);
18422         }
18423     },
18424
18425     // private
18426     validateBlur : function(){
18427         return !this.list || !this.list.isVisible();   
18428     },
18429
18430     // private
18431     initQuery : function(){
18432         
18433         var v = this.getRawValue();
18434         
18435         if(this.tickable && this.editable){
18436             v = this.tickableInputEl().getValue();
18437         }
18438         
18439         this.doQuery(v);
18440     },
18441
18442     // private
18443     doForce : function(){
18444         if(this.inputEl().dom.value.length > 0){
18445             this.inputEl().dom.value =
18446                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18447              
18448         }
18449     },
18450
18451     /**
18452      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18453      * query allowing the query action to be canceled if needed.
18454      * @param {String} query The SQL query to execute
18455      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18456      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18457      * saved in the current store (defaults to false)
18458      */
18459     doQuery : function(q, forceAll){
18460         
18461         if(q === undefined || q === null){
18462             q = '';
18463         }
18464         var qe = {
18465             query: q,
18466             forceAll: forceAll,
18467             combo: this,
18468             cancel:false
18469         };
18470         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18471             return false;
18472         }
18473         q = qe.query;
18474         
18475         forceAll = qe.forceAll;
18476         if(forceAll === true || (q.length >= this.minChars)){
18477             
18478             this.hasQuery = true;
18479             
18480             if(this.lastQuery != q || this.alwaysQuery){
18481                 this.lastQuery = q;
18482                 if(this.mode == 'local'){
18483                     this.selectedIndex = -1;
18484                     if(forceAll){
18485                         this.store.clearFilter();
18486                     }else{
18487                         
18488                         if(this.specialFilter){
18489                             this.fireEvent('specialfilter', this);
18490                             this.onLoad();
18491                             return;
18492                         }
18493                         
18494                         this.store.filter(this.displayField, q);
18495                     }
18496                     
18497                     this.store.fireEvent("datachanged", this.store);
18498                     
18499                     this.onLoad();
18500                     
18501                     
18502                 }else{
18503                     
18504                     this.store.baseParams[this.queryParam] = q;
18505                     
18506                     var options = {params : this.getParams(q)};
18507                     
18508                     if(this.loadNext){
18509                         options.add = true;
18510                         options.params.start = this.page * this.pageSize;
18511                     }
18512                     
18513                     this.store.load(options);
18514                     
18515                     /*
18516                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18517                      *  we should expand the list on onLoad
18518                      *  so command out it
18519                      */
18520 //                    this.expand();
18521                 }
18522             }else{
18523                 this.selectedIndex = -1;
18524                 this.onLoad();   
18525             }
18526         }
18527         
18528         this.loadNext = false;
18529     },
18530     
18531     // private
18532     getParams : function(q){
18533         var p = {};
18534         //p[this.queryParam] = q;
18535         
18536         if(this.pageSize){
18537             p.start = 0;
18538             p.limit = this.pageSize;
18539         }
18540         return p;
18541     },
18542
18543     /**
18544      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18545      */
18546     collapse : function(){
18547         if(!this.isExpanded()){
18548             return;
18549         }
18550         
18551         this.list.hide();
18552         
18553         this.hasFocus = false;
18554         
18555         if(this.tickable){
18556             this.okBtn.hide();
18557             this.cancelBtn.hide();
18558             this.trigger.show();
18559             
18560             if(this.editable){
18561                 this.tickableInputEl().dom.value = '';
18562                 this.tickableInputEl().blur();
18563             }
18564             
18565         }
18566         
18567         Roo.get(document).un('mousedown', this.collapseIf, this);
18568         Roo.get(document).un('mousewheel', this.collapseIf, this);
18569         if (!this.editable) {
18570             Roo.get(document).un('keydown', this.listKeyPress, this);
18571         }
18572         this.fireEvent('collapse', this);
18573         
18574         this.validate();
18575     },
18576
18577     // private
18578     collapseIf : function(e){
18579         var in_combo  = e.within(this.el);
18580         var in_list =  e.within(this.list);
18581         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18582         
18583         if (in_combo || in_list || is_list) {
18584             //e.stopPropagation();
18585             return;
18586         }
18587         
18588         if(this.tickable){
18589             this.onTickableFooterButtonClick(e, false, false);
18590         }
18591
18592         this.collapse();
18593         
18594     },
18595
18596     /**
18597      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18598      */
18599     expand : function(){
18600        
18601         if(this.isExpanded() || !this.hasFocus){
18602             return;
18603         }
18604         
18605         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18606         this.list.setWidth(lw);
18607         
18608         Roo.log('expand');
18609         
18610         this.list.show();
18611         
18612         this.restrictHeight();
18613         
18614         if(this.tickable){
18615             
18616             this.tickItems = Roo.apply([], this.item);
18617             
18618             this.okBtn.show();
18619             this.cancelBtn.show();
18620             this.trigger.hide();
18621             
18622             if(this.editable){
18623                 this.tickableInputEl().focus();
18624             }
18625             
18626         }
18627         
18628         Roo.get(document).on('mousedown', this.collapseIf, this);
18629         Roo.get(document).on('mousewheel', this.collapseIf, this);
18630         if (!this.editable) {
18631             Roo.get(document).on('keydown', this.listKeyPress, this);
18632         }
18633         
18634         this.fireEvent('expand', this);
18635     },
18636
18637     // private
18638     // Implements the default empty TriggerField.onTriggerClick function
18639     onTriggerClick : function(e)
18640     {
18641         Roo.log('trigger click');
18642         
18643         if(this.disabled || !this.triggerList){
18644             return;
18645         }
18646         
18647         this.page = 0;
18648         this.loadNext = false;
18649         
18650         if(this.isExpanded()){
18651             this.collapse();
18652             if (!this.blockFocus) {
18653                 this.inputEl().focus();
18654             }
18655             
18656         }else {
18657             this.hasFocus = true;
18658             if(this.triggerAction == 'all') {
18659                 this.doQuery(this.allQuery, true);
18660             } else {
18661                 this.doQuery(this.getRawValue());
18662             }
18663             if (!this.blockFocus) {
18664                 this.inputEl().focus();
18665             }
18666         }
18667     },
18668     
18669     onTickableTriggerClick : function(e)
18670     {
18671         if(this.disabled){
18672             return;
18673         }
18674         
18675         this.page = 0;
18676         this.loadNext = false;
18677         this.hasFocus = true;
18678         
18679         if(this.triggerAction == 'all') {
18680             this.doQuery(this.allQuery, true);
18681         } else {
18682             this.doQuery(this.getRawValue());
18683         }
18684     },
18685     
18686     onSearchFieldClick : function(e)
18687     {
18688         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18689             this.onTickableFooterButtonClick(e, false, false);
18690             return;
18691         }
18692         
18693         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18694             return;
18695         }
18696         
18697         this.page = 0;
18698         this.loadNext = false;
18699         this.hasFocus = true;
18700         
18701         if(this.triggerAction == 'all') {
18702             this.doQuery(this.allQuery, true);
18703         } else {
18704             this.doQuery(this.getRawValue());
18705         }
18706     },
18707     
18708     listKeyPress : function(e)
18709     {
18710         //Roo.log('listkeypress');
18711         // scroll to first matching element based on key pres..
18712         if (e.isSpecialKey()) {
18713             return false;
18714         }
18715         var k = String.fromCharCode(e.getKey()).toUpperCase();
18716         //Roo.log(k);
18717         var match  = false;
18718         var csel = this.view.getSelectedNodes();
18719         var cselitem = false;
18720         if (csel.length) {
18721             var ix = this.view.indexOf(csel[0]);
18722             cselitem  = this.store.getAt(ix);
18723             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18724                 cselitem = false;
18725             }
18726             
18727         }
18728         
18729         this.store.each(function(v) { 
18730             if (cselitem) {
18731                 // start at existing selection.
18732                 if (cselitem.id == v.id) {
18733                     cselitem = false;
18734                 }
18735                 return true;
18736             }
18737                 
18738             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18739                 match = this.store.indexOf(v);
18740                 return false;
18741             }
18742             return true;
18743         }, this);
18744         
18745         if (match === false) {
18746             return true; // no more action?
18747         }
18748         // scroll to?
18749         this.view.select(match);
18750         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18751         sn.scrollIntoView(sn.dom.parentNode, false);
18752     },
18753     
18754     onViewScroll : function(e, t){
18755         
18756         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){
18757             return;
18758         }
18759         
18760         this.hasQuery = true;
18761         
18762         this.loading = this.list.select('.loading', true).first();
18763         
18764         if(this.loading === null){
18765             this.list.createChild({
18766                 tag: 'div',
18767                 cls: 'loading roo-select2-more-results roo-select2-active',
18768                 html: 'Loading more results...'
18769             });
18770             
18771             this.loading = this.list.select('.loading', true).first();
18772             
18773             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18774             
18775             this.loading.hide();
18776         }
18777         
18778         this.loading.show();
18779         
18780         var _combo = this;
18781         
18782         this.page++;
18783         this.loadNext = true;
18784         
18785         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18786         
18787         return;
18788     },
18789     
18790     addItem : function(o)
18791     {   
18792         var dv = ''; // display value
18793         
18794         if (this.displayField) {
18795             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18796         } else {
18797             // this is an error condition!!!
18798             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18799         }
18800         
18801         if(!dv.length){
18802             return;
18803         }
18804         
18805         var choice = this.choices.createChild({
18806             tag: 'li',
18807             cls: 'roo-select2-search-choice',
18808             cn: [
18809                 {
18810                     tag: 'div',
18811                     html: dv
18812                 },
18813                 {
18814                     tag: 'a',
18815                     href: '#',
18816                     cls: 'roo-select2-search-choice-close fa fa-times',
18817                     tabindex: '-1'
18818                 }
18819             ]
18820             
18821         }, this.searchField);
18822         
18823         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18824         
18825         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18826         
18827         this.item.push(o);
18828         
18829         this.lastData = o;
18830         
18831         this.syncValue();
18832         
18833         this.inputEl().dom.value = '';
18834         
18835         this.validate();
18836     },
18837     
18838     onRemoveItem : function(e, _self, o)
18839     {
18840         e.preventDefault();
18841         
18842         this.lastItem = Roo.apply([], this.item);
18843         
18844         var index = this.item.indexOf(o.data) * 1;
18845         
18846         if( index < 0){
18847             Roo.log('not this item?!');
18848             return;
18849         }
18850         
18851         this.item.splice(index, 1);
18852         o.item.remove();
18853         
18854         this.syncValue();
18855         
18856         this.fireEvent('remove', this, e);
18857         
18858         this.validate();
18859         
18860     },
18861     
18862     syncValue : function()
18863     {
18864         if(!this.item.length){
18865             this.clearValue();
18866             return;
18867         }
18868             
18869         var value = [];
18870         var _this = this;
18871         Roo.each(this.item, function(i){
18872             if(_this.valueField){
18873                 value.push(i[_this.valueField]);
18874                 return;
18875             }
18876
18877             value.push(i);
18878         });
18879
18880         this.value = value.join(',');
18881
18882         if(this.hiddenField){
18883             this.hiddenField.dom.value = this.value;
18884         }
18885         
18886         this.store.fireEvent("datachanged", this.store);
18887         
18888         this.validate();
18889     },
18890     
18891     clearItem : function()
18892     {
18893         if(!this.multiple){
18894             return;
18895         }
18896         
18897         this.item = [];
18898         
18899         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18900            c.remove();
18901         });
18902         
18903         this.syncValue();
18904         
18905         this.validate();
18906         
18907         if(this.tickable && !Roo.isTouch){
18908             this.view.refresh();
18909         }
18910     },
18911     
18912     inputEl: function ()
18913     {
18914         if(Roo.isIOS && this.useNativeIOS){
18915             return this.el.select('select.roo-ios-select', true).first();
18916         }
18917         
18918         if(Roo.isTouch && this.mobileTouchView){
18919             return this.el.select('input.form-control',true).first();
18920         }
18921         
18922         if(this.tickable){
18923             return this.searchField;
18924         }
18925         
18926         return this.el.select('input.form-control',true).first();
18927     },
18928     
18929     onTickableFooterButtonClick : function(e, btn, el)
18930     {
18931         e.preventDefault();
18932         
18933         this.lastItem = Roo.apply([], this.item);
18934         
18935         if(btn && btn.name == 'cancel'){
18936             this.tickItems = Roo.apply([], this.item);
18937             this.collapse();
18938             return;
18939         }
18940         
18941         this.clearItem();
18942         
18943         var _this = this;
18944         
18945         Roo.each(this.tickItems, function(o){
18946             _this.addItem(o);
18947         });
18948         
18949         this.collapse();
18950         
18951     },
18952     
18953     validate : function()
18954     {
18955         if(this.getVisibilityEl().hasClass('hidden')){
18956             return true;
18957         }
18958         
18959         var v = this.getRawValue();
18960         
18961         if(this.multiple){
18962             v = this.getValue();
18963         }
18964         
18965         if(this.disabled || this.allowBlank || v.length){
18966             this.markValid();
18967             return true;
18968         }
18969         
18970         this.markInvalid();
18971         return false;
18972     },
18973     
18974     tickableInputEl : function()
18975     {
18976         if(!this.tickable || !this.editable){
18977             return this.inputEl();
18978         }
18979         
18980         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18981     },
18982     
18983     
18984     getAutoCreateTouchView : function()
18985     {
18986         var id = Roo.id();
18987         
18988         var cfg = {
18989             cls: 'form-group' //input-group
18990         };
18991         
18992         var input =  {
18993             tag: 'input',
18994             id : id,
18995             type : this.inputType,
18996             cls : 'form-control x-combo-noedit',
18997             autocomplete: 'new-password',
18998             placeholder : this.placeholder || '',
18999             readonly : true
19000         };
19001         
19002         if (this.name) {
19003             input.name = this.name;
19004         }
19005         
19006         if (this.size) {
19007             input.cls += ' input-' + this.size;
19008         }
19009         
19010         if (this.disabled) {
19011             input.disabled = true;
19012         }
19013         
19014         var inputblock = {
19015             cls : 'roo-combobox-wrap',
19016             cn : [
19017                 input
19018             ]
19019         };
19020         
19021         if(this.before){
19022             inputblock.cls += ' input-group';
19023             
19024             inputblock.cn.unshift({
19025                 tag :'span',
19026                 cls : 'input-group-addon input-group-prepend input-group-text',
19027                 html : this.before
19028             });
19029         }
19030         
19031         if(this.removable && !this.multiple){
19032             inputblock.cls += ' roo-removable';
19033             
19034             inputblock.cn.push({
19035                 tag: 'button',
19036                 html : 'x',
19037                 cls : 'roo-combo-removable-btn close'
19038             });
19039         }
19040
19041         if(this.hasFeedback && !this.allowBlank){
19042             
19043             inputblock.cls += ' has-feedback';
19044             
19045             inputblock.cn.push({
19046                 tag: 'span',
19047                 cls: 'glyphicon form-control-feedback'
19048             });
19049             
19050         }
19051         
19052         if (this.after) {
19053             
19054             inputblock.cls += (this.before) ? '' : ' input-group';
19055             
19056             inputblock.cn.push({
19057                 tag :'span',
19058                 cls : 'input-group-addon input-group-append input-group-text',
19059                 html : this.after
19060             });
19061         }
19062
19063         
19064         var ibwrap = inputblock;
19065         
19066         if(this.multiple){
19067             ibwrap = {
19068                 tag: 'ul',
19069                 cls: 'roo-select2-choices',
19070                 cn:[
19071                     {
19072                         tag: 'li',
19073                         cls: 'roo-select2-search-field',
19074                         cn: [
19075
19076                             inputblock
19077                         ]
19078                     }
19079                 ]
19080             };
19081         
19082             
19083         }
19084         
19085         var combobox = {
19086             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19087             cn: [
19088                 {
19089                     tag: 'input',
19090                     type : 'hidden',
19091                     cls: 'form-hidden-field'
19092                 },
19093                 ibwrap
19094             ]
19095         };
19096         
19097         if(!this.multiple && this.showToggleBtn){
19098             
19099             var caret = {
19100                 cls: 'caret'
19101             };
19102             
19103             if (this.caret != false) {
19104                 caret = {
19105                      tag: 'i',
19106                      cls: 'fa fa-' + this.caret
19107                 };
19108                 
19109             }
19110             
19111             combobox.cn.push({
19112                 tag :'span',
19113                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19114                 cn : [
19115                     Roo.bootstrap.version == 3 ? caret : '',
19116                     {
19117                         tag: 'span',
19118                         cls: 'combobox-clear',
19119                         cn  : [
19120                             {
19121                                 tag : 'i',
19122                                 cls: 'icon-remove'
19123                             }
19124                         ]
19125                     }
19126                 ]
19127
19128             })
19129         }
19130         
19131         if(this.multiple){
19132             combobox.cls += ' roo-select2-container-multi';
19133         }
19134         
19135         var required =  this.allowBlank ?  {
19136                     tag : 'i',
19137                     style: 'display: none'
19138                 } : {
19139                    tag : 'i',
19140                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19141                    tooltip : 'This field is required'
19142                 };
19143         
19144         var align = this.labelAlign || this.parentLabelAlign();
19145         
19146         if (align ==='left' && this.fieldLabel.length) {
19147
19148             cfg.cn = [
19149                 required,
19150                 {
19151                     tag: 'label',
19152                     cls : 'control-label col-form-label',
19153                     html : this.fieldLabel
19154
19155                 },
19156                 {
19157                     cls : 'roo-combobox-wrap ', 
19158                     cn: [
19159                         combobox
19160                     ]
19161                 }
19162             ];
19163             
19164             var labelCfg = cfg.cn[1];
19165             var contentCfg = cfg.cn[2];
19166             
19167
19168             if(this.indicatorpos == 'right'){
19169                 cfg.cn = [
19170                     {
19171                         tag: 'label',
19172                         'for' :  id,
19173                         cls : 'control-label col-form-label',
19174                         cn : [
19175                             {
19176                                 tag : 'span',
19177                                 html : this.fieldLabel
19178                             },
19179                             required
19180                         ]
19181                     },
19182                     {
19183                         cls : "roo-combobox-wrap ",
19184                         cn: [
19185                             combobox
19186                         ]
19187                     }
19188
19189                 ];
19190                 
19191                 labelCfg = cfg.cn[0];
19192                 contentCfg = cfg.cn[1];
19193             }
19194             
19195            
19196             
19197             if(this.labelWidth > 12){
19198                 labelCfg.style = "width: " + this.labelWidth + 'px';
19199             }
19200            
19201             if(this.labelWidth < 13 && this.labelmd == 0){
19202                 this.labelmd = this.labelWidth;
19203             }
19204             
19205             if(this.labellg > 0){
19206                 labelCfg.cls += ' col-lg-' + this.labellg;
19207                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19208             }
19209             
19210             if(this.labelmd > 0){
19211                 labelCfg.cls += ' col-md-' + this.labelmd;
19212                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19213             }
19214             
19215             if(this.labelsm > 0){
19216                 labelCfg.cls += ' col-sm-' + this.labelsm;
19217                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19218             }
19219             
19220             if(this.labelxs > 0){
19221                 labelCfg.cls += ' col-xs-' + this.labelxs;
19222                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19223             }
19224                 
19225                 
19226         } else if ( this.fieldLabel.length) {
19227             cfg.cn = [
19228                required,
19229                 {
19230                     tag: 'label',
19231                     cls : 'control-label',
19232                     html : this.fieldLabel
19233
19234                 },
19235                 {
19236                     cls : '', 
19237                     cn: [
19238                         combobox
19239                     ]
19240                 }
19241             ];
19242             
19243             if(this.indicatorpos == 'right'){
19244                 cfg.cn = [
19245                     {
19246                         tag: 'label',
19247                         cls : 'control-label',
19248                         html : this.fieldLabel,
19249                         cn : [
19250                             required
19251                         ]
19252                     },
19253                     {
19254                         cls : '', 
19255                         cn: [
19256                             combobox
19257                         ]
19258                     }
19259                 ];
19260             }
19261         } else {
19262             cfg.cn = combobox;    
19263         }
19264         
19265         
19266         var settings = this;
19267         
19268         ['xs','sm','md','lg'].map(function(size){
19269             if (settings[size]) {
19270                 cfg.cls += ' col-' + size + '-' + settings[size];
19271             }
19272         });
19273         
19274         return cfg;
19275     },
19276     
19277     initTouchView : function()
19278     {
19279         this.renderTouchView();
19280         
19281         this.touchViewEl.on('scroll', function(){
19282             this.el.dom.scrollTop = 0;
19283         }, this);
19284         
19285         this.originalValue = this.getValue();
19286         
19287         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19288         
19289         this.inputEl().on("click", this.showTouchView, this);
19290         if (this.triggerEl) {
19291             this.triggerEl.on("click", this.showTouchView, this);
19292         }
19293         
19294         
19295         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19296         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19297         
19298         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19299         
19300         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19301         this.store.on('load', this.onTouchViewLoad, this);
19302         this.store.on('loadexception', this.onTouchViewLoadException, this);
19303         
19304         if(this.hiddenName){
19305             
19306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19307             
19308             this.hiddenField.dom.value =
19309                 this.hiddenValue !== undefined ? this.hiddenValue :
19310                 this.value !== undefined ? this.value : '';
19311         
19312             this.el.dom.removeAttribute('name');
19313             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19314         }
19315         
19316         if(this.multiple){
19317             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19318             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19319         }
19320         
19321         if(this.removable && !this.multiple){
19322             var close = this.closeTriggerEl();
19323             if(close){
19324                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19325                 close.on('click', this.removeBtnClick, this, close);
19326             }
19327         }
19328         /*
19329          * fix the bug in Safari iOS8
19330          */
19331         this.inputEl().on("focus", function(e){
19332             document.activeElement.blur();
19333         }, this);
19334         
19335         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19336         
19337         return;
19338         
19339         
19340     },
19341     
19342     renderTouchView : function()
19343     {
19344         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19345         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19346         
19347         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19348         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19349         
19350         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19351         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19352         this.touchViewBodyEl.setStyle('overflow', 'auto');
19353         
19354         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19355         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19356         
19357         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19358         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19359         
19360     },
19361     
19362     showTouchView : function()
19363     {
19364         if(this.disabled){
19365             return;
19366         }
19367         
19368         this.touchViewHeaderEl.hide();
19369
19370         if(this.modalTitle.length){
19371             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19372             this.touchViewHeaderEl.show();
19373         }
19374
19375         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19376         this.touchViewEl.show();
19377
19378         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19379         
19380         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19381         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19382
19383         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19384
19385         if(this.modalTitle.length){
19386             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19387         }
19388         
19389         this.touchViewBodyEl.setHeight(bodyHeight);
19390
19391         if(this.animate){
19392             var _this = this;
19393             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19394         }else{
19395             this.touchViewEl.addClass(['in','show']);
19396         }
19397         
19398         if(this._touchViewMask){
19399             Roo.get(document.body).addClass("x-body-masked");
19400             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19401             this._touchViewMask.setStyle('z-index', 10000);
19402             this._touchViewMask.addClass('show');
19403         }
19404         
19405         this.doTouchViewQuery();
19406         
19407     },
19408     
19409     hideTouchView : function()
19410     {
19411         this.touchViewEl.removeClass(['in','show']);
19412
19413         if(this.animate){
19414             var _this = this;
19415             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19416         }else{
19417             this.touchViewEl.setStyle('display', 'none');
19418         }
19419         
19420         if(this._touchViewMask){
19421             this._touchViewMask.removeClass('show');
19422             Roo.get(document.body).removeClass("x-body-masked");
19423         }
19424     },
19425     
19426     setTouchViewValue : function()
19427     {
19428         if(this.multiple){
19429             this.clearItem();
19430         
19431             var _this = this;
19432
19433             Roo.each(this.tickItems, function(o){
19434                 this.addItem(o);
19435             }, this);
19436         }
19437         
19438         this.hideTouchView();
19439     },
19440     
19441     doTouchViewQuery : function()
19442     {
19443         var qe = {
19444             query: '',
19445             forceAll: true,
19446             combo: this,
19447             cancel:false
19448         };
19449         
19450         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19451             return false;
19452         }
19453         
19454         if(!this.alwaysQuery || this.mode == 'local'){
19455             this.onTouchViewLoad();
19456             return;
19457         }
19458         
19459         this.store.load();
19460     },
19461     
19462     onTouchViewBeforeLoad : function(combo,opts)
19463     {
19464         return;
19465     },
19466
19467     // private
19468     onTouchViewLoad : function()
19469     {
19470         if(this.store.getCount() < 1){
19471             this.onTouchViewEmptyResults();
19472             return;
19473         }
19474         
19475         this.clearTouchView();
19476         
19477         var rawValue = this.getRawValue();
19478         
19479         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19480         
19481         this.tickItems = [];
19482         
19483         this.store.data.each(function(d, rowIndex){
19484             var row = this.touchViewListGroup.createChild(template);
19485             
19486             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19487                 row.addClass(d.data.cls);
19488             }
19489             
19490             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19491                 var cfg = {
19492                     data : d.data,
19493                     html : d.data[this.displayField]
19494                 };
19495                 
19496                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19497                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19498                 }
19499             }
19500             row.removeClass('selected');
19501             if(!this.multiple && this.valueField &&
19502                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19503             {
19504                 // radio buttons..
19505                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19506                 row.addClass('selected');
19507             }
19508             
19509             if(this.multiple && this.valueField &&
19510                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19511             {
19512                 
19513                 // checkboxes...
19514                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19515                 this.tickItems.push(d.data);
19516             }
19517             
19518             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19519             
19520         }, this);
19521         
19522         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19523         
19524         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19525
19526         if(this.modalTitle.length){
19527             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19528         }
19529
19530         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19531         
19532         if(this.mobile_restrict_height && listHeight < bodyHeight){
19533             this.touchViewBodyEl.setHeight(listHeight);
19534         }
19535         
19536         var _this = this;
19537         
19538         if(firstChecked && listHeight > bodyHeight){
19539             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19540         }
19541         
19542     },
19543     
19544     onTouchViewLoadException : function()
19545     {
19546         this.hideTouchView();
19547     },
19548     
19549     onTouchViewEmptyResults : function()
19550     {
19551         this.clearTouchView();
19552         
19553         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19554         
19555         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19556         
19557     },
19558     
19559     clearTouchView : function()
19560     {
19561         this.touchViewListGroup.dom.innerHTML = '';
19562     },
19563     
19564     onTouchViewClick : function(e, el, o)
19565     {
19566         e.preventDefault();
19567         
19568         var row = o.row;
19569         var rowIndex = o.rowIndex;
19570         
19571         var r = this.store.getAt(rowIndex);
19572         
19573         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19574             
19575             if(!this.multiple){
19576                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19577                     c.dom.removeAttribute('checked');
19578                 }, this);
19579
19580                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19581
19582                 this.setFromData(r.data);
19583
19584                 var close = this.closeTriggerEl();
19585
19586                 if(close){
19587                     close.show();
19588                 }
19589
19590                 this.hideTouchView();
19591
19592                 this.fireEvent('select', this, r, rowIndex);
19593
19594                 return;
19595             }
19596
19597             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19598                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19599                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19600                 return;
19601             }
19602
19603             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19604             this.addItem(r.data);
19605             this.tickItems.push(r.data);
19606         }
19607     },
19608     
19609     getAutoCreateNativeIOS : function()
19610     {
19611         var cfg = {
19612             cls: 'form-group' //input-group,
19613         };
19614         
19615         var combobox =  {
19616             tag: 'select',
19617             cls : 'roo-ios-select'
19618         };
19619         
19620         if (this.name) {
19621             combobox.name = this.name;
19622         }
19623         
19624         if (this.disabled) {
19625             combobox.disabled = true;
19626         }
19627         
19628         var settings = this;
19629         
19630         ['xs','sm','md','lg'].map(function(size){
19631             if (settings[size]) {
19632                 cfg.cls += ' col-' + size + '-' + settings[size];
19633             }
19634         });
19635         
19636         cfg.cn = combobox;
19637         
19638         return cfg;
19639         
19640     },
19641     
19642     initIOSView : function()
19643     {
19644         this.store.on('load', this.onIOSViewLoad, this);
19645         
19646         return;
19647     },
19648     
19649     onIOSViewLoad : function()
19650     {
19651         if(this.store.getCount() < 1){
19652             return;
19653         }
19654         
19655         this.clearIOSView();
19656         
19657         if(this.allowBlank) {
19658             
19659             var default_text = '-- SELECT --';
19660             
19661             if(this.placeholder.length){
19662                 default_text = this.placeholder;
19663             }
19664             
19665             if(this.emptyTitle.length){
19666                 default_text += ' - ' + this.emptyTitle + ' -';
19667             }
19668             
19669             var opt = this.inputEl().createChild({
19670                 tag: 'option',
19671                 value : 0,
19672                 html : default_text
19673             });
19674             
19675             var o = {};
19676             o[this.valueField] = 0;
19677             o[this.displayField] = default_text;
19678             
19679             this.ios_options.push({
19680                 data : o,
19681                 el : opt
19682             });
19683             
19684         }
19685         
19686         this.store.data.each(function(d, rowIndex){
19687             
19688             var html = '';
19689             
19690             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19691                 html = d.data[this.displayField];
19692             }
19693             
19694             var value = '';
19695             
19696             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19697                 value = d.data[this.valueField];
19698             }
19699             
19700             var option = {
19701                 tag: 'option',
19702                 value : value,
19703                 html : html
19704             };
19705             
19706             if(this.value == d.data[this.valueField]){
19707                 option['selected'] = true;
19708             }
19709             
19710             var opt = this.inputEl().createChild(option);
19711             
19712             this.ios_options.push({
19713                 data : d.data,
19714                 el : opt
19715             });
19716             
19717         }, this);
19718         
19719         this.inputEl().on('change', function(){
19720            this.fireEvent('select', this);
19721         }, this);
19722         
19723     },
19724     
19725     clearIOSView: function()
19726     {
19727         this.inputEl().dom.innerHTML = '';
19728         
19729         this.ios_options = [];
19730     },
19731     
19732     setIOSValue: function(v)
19733     {
19734         this.value = v;
19735         
19736         if(!this.ios_options){
19737             return;
19738         }
19739         
19740         Roo.each(this.ios_options, function(opts){
19741            
19742            opts.el.dom.removeAttribute('selected');
19743            
19744            if(opts.data[this.valueField] != v){
19745                return;
19746            }
19747            
19748            opts.el.dom.setAttribute('selected', true);
19749            
19750         }, this);
19751     }
19752
19753     /** 
19754     * @cfg {Boolean} grow 
19755     * @hide 
19756     */
19757     /** 
19758     * @cfg {Number} growMin 
19759     * @hide 
19760     */
19761     /** 
19762     * @cfg {Number} growMax 
19763     * @hide 
19764     */
19765     /**
19766      * @hide
19767      * @method autoSize
19768      */
19769 });
19770
19771 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19772     
19773     header : {
19774         tag: 'div',
19775         cls: 'modal-header',
19776         cn: [
19777             {
19778                 tag: 'h4',
19779                 cls: 'modal-title'
19780             }
19781         ]
19782     },
19783     
19784     body : {
19785         tag: 'div',
19786         cls: 'modal-body',
19787         cn: [
19788             {
19789                 tag: 'ul',
19790                 cls: 'list-group'
19791             }
19792         ]
19793     },
19794     
19795     listItemRadio : {
19796         tag: 'li',
19797         cls: 'list-group-item',
19798         cn: [
19799             {
19800                 tag: 'span',
19801                 cls: 'roo-combobox-list-group-item-value'
19802             },
19803             {
19804                 tag: 'div',
19805                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19806                 cn: [
19807                     {
19808                         tag: 'input',
19809                         type: 'radio'
19810                     },
19811                     {
19812                         tag: 'label'
19813                     }
19814                 ]
19815             }
19816         ]
19817     },
19818     
19819     listItemCheckbox : {
19820         tag: 'li',
19821         cls: 'list-group-item',
19822         cn: [
19823             {
19824                 tag: 'span',
19825                 cls: 'roo-combobox-list-group-item-value'
19826             },
19827             {
19828                 tag: 'div',
19829                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19830                 cn: [
19831                     {
19832                         tag: 'input',
19833                         type: 'checkbox'
19834                     },
19835                     {
19836                         tag: 'label'
19837                     }
19838                 ]
19839             }
19840         ]
19841     },
19842     
19843     emptyResult : {
19844         tag: 'div',
19845         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19846     },
19847     
19848     footer : {
19849         tag: 'div',
19850         cls: 'modal-footer',
19851         cn: [
19852             {
19853                 tag: 'div',
19854                 cls: 'row',
19855                 cn: [
19856                     {
19857                         tag: 'div',
19858                         cls: 'col-xs-6 text-left',
19859                         cn: {
19860                             tag: 'button',
19861                             cls: 'btn btn-danger roo-touch-view-cancel',
19862                             html: 'Cancel'
19863                         }
19864                     },
19865                     {
19866                         tag: 'div',
19867                         cls: 'col-xs-6 text-right',
19868                         cn: {
19869                             tag: 'button',
19870                             cls: 'btn btn-success roo-touch-view-ok',
19871                             html: 'OK'
19872                         }
19873                     }
19874                 ]
19875             }
19876         ]
19877         
19878     }
19879 });
19880
19881 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19882     
19883     touchViewTemplate : {
19884         tag: 'div',
19885         cls: 'modal fade roo-combobox-touch-view',
19886         cn: [
19887             {
19888                 tag: 'div',
19889                 cls: 'modal-dialog',
19890                 style : 'position:fixed', // we have to fix position....
19891                 cn: [
19892                     {
19893                         tag: 'div',
19894                         cls: 'modal-content',
19895                         cn: [
19896                             Roo.bootstrap.form.ComboBox.header,
19897                             Roo.bootstrap.form.ComboBox.body,
19898                             Roo.bootstrap.form.ComboBox.footer
19899                         ]
19900                     }
19901                 ]
19902             }
19903         ]
19904     }
19905 });/*
19906  * Based on:
19907  * Ext JS Library 1.1.1
19908  * Copyright(c) 2006-2007, Ext JS, LLC.
19909  *
19910  * Originally Released Under LGPL - original licence link has changed is not relivant.
19911  *
19912  * Fork - LGPL
19913  * <script type="text/javascript">
19914  */
19915
19916 /**
19917  * @class Roo.View
19918  * @extends Roo.util.Observable
19919  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19920  * This class also supports single and multi selection modes. <br>
19921  * Create a data model bound view:
19922  <pre><code>
19923  var store = new Roo.data.Store(...);
19924
19925  var view = new Roo.View({
19926     el : "my-element",
19927     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19928  
19929     singleSelect: true,
19930     selectedClass: "ydataview-selected",
19931     store: store
19932  });
19933
19934  // listen for node click?
19935  view.on("click", function(vw, index, node, e){
19936  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19937  });
19938
19939  // load XML data
19940  dataModel.load("foobar.xml");
19941  </code></pre>
19942  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19943  * <br><br>
19944  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19945  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19946  * 
19947  * Note: old style constructor is still suported (container, template, config)
19948  * 
19949  * @constructor
19950  * Create a new View
19951  * @param {Object} config The config object
19952  * 
19953  */
19954 Roo.View = function(config, depreciated_tpl, depreciated_config){
19955     
19956     this.parent = false;
19957     
19958     if (typeof(depreciated_tpl) == 'undefined') {
19959         // new way.. - universal constructor.
19960         Roo.apply(this, config);
19961         this.el  = Roo.get(this.el);
19962     } else {
19963         // old format..
19964         this.el  = Roo.get(config);
19965         this.tpl = depreciated_tpl;
19966         Roo.apply(this, depreciated_config);
19967     }
19968     this.wrapEl  = this.el.wrap().wrap();
19969     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19970     
19971     
19972     if(typeof(this.tpl) == "string"){
19973         this.tpl = new Roo.Template(this.tpl);
19974     } else {
19975         // support xtype ctors..
19976         this.tpl = new Roo.factory(this.tpl, Roo);
19977     }
19978     
19979     
19980     this.tpl.compile();
19981     
19982     /** @private */
19983     this.addEvents({
19984         /**
19985          * @event beforeclick
19986          * Fires before a click is processed. Returns false to cancel the default action.
19987          * @param {Roo.View} this
19988          * @param {Number} index The index of the target node
19989          * @param {HTMLElement} node The target node
19990          * @param {Roo.EventObject} e The raw event object
19991          */
19992             "beforeclick" : true,
19993         /**
19994          * @event click
19995          * Fires when a template node is clicked.
19996          * @param {Roo.View} this
19997          * @param {Number} index The index of the target node
19998          * @param {HTMLElement} node The target node
19999          * @param {Roo.EventObject} e The raw event object
20000          */
20001             "click" : true,
20002         /**
20003          * @event dblclick
20004          * Fires when a template node is double clicked.
20005          * @param {Roo.View} this
20006          * @param {Number} index The index of the target node
20007          * @param {HTMLElement} node The target node
20008          * @param {Roo.EventObject} e The raw event object
20009          */
20010             "dblclick" : true,
20011         /**
20012          * @event contextmenu
20013          * Fires when a template node is right clicked.
20014          * @param {Roo.View} this
20015          * @param {Number} index The index of the target node
20016          * @param {HTMLElement} node The target node
20017          * @param {Roo.EventObject} e The raw event object
20018          */
20019             "contextmenu" : true,
20020         /**
20021          * @event selectionchange
20022          * Fires when the selected nodes change.
20023          * @param {Roo.View} this
20024          * @param {Array} selections Array of the selected nodes
20025          */
20026             "selectionchange" : true,
20027     
20028         /**
20029          * @event beforeselect
20030          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20031          * @param {Roo.View} this
20032          * @param {HTMLElement} node The node to be selected
20033          * @param {Array} selections Array of currently selected nodes
20034          */
20035             "beforeselect" : true,
20036         /**
20037          * @event preparedata
20038          * Fires on every row to render, to allow you to change the data.
20039          * @param {Roo.View} this
20040          * @param {Object} data to be rendered (change this)
20041          */
20042           "preparedata" : true
20043           
20044           
20045         });
20046
20047
20048
20049     this.el.on({
20050         "click": this.onClick,
20051         "dblclick": this.onDblClick,
20052         "contextmenu": this.onContextMenu,
20053         scope:this
20054     });
20055
20056     this.selections = [];
20057     this.nodes = [];
20058     this.cmp = new Roo.CompositeElementLite([]);
20059     if(this.store){
20060         this.store = Roo.factory(this.store, Roo.data);
20061         this.setStore(this.store, true);
20062     }
20063     
20064     if ( this.footer && this.footer.xtype) {
20065            
20066          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20067         
20068         this.footer.dataSource = this.store;
20069         this.footer.container = fctr;
20070         this.footer = Roo.factory(this.footer, Roo);
20071         fctr.insertFirst(this.el);
20072         
20073         // this is a bit insane - as the paging toolbar seems to detach the el..
20074 //        dom.parentNode.parentNode.parentNode
20075          // they get detached?
20076     }
20077     
20078     
20079     Roo.View.superclass.constructor.call(this);
20080     
20081     
20082 };
20083
20084 Roo.extend(Roo.View, Roo.util.Observable, {
20085     
20086      /**
20087      * @cfg {Roo.data.Store} store Data store to load data from.
20088      */
20089     store : false,
20090     
20091     /**
20092      * @cfg {String|Roo.Element} el The container element.
20093      */
20094     el : '',
20095     
20096     /**
20097      * @cfg {String|Roo.Template} tpl The template used by this View 
20098      */
20099     tpl : false,
20100     /**
20101      * @cfg {String} dataName the named area of the template to use as the data area
20102      *                          Works with domtemplates roo-name="name"
20103      */
20104     dataName: false,
20105     /**
20106      * @cfg {String} selectedClass The css class to add to selected nodes
20107      */
20108     selectedClass : "x-view-selected",
20109      /**
20110      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20111      */
20112     emptyText : "",
20113     
20114     /**
20115      * @cfg {String} text to display on mask (default Loading)
20116      */
20117     mask : false,
20118     /**
20119      * @cfg {Boolean} multiSelect Allow multiple selection
20120      */
20121     multiSelect : false,
20122     /**
20123      * @cfg {Boolean} singleSelect Allow single selection
20124      */
20125     singleSelect:  false,
20126     
20127     /**
20128      * @cfg {Boolean} toggleSelect - selecting 
20129      */
20130     toggleSelect : false,
20131     
20132     /**
20133      * @cfg {Boolean} tickable - selecting 
20134      */
20135     tickable : false,
20136     
20137     /**
20138      * Returns the element this view is bound to.
20139      * @return {Roo.Element}
20140      */
20141     getEl : function(){
20142         return this.wrapEl;
20143     },
20144     
20145     
20146
20147     /**
20148      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20149      */
20150     refresh : function(){
20151         //Roo.log('refresh');
20152         var t = this.tpl;
20153         
20154         // if we are using something like 'domtemplate', then
20155         // the what gets used is:
20156         // t.applySubtemplate(NAME, data, wrapping data..)
20157         // the outer template then get' applied with
20158         //     the store 'extra data'
20159         // and the body get's added to the
20160         //      roo-name="data" node?
20161         //      <span class='roo-tpl-{name}'></span> ?????
20162         
20163         
20164         
20165         this.clearSelections();
20166         this.el.update("");
20167         var html = [];
20168         var records = this.store.getRange();
20169         if(records.length < 1) {
20170             
20171             // is this valid??  = should it render a template??
20172             
20173             this.el.update(this.emptyText);
20174             return;
20175         }
20176         var el = this.el;
20177         if (this.dataName) {
20178             this.el.update(t.apply(this.store.meta)); //????
20179             el = this.el.child('.roo-tpl-' + this.dataName);
20180         }
20181         
20182         for(var i = 0, len = records.length; i < len; i++){
20183             var data = this.prepareData(records[i].data, i, records[i]);
20184             this.fireEvent("preparedata", this, data, i, records[i]);
20185             
20186             var d = Roo.apply({}, data);
20187             
20188             if(this.tickable){
20189                 Roo.apply(d, {'roo-id' : Roo.id()});
20190                 
20191                 var _this = this;
20192             
20193                 Roo.each(this.parent.item, function(item){
20194                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20195                         return;
20196                     }
20197                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20198                 });
20199             }
20200             
20201             html[html.length] = Roo.util.Format.trim(
20202                 this.dataName ?
20203                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20204                     t.apply(d)
20205             );
20206         }
20207         
20208         
20209         
20210         el.update(html.join(""));
20211         this.nodes = el.dom.childNodes;
20212         this.updateIndexes(0);
20213     },
20214     
20215
20216     /**
20217      * Function to override to reformat the data that is sent to
20218      * the template for each node.
20219      * DEPRICATED - use the preparedata event handler.
20220      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20221      * a JSON object for an UpdateManager bound view).
20222      */
20223     prepareData : function(data, index, record)
20224     {
20225         this.fireEvent("preparedata", this, data, index, record);
20226         return data;
20227     },
20228
20229     onUpdate : function(ds, record){
20230         // Roo.log('on update');   
20231         this.clearSelections();
20232         var index = this.store.indexOf(record);
20233         var n = this.nodes[index];
20234         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20235         n.parentNode.removeChild(n);
20236         this.updateIndexes(index, index);
20237     },
20238
20239     
20240     
20241 // --------- FIXME     
20242     onAdd : function(ds, records, index)
20243     {
20244         //Roo.log(['on Add', ds, records, index] );        
20245         this.clearSelections();
20246         if(this.nodes.length == 0){
20247             this.refresh();
20248             return;
20249         }
20250         var n = this.nodes[index];
20251         for(var i = 0, len = records.length; i < len; i++){
20252             var d = this.prepareData(records[i].data, i, records[i]);
20253             if(n){
20254                 this.tpl.insertBefore(n, d);
20255             }else{
20256                 
20257                 this.tpl.append(this.el, d);
20258             }
20259         }
20260         this.updateIndexes(index);
20261     },
20262
20263     onRemove : function(ds, record, index){
20264        // Roo.log('onRemove');
20265         this.clearSelections();
20266         var el = this.dataName  ?
20267             this.el.child('.roo-tpl-' + this.dataName) :
20268             this.el; 
20269         
20270         el.dom.removeChild(this.nodes[index]);
20271         this.updateIndexes(index);
20272     },
20273
20274     /**
20275      * Refresh an individual node.
20276      * @param {Number} index
20277      */
20278     refreshNode : function(index){
20279         this.onUpdate(this.store, this.store.getAt(index));
20280     },
20281
20282     updateIndexes : function(startIndex, endIndex){
20283         var ns = this.nodes;
20284         startIndex = startIndex || 0;
20285         endIndex = endIndex || ns.length - 1;
20286         for(var i = startIndex; i <= endIndex; i++){
20287             ns[i].nodeIndex = i;
20288         }
20289     },
20290
20291     /**
20292      * Changes the data store this view uses and refresh the view.
20293      * @param {Store} store
20294      */
20295     setStore : function(store, initial){
20296         if(!initial && this.store){
20297             this.store.un("datachanged", this.refresh);
20298             this.store.un("add", this.onAdd);
20299             this.store.un("remove", this.onRemove);
20300             this.store.un("update", this.onUpdate);
20301             this.store.un("clear", this.refresh);
20302             this.store.un("beforeload", this.onBeforeLoad);
20303             this.store.un("load", this.onLoad);
20304             this.store.un("loadexception", this.onLoad);
20305         }
20306         if(store){
20307           
20308             store.on("datachanged", this.refresh, this);
20309             store.on("add", this.onAdd, this);
20310             store.on("remove", this.onRemove, this);
20311             store.on("update", this.onUpdate, this);
20312             store.on("clear", this.refresh, this);
20313             store.on("beforeload", this.onBeforeLoad, this);
20314             store.on("load", this.onLoad, this);
20315             store.on("loadexception", this.onLoad, this);
20316         }
20317         
20318         if(store){
20319             this.refresh();
20320         }
20321     },
20322     /**
20323      * onbeforeLoad - masks the loading area.
20324      *
20325      */
20326     onBeforeLoad : function(store,opts)
20327     {
20328          //Roo.log('onBeforeLoad');   
20329         if (!opts.add) {
20330             this.el.update("");
20331         }
20332         this.el.mask(this.mask ? this.mask : "Loading" ); 
20333     },
20334     onLoad : function ()
20335     {
20336         this.el.unmask();
20337     },
20338     
20339
20340     /**
20341      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20342      * @param {HTMLElement} node
20343      * @return {HTMLElement} The template node
20344      */
20345     findItemFromChild : function(node){
20346         var el = this.dataName  ?
20347             this.el.child('.roo-tpl-' + this.dataName,true) :
20348             this.el.dom; 
20349         
20350         if(!node || node.parentNode == el){
20351                     return node;
20352             }
20353             var p = node.parentNode;
20354             while(p && p != el){
20355             if(p.parentNode == el){
20356                 return p;
20357             }
20358             p = p.parentNode;
20359         }
20360             return null;
20361     },
20362
20363     /** @ignore */
20364     onClick : function(e){
20365         var item = this.findItemFromChild(e.getTarget());
20366         if(item){
20367             var index = this.indexOf(item);
20368             if(this.onItemClick(item, index, e) !== false){
20369                 this.fireEvent("click", this, index, item, e);
20370             }
20371         }else{
20372             this.clearSelections();
20373         }
20374     },
20375
20376     /** @ignore */
20377     onContextMenu : function(e){
20378         var item = this.findItemFromChild(e.getTarget());
20379         if(item){
20380             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20381         }
20382     },
20383
20384     /** @ignore */
20385     onDblClick : function(e){
20386         var item = this.findItemFromChild(e.getTarget());
20387         if(item){
20388             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20389         }
20390     },
20391
20392     onItemClick : function(item, index, e)
20393     {
20394         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20395             return false;
20396         }
20397         if (this.toggleSelect) {
20398             var m = this.isSelected(item) ? 'unselect' : 'select';
20399             //Roo.log(m);
20400             var _t = this;
20401             _t[m](item, true, false);
20402             return true;
20403         }
20404         if(this.multiSelect || this.singleSelect){
20405             if(this.multiSelect && e.shiftKey && this.lastSelection){
20406                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20407             }else{
20408                 this.select(item, this.multiSelect && e.ctrlKey);
20409                 this.lastSelection = item;
20410             }
20411             
20412             if(!this.tickable){
20413                 e.preventDefault();
20414             }
20415             
20416         }
20417         return true;
20418     },
20419
20420     /**
20421      * Get the number of selected nodes.
20422      * @return {Number}
20423      */
20424     getSelectionCount : function(){
20425         return this.selections.length;
20426     },
20427
20428     /**
20429      * Get the currently selected nodes.
20430      * @return {Array} An array of HTMLElements
20431      */
20432     getSelectedNodes : function(){
20433         return this.selections;
20434     },
20435
20436     /**
20437      * Get the indexes of the selected nodes.
20438      * @return {Array}
20439      */
20440     getSelectedIndexes : function(){
20441         var indexes = [], s = this.selections;
20442         for(var i = 0, len = s.length; i < len; i++){
20443             indexes.push(s[i].nodeIndex);
20444         }
20445         return indexes;
20446     },
20447
20448     /**
20449      * Clear all selections
20450      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20451      */
20452     clearSelections : function(suppressEvent){
20453         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20454             this.cmp.elements = this.selections;
20455             this.cmp.removeClass(this.selectedClass);
20456             this.selections = [];
20457             if(!suppressEvent){
20458                 this.fireEvent("selectionchange", this, this.selections);
20459             }
20460         }
20461     },
20462
20463     /**
20464      * Returns true if the passed node is selected
20465      * @param {HTMLElement/Number} node The node or node index
20466      * @return {Boolean}
20467      */
20468     isSelected : function(node){
20469         var s = this.selections;
20470         if(s.length < 1){
20471             return false;
20472         }
20473         node = this.getNode(node);
20474         return s.indexOf(node) !== -1;
20475     },
20476
20477     /**
20478      * Selects nodes.
20479      * @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
20480      * @param {Boolean} keepExisting (optional) true to keep existing selections
20481      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20482      */
20483     select : function(nodeInfo, keepExisting, suppressEvent){
20484         if(nodeInfo instanceof Array){
20485             if(!keepExisting){
20486                 this.clearSelections(true);
20487             }
20488             for(var i = 0, len = nodeInfo.length; i < len; i++){
20489                 this.select(nodeInfo[i], true, true);
20490             }
20491             return;
20492         } 
20493         var node = this.getNode(nodeInfo);
20494         if(!node || this.isSelected(node)){
20495             return; // already selected.
20496         }
20497         if(!keepExisting){
20498             this.clearSelections(true);
20499         }
20500         
20501         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20502             Roo.fly(node).addClass(this.selectedClass);
20503             this.selections.push(node);
20504             if(!suppressEvent){
20505                 this.fireEvent("selectionchange", this, this.selections);
20506             }
20507         }
20508         
20509         
20510     },
20511       /**
20512      * Unselects nodes.
20513      * @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
20514      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20516      */
20517     unselect : function(nodeInfo, keepExisting, suppressEvent)
20518     {
20519         if(nodeInfo instanceof Array){
20520             Roo.each(this.selections, function(s) {
20521                 this.unselect(s, nodeInfo);
20522             }, this);
20523             return;
20524         }
20525         var node = this.getNode(nodeInfo);
20526         if(!node || !this.isSelected(node)){
20527             //Roo.log("not selected");
20528             return; // not selected.
20529         }
20530         // fireevent???
20531         var ns = [];
20532         Roo.each(this.selections, function(s) {
20533             if (s == node ) {
20534                 Roo.fly(node).removeClass(this.selectedClass);
20535
20536                 return;
20537             }
20538             ns.push(s);
20539         },this);
20540         
20541         this.selections= ns;
20542         this.fireEvent("selectionchange", this, this.selections);
20543     },
20544
20545     /**
20546      * Gets a template node.
20547      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20548      * @return {HTMLElement} The node or null if it wasn't found
20549      */
20550     getNode : function(nodeInfo){
20551         if(typeof nodeInfo == "string"){
20552             return document.getElementById(nodeInfo);
20553         }else if(typeof nodeInfo == "number"){
20554             return this.nodes[nodeInfo];
20555         }
20556         return nodeInfo;
20557     },
20558
20559     /**
20560      * Gets a range template nodes.
20561      * @param {Number} startIndex
20562      * @param {Number} endIndex
20563      * @return {Array} An array of nodes
20564      */
20565     getNodes : function(start, end){
20566         var ns = this.nodes;
20567         start = start || 0;
20568         end = typeof end == "undefined" ? ns.length - 1 : end;
20569         var nodes = [];
20570         if(start <= end){
20571             for(var i = start; i <= end; i++){
20572                 nodes.push(ns[i]);
20573             }
20574         } else{
20575             for(var i = start; i >= end; i--){
20576                 nodes.push(ns[i]);
20577             }
20578         }
20579         return nodes;
20580     },
20581
20582     /**
20583      * Finds the index of the passed node
20584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20585      * @return {Number} The index of the node or -1
20586      */
20587     indexOf : function(node){
20588         node = this.getNode(node);
20589         if(typeof node.nodeIndex == "number"){
20590             return node.nodeIndex;
20591         }
20592         var ns = this.nodes;
20593         for(var i = 0, len = ns.length; i < len; i++){
20594             if(ns[i] == node){
20595                 return i;
20596             }
20597         }
20598         return -1;
20599     }
20600 });
20601 /*
20602  * - LGPL
20603  *
20604  * based on jquery fullcalendar
20605  * 
20606  */
20607
20608 Roo.bootstrap = Roo.bootstrap || {};
20609 /**
20610  * @class Roo.bootstrap.Calendar
20611  * @extends Roo.bootstrap.Component
20612  * Bootstrap Calendar class
20613  * @cfg {Boolean} loadMask (true|false) default false
20614  * @cfg {Object} header generate the user specific header of the calendar, default false
20615
20616  * @constructor
20617  * Create a new Container
20618  * @param {Object} config The config object
20619  */
20620
20621
20622
20623 Roo.bootstrap.Calendar = function(config){
20624     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20625      this.addEvents({
20626         /**
20627              * @event select
20628              * Fires when a date is selected
20629              * @param {DatePicker} this
20630              * @param {Date} date The selected date
20631              */
20632         'select': true,
20633         /**
20634              * @event monthchange
20635              * Fires when the displayed month changes 
20636              * @param {DatePicker} this
20637              * @param {Date} date The selected month
20638              */
20639         'monthchange': true,
20640         /**
20641              * @event evententer
20642              * Fires when mouse over an event
20643              * @param {Calendar} this
20644              * @param {event} Event
20645              */
20646         'evententer': true,
20647         /**
20648              * @event eventleave
20649              * Fires when the mouse leaves an
20650              * @param {Calendar} this
20651              * @param {event}
20652              */
20653         'eventleave': true,
20654         /**
20655              * @event eventclick
20656              * Fires when the mouse click an
20657              * @param {Calendar} this
20658              * @param {event}
20659              */
20660         'eventclick': true
20661         
20662     });
20663
20664 };
20665
20666 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20667     
20668           /**
20669      * @cfg {Roo.data.Store} store
20670      * The data source for the calendar
20671      */
20672         store : false,
20673      /**
20674      * @cfg {Number} startDay
20675      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20676      */
20677     startDay : 0,
20678     
20679     loadMask : false,
20680     
20681     header : false,
20682       
20683     getAutoCreate : function(){
20684         
20685         
20686         var fc_button = function(name, corner, style, content ) {
20687             return Roo.apply({},{
20688                 tag : 'span',
20689                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20690                          (corner.length ?
20691                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20692                             ''
20693                         ),
20694                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20695                 unselectable: 'on'
20696             });
20697         };
20698         
20699         var header = {};
20700         
20701         if(!this.header){
20702             header = {
20703                 tag : 'table',
20704                 cls : 'fc-header',
20705                 style : 'width:100%',
20706                 cn : [
20707                     {
20708                         tag: 'tr',
20709                         cn : [
20710                             {
20711                                 tag : 'td',
20712                                 cls : 'fc-header-left',
20713                                 cn : [
20714                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20715                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20716                                     { tag: 'span', cls: 'fc-header-space' },
20717                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20718
20719
20720                                 ]
20721                             },
20722
20723                             {
20724                                 tag : 'td',
20725                                 cls : 'fc-header-center',
20726                                 cn : [
20727                                     {
20728                                         tag: 'span',
20729                                         cls: 'fc-header-title',
20730                                         cn : {
20731                                             tag: 'H2',
20732                                             html : 'month / year'
20733                                         }
20734                                     }
20735
20736                                 ]
20737                             },
20738                             {
20739                                 tag : 'td',
20740                                 cls : 'fc-header-right',
20741                                 cn : [
20742                               /*      fc_button('month', 'left', '', 'month' ),
20743                                     fc_button('week', '', '', 'week' ),
20744                                     fc_button('day', 'right', '', 'day' )
20745                                 */    
20746
20747                                 ]
20748                             }
20749
20750                         ]
20751                     }
20752                 ]
20753             };
20754         }
20755         
20756         header = this.header;
20757         
20758        
20759         var cal_heads = function() {
20760             var ret = [];
20761             // fixme - handle this.
20762             
20763             for (var i =0; i < Date.dayNames.length; i++) {
20764                 var d = Date.dayNames[i];
20765                 ret.push({
20766                     tag: 'th',
20767                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20768                     html : d.substring(0,3)
20769                 });
20770                 
20771             }
20772             ret[0].cls += ' fc-first';
20773             ret[6].cls += ' fc-last';
20774             return ret;
20775         };
20776         var cal_cell = function(n) {
20777             return  {
20778                 tag: 'td',
20779                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20780                 cn : [
20781                     {
20782                         cn : [
20783                             {
20784                                 cls: 'fc-day-number',
20785                                 html: 'D'
20786                             },
20787                             {
20788                                 cls: 'fc-day-content',
20789                              
20790                                 cn : [
20791                                      {
20792                                         style: 'position: relative;' // height: 17px;
20793                                     }
20794                                 ]
20795                             }
20796                             
20797                             
20798                         ]
20799                     }
20800                 ]
20801                 
20802             }
20803         };
20804         var cal_rows = function() {
20805             
20806             var ret = [];
20807             for (var r = 0; r < 6; r++) {
20808                 var row= {
20809                     tag : 'tr',
20810                     cls : 'fc-week',
20811                     cn : []
20812                 };
20813                 
20814                 for (var i =0; i < Date.dayNames.length; i++) {
20815                     var d = Date.dayNames[i];
20816                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20817
20818                 }
20819                 row.cn[0].cls+=' fc-first';
20820                 row.cn[0].cn[0].style = 'min-height:90px';
20821                 row.cn[6].cls+=' fc-last';
20822                 ret.push(row);
20823                 
20824             }
20825             ret[0].cls += ' fc-first';
20826             ret[4].cls += ' fc-prev-last';
20827             ret[5].cls += ' fc-last';
20828             return ret;
20829             
20830         };
20831         
20832         var cal_table = {
20833             tag: 'table',
20834             cls: 'fc-border-separate',
20835             style : 'width:100%',
20836             cellspacing  : 0,
20837             cn : [
20838                 { 
20839                     tag: 'thead',
20840                     cn : [
20841                         { 
20842                             tag: 'tr',
20843                             cls : 'fc-first fc-last',
20844                             cn : cal_heads()
20845                         }
20846                     ]
20847                 },
20848                 { 
20849                     tag: 'tbody',
20850                     cn : cal_rows()
20851                 }
20852                   
20853             ]
20854         };
20855          
20856          var cfg = {
20857             cls : 'fc fc-ltr',
20858             cn : [
20859                 header,
20860                 {
20861                     cls : 'fc-content',
20862                     style : "position: relative;",
20863                     cn : [
20864                         {
20865                             cls : 'fc-view fc-view-month fc-grid',
20866                             style : 'position: relative',
20867                             unselectable : 'on',
20868                             cn : [
20869                                 {
20870                                     cls : 'fc-event-container',
20871                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20872                                 },
20873                                 cal_table
20874                             ]
20875                         }
20876                     ]
20877     
20878                 }
20879            ] 
20880             
20881         };
20882         
20883          
20884         
20885         return cfg;
20886     },
20887     
20888     
20889     initEvents : function()
20890     {
20891         if(!this.store){
20892             throw "can not find store for calendar";
20893         }
20894         
20895         var mark = {
20896             tag: "div",
20897             cls:"x-dlg-mask",
20898             style: "text-align:center",
20899             cn: [
20900                 {
20901                     tag: "div",
20902                     style: "background-color:white;width:50%;margin:250 auto",
20903                     cn: [
20904                         {
20905                             tag: "img",
20906                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20907                         },
20908                         {
20909                             tag: "span",
20910                             html: "Loading"
20911                         }
20912                         
20913                     ]
20914                 }
20915             ]
20916         };
20917         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20918         
20919         var size = this.el.select('.fc-content', true).first().getSize();
20920         this.maskEl.setSize(size.width, size.height);
20921         this.maskEl.enableDisplayMode("block");
20922         if(!this.loadMask){
20923             this.maskEl.hide();
20924         }
20925         
20926         this.store = Roo.factory(this.store, Roo.data);
20927         this.store.on('load', this.onLoad, this);
20928         this.store.on('beforeload', this.onBeforeLoad, this);
20929         
20930         this.resize();
20931         
20932         this.cells = this.el.select('.fc-day',true);
20933         //Roo.log(this.cells);
20934         this.textNodes = this.el.query('.fc-day-number');
20935         this.cells.addClassOnOver('fc-state-hover');
20936         
20937         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20938         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20939         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20940         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20941         
20942         this.on('monthchange', this.onMonthChange, this);
20943         
20944         this.update(new Date().clearTime());
20945     },
20946     
20947     resize : function() {
20948         var sz  = this.el.getSize();
20949         
20950         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20951         this.el.select('.fc-day-content div',true).setHeight(34);
20952     },
20953     
20954     
20955     // private
20956     showPrevMonth : function(e){
20957         this.update(this.activeDate.add("mo", -1));
20958     },
20959     showToday : function(e){
20960         this.update(new Date().clearTime());
20961     },
20962     // private
20963     showNextMonth : function(e){
20964         this.update(this.activeDate.add("mo", 1));
20965     },
20966
20967     // private
20968     showPrevYear : function(){
20969         this.update(this.activeDate.add("y", -1));
20970     },
20971
20972     // private
20973     showNextYear : function(){
20974         this.update(this.activeDate.add("y", 1));
20975     },
20976
20977     
20978    // private
20979     update : function(date)
20980     {
20981         var vd = this.activeDate;
20982         this.activeDate = date;
20983 //        if(vd && this.el){
20984 //            var t = date.getTime();
20985 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20986 //                Roo.log('using add remove');
20987 //                
20988 //                this.fireEvent('monthchange', this, date);
20989 //                
20990 //                this.cells.removeClass("fc-state-highlight");
20991 //                this.cells.each(function(c){
20992 //                   if(c.dateValue == t){
20993 //                       c.addClass("fc-state-highlight");
20994 //                       setTimeout(function(){
20995 //                            try{c.dom.firstChild.focus();}catch(e){}
20996 //                       }, 50);
20997 //                       return false;
20998 //                   }
20999 //                   return true;
21000 //                });
21001 //                return;
21002 //            }
21003 //        }
21004         
21005         var days = date.getDaysInMonth();
21006         
21007         var firstOfMonth = date.getFirstDateOfMonth();
21008         var startingPos = firstOfMonth.getDay()-this.startDay;
21009         
21010         if(startingPos < this.startDay){
21011             startingPos += 7;
21012         }
21013         
21014         var pm = date.add(Date.MONTH, -1);
21015         var prevStart = pm.getDaysInMonth()-startingPos;
21016 //        
21017         this.cells = this.el.select('.fc-day',true);
21018         this.textNodes = this.el.query('.fc-day-number');
21019         this.cells.addClassOnOver('fc-state-hover');
21020         
21021         var cells = this.cells.elements;
21022         var textEls = this.textNodes;
21023         
21024         Roo.each(cells, function(cell){
21025             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21026         });
21027         
21028         days += startingPos;
21029
21030         // convert everything to numbers so it's fast
21031         var day = 86400000;
21032         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21033         //Roo.log(d);
21034         //Roo.log(pm);
21035         //Roo.log(prevStart);
21036         
21037         var today = new Date().clearTime().getTime();
21038         var sel = date.clearTime().getTime();
21039         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21040         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21041         var ddMatch = this.disabledDatesRE;
21042         var ddText = this.disabledDatesText;
21043         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21044         var ddaysText = this.disabledDaysText;
21045         var format = this.format;
21046         
21047         var setCellClass = function(cal, cell){
21048             cell.row = 0;
21049             cell.events = [];
21050             cell.more = [];
21051             //Roo.log('set Cell Class');
21052             cell.title = "";
21053             var t = d.getTime();
21054             
21055             //Roo.log(d);
21056             
21057             cell.dateValue = t;
21058             if(t == today){
21059                 cell.className += " fc-today";
21060                 cell.className += " fc-state-highlight";
21061                 cell.title = cal.todayText;
21062             }
21063             if(t == sel){
21064                 // disable highlight in other month..
21065                 //cell.className += " fc-state-highlight";
21066                 
21067             }
21068             // disabling
21069             if(t < min) {
21070                 cell.className = " fc-state-disabled";
21071                 cell.title = cal.minText;
21072                 return;
21073             }
21074             if(t > max) {
21075                 cell.className = " fc-state-disabled";
21076                 cell.title = cal.maxText;
21077                 return;
21078             }
21079             if(ddays){
21080                 if(ddays.indexOf(d.getDay()) != -1){
21081                     cell.title = ddaysText;
21082                     cell.className = " fc-state-disabled";
21083                 }
21084             }
21085             if(ddMatch && format){
21086                 var fvalue = d.dateFormat(format);
21087                 if(ddMatch.test(fvalue)){
21088                     cell.title = ddText.replace("%0", fvalue);
21089                     cell.className = " fc-state-disabled";
21090                 }
21091             }
21092             
21093             if (!cell.initialClassName) {
21094                 cell.initialClassName = cell.dom.className;
21095             }
21096             
21097             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21098         };
21099
21100         var i = 0;
21101         
21102         for(; i < startingPos; i++) {
21103             textEls[i].innerHTML = (++prevStart);
21104             d.setDate(d.getDate()+1);
21105             
21106             cells[i].className = "fc-past fc-other-month";
21107             setCellClass(this, cells[i]);
21108         }
21109         
21110         var intDay = 0;
21111         
21112         for(; i < days; i++){
21113             intDay = i - startingPos + 1;
21114             textEls[i].innerHTML = (intDay);
21115             d.setDate(d.getDate()+1);
21116             
21117             cells[i].className = ''; // "x-date-active";
21118             setCellClass(this, cells[i]);
21119         }
21120         var extraDays = 0;
21121         
21122         for(; i < 42; i++) {
21123             textEls[i].innerHTML = (++extraDays);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-future fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21131         
21132         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21133         
21134         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21135         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21136         
21137         if(totalRows != 6){
21138             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21139             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21140         }
21141         
21142         this.fireEvent('monthchange', this, date);
21143         
21144         
21145         /*
21146         if(!this.internalRender){
21147             var main = this.el.dom.firstChild;
21148             var w = main.offsetWidth;
21149             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21150             Roo.fly(main).setWidth(w);
21151             this.internalRender = true;
21152             // opera does not respect the auto grow header center column
21153             // then, after it gets a width opera refuses to recalculate
21154             // without a second pass
21155             if(Roo.isOpera && !this.secondPass){
21156                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21157                 this.secondPass = true;
21158                 this.update.defer(10, this, [date]);
21159             }
21160         }
21161         */
21162         
21163     },
21164     
21165     findCell : function(dt) {
21166         dt = dt.clearTime().getTime();
21167         var ret = false;
21168         this.cells.each(function(c){
21169             //Roo.log("check " +c.dateValue + '?=' + dt);
21170             if(c.dateValue == dt){
21171                 ret = c;
21172                 return false;
21173             }
21174             return true;
21175         });
21176         
21177         return ret;
21178     },
21179     
21180     findCells : function(ev) {
21181         var s = ev.start.clone().clearTime().getTime();
21182        // Roo.log(s);
21183         var e= ev.end.clone().clearTime().getTime();
21184        // Roo.log(e);
21185         var ret = [];
21186         this.cells.each(function(c){
21187              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21188             
21189             if(c.dateValue > e){
21190                 return ;
21191             }
21192             if(c.dateValue < s){
21193                 return ;
21194             }
21195             ret.push(c);
21196         });
21197         
21198         return ret;    
21199     },
21200     
21201 //    findBestRow: function(cells)
21202 //    {
21203 //        var ret = 0;
21204 //        
21205 //        for (var i =0 ; i < cells.length;i++) {
21206 //            ret  = Math.max(cells[i].rows || 0,ret);
21207 //        }
21208 //        return ret;
21209 //        
21210 //    },
21211     
21212     
21213     addItem : function(ev)
21214     {
21215         // look for vertical location slot in
21216         var cells = this.findCells(ev);
21217         
21218 //        ev.row = this.findBestRow(cells);
21219         
21220         // work out the location.
21221         
21222         var crow = false;
21223         var rows = [];
21224         for(var i =0; i < cells.length; i++) {
21225             
21226             cells[i].row = cells[0].row;
21227             
21228             if(i == 0){
21229                 cells[i].row = cells[i].row + 1;
21230             }
21231             
21232             if (!crow) {
21233                 crow = {
21234                     start : cells[i],
21235                     end :  cells[i]
21236                 };
21237                 continue;
21238             }
21239             if (crow.start.getY() == cells[i].getY()) {
21240                 // on same row.
21241                 crow.end = cells[i];
21242                 continue;
21243             }
21244             // different row.
21245             rows.push(crow);
21246             crow = {
21247                 start: cells[i],
21248                 end : cells[i]
21249             };
21250             
21251         }
21252         
21253         rows.push(crow);
21254         ev.els = [];
21255         ev.rows = rows;
21256         ev.cells = cells;
21257         
21258         cells[0].events.push(ev);
21259         
21260         this.calevents.push(ev);
21261     },
21262     
21263     clearEvents: function() {
21264         
21265         if(!this.calevents){
21266             return;
21267         }
21268         
21269         Roo.each(this.cells.elements, function(c){
21270             c.row = 0;
21271             c.events = [];
21272             c.more = [];
21273         });
21274         
21275         Roo.each(this.calevents, function(e) {
21276             Roo.each(e.els, function(el) {
21277                 el.un('mouseenter' ,this.onEventEnter, this);
21278                 el.un('mouseleave' ,this.onEventLeave, this);
21279                 el.remove();
21280             },this);
21281         },this);
21282         
21283         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21284             e.remove();
21285         });
21286         
21287     },
21288     
21289     renderEvents: function()
21290     {   
21291         var _this = this;
21292         
21293         this.cells.each(function(c) {
21294             
21295             if(c.row < 5){
21296                 return;
21297             }
21298             
21299             var ev = c.events;
21300             
21301             var r = 4;
21302             if(c.row != c.events.length){
21303                 r = 4 - (4 - (c.row - c.events.length));
21304             }
21305             
21306             c.events = ev.slice(0, r);
21307             c.more = ev.slice(r);
21308             
21309             if(c.more.length && c.more.length == 1){
21310                 c.events.push(c.more.pop());
21311             }
21312             
21313             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21314             
21315         });
21316             
21317         this.cells.each(function(c) {
21318             
21319             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21320             
21321             
21322             for (var e = 0; e < c.events.length; e++){
21323                 var ev = c.events[e];
21324                 var rows = ev.rows;
21325                 
21326                 for(var i = 0; i < rows.length; i++) {
21327                 
21328                     // how many rows should it span..
21329
21330                     var  cfg = {
21331                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21332                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21333
21334                         unselectable : "on",
21335                         cn : [
21336                             {
21337                                 cls: 'fc-event-inner',
21338                                 cn : [
21339     //                                {
21340     //                                  tag:'span',
21341     //                                  cls: 'fc-event-time',
21342     //                                  html : cells.length > 1 ? '' : ev.time
21343     //                                },
21344                                     {
21345                                       tag:'span',
21346                                       cls: 'fc-event-title',
21347                                       html : String.format('{0}', ev.title)
21348                                     }
21349
21350
21351                                 ]
21352                             },
21353                             {
21354                                 cls: 'ui-resizable-handle ui-resizable-e',
21355                                 html : '&nbsp;&nbsp;&nbsp'
21356                             }
21357
21358                         ]
21359                     };
21360
21361                     if (i == 0) {
21362                         cfg.cls += ' fc-event-start';
21363                     }
21364                     if ((i+1) == rows.length) {
21365                         cfg.cls += ' fc-event-end';
21366                     }
21367
21368                     var ctr = _this.el.select('.fc-event-container',true).first();
21369                     var cg = ctr.createChild(cfg);
21370
21371                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21372                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21373
21374                     var r = (c.more.length) ? 1 : 0;
21375                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21376                     cg.setWidth(ebox.right - sbox.x -2);
21377
21378                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21379                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21380                     cg.on('click', _this.onEventClick, _this, ev);
21381
21382                     ev.els.push(cg);
21383                     
21384                 }
21385                 
21386             }
21387             
21388             
21389             if(c.more.length){
21390                 var  cfg = {
21391                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21392                     style : 'position: absolute',
21393                     unselectable : "on",
21394                     cn : [
21395                         {
21396                             cls: 'fc-event-inner',
21397                             cn : [
21398                                 {
21399                                   tag:'span',
21400                                   cls: 'fc-event-title',
21401                                   html : 'More'
21402                                 }
21403
21404
21405                             ]
21406                         },
21407                         {
21408                             cls: 'ui-resizable-handle ui-resizable-e',
21409                             html : '&nbsp;&nbsp;&nbsp'
21410                         }
21411
21412                     ]
21413                 };
21414
21415                 var ctr = _this.el.select('.fc-event-container',true).first();
21416                 var cg = ctr.createChild(cfg);
21417
21418                 var sbox = c.select('.fc-day-content',true).first().getBox();
21419                 var ebox = c.select('.fc-day-content',true).first().getBox();
21420                 //Roo.log(cg);
21421                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21422                 cg.setWidth(ebox.right - sbox.x -2);
21423
21424                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21425                 
21426             }
21427             
21428         });
21429         
21430         
21431         
21432     },
21433     
21434     onEventEnter: function (e, el,event,d) {
21435         this.fireEvent('evententer', this, el, event);
21436     },
21437     
21438     onEventLeave: function (e, el,event,d) {
21439         this.fireEvent('eventleave', this, el, event);
21440     },
21441     
21442     onEventClick: function (e, el,event,d) {
21443         this.fireEvent('eventclick', this, el, event);
21444     },
21445     
21446     onMonthChange: function () {
21447         this.store.load();
21448     },
21449     
21450     onMoreEventClick: function(e, el, more)
21451     {
21452         var _this = this;
21453         
21454         this.calpopover.placement = 'right';
21455         this.calpopover.setTitle('More');
21456         
21457         this.calpopover.setContent('');
21458         
21459         var ctr = this.calpopover.el.select('.popover-content', true).first();
21460         
21461         Roo.each(more, function(m){
21462             var cfg = {
21463                 cls : 'fc-event-hori fc-event-draggable',
21464                 html : m.title
21465             };
21466             var cg = ctr.createChild(cfg);
21467             
21468             cg.on('click', _this.onEventClick, _this, m);
21469         });
21470         
21471         this.calpopover.show(el);
21472         
21473         
21474     },
21475     
21476     onLoad: function () 
21477     {   
21478         this.calevents = [];
21479         var cal = this;
21480         
21481         if(this.store.getCount() > 0){
21482             this.store.data.each(function(d){
21483                cal.addItem({
21484                     id : d.data.id,
21485                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21486                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21487                     time : d.data.start_time,
21488                     title : d.data.title,
21489                     description : d.data.description,
21490                     venue : d.data.venue
21491                 });
21492             });
21493         }
21494         
21495         this.renderEvents();
21496         
21497         if(this.calevents.length && this.loadMask){
21498             this.maskEl.hide();
21499         }
21500     },
21501     
21502     onBeforeLoad: function()
21503     {
21504         this.clearEvents();
21505         if(this.loadMask){
21506             this.maskEl.show();
21507         }
21508     }
21509 });
21510
21511  
21512  /*
21513  * - LGPL
21514  *
21515  * element
21516  * 
21517  */
21518
21519 /**
21520  * @class Roo.bootstrap.Popover
21521  * @extends Roo.bootstrap.Component
21522  * @parent none builder
21523  * @children Roo.bootstrap.Component
21524  * Bootstrap Popover class
21525  * @cfg {String} html contents of the popover   (or false to use children..)
21526  * @cfg {String} title of popover (or false to hide)
21527  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21528  * @cfg {String} trigger click || hover (or false to trigger manually)
21529  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21530  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21531  *      - if false and it has a 'parent' then it will be automatically added to that element
21532  *      - if string - Roo.get  will be called 
21533  * @cfg {Number} delay - delay before showing
21534  
21535  * @constructor
21536  * Create a new Popover
21537  * @param {Object} config The config object
21538  */
21539
21540 Roo.bootstrap.Popover = function(config){
21541     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21542     
21543     this.addEvents({
21544         // raw events
21545          /**
21546          * @event show
21547          * After the popover show
21548          * 
21549          * @param {Roo.bootstrap.Popover} this
21550          */
21551         "show" : true,
21552         /**
21553          * @event hide
21554          * After the popover hide
21555          * 
21556          * @param {Roo.bootstrap.Popover} this
21557          */
21558         "hide" : true
21559     });
21560 };
21561
21562 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21563     
21564     title: false,
21565     html: false,
21566     
21567     placement : 'right',
21568     trigger : 'hover', // hover
21569     modal : false,
21570     delay : 0,
21571     
21572     over: false,
21573     
21574     can_build_overlaid : false,
21575     
21576     maskEl : false, // the mask element
21577     headerEl : false,
21578     contentEl : false,
21579     alignEl : false, // when show is called with an element - this get's stored.
21580     
21581     getChildContainer : function()
21582     {
21583         return this.contentEl;
21584         
21585     },
21586     getPopoverHeader : function()
21587     {
21588         this.title = true; // flag not to hide it..
21589         this.headerEl.addClass('p-0');
21590         return this.headerEl
21591     },
21592     
21593     
21594     getAutoCreate : function(){
21595          
21596         var cfg = {
21597            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21598            style: 'display:block',
21599            cn : [
21600                 {
21601                     cls : 'arrow'
21602                 },
21603                 {
21604                     cls : 'popover-inner ',
21605                     cn : [
21606                         {
21607                             tag: 'h3',
21608                             cls: 'popover-title popover-header',
21609                             html : this.title === false ? '' : this.title
21610                         },
21611                         {
21612                             cls : 'popover-content popover-body '  + (this.cls || ''),
21613                             html : this.html || ''
21614                         }
21615                     ]
21616                     
21617                 }
21618            ]
21619         };
21620         
21621         return cfg;
21622     },
21623     /**
21624      * @param {string} the title
21625      */
21626     setTitle: function(str)
21627     {
21628         this.title = str;
21629         if (this.el) {
21630             this.headerEl.dom.innerHTML = str;
21631         }
21632         
21633     },
21634     /**
21635      * @param {string} the body content
21636      */
21637     setContent: function(str)
21638     {
21639         this.html = str;
21640         if (this.contentEl) {
21641             this.contentEl.dom.innerHTML = str;
21642         }
21643         
21644     },
21645     // as it get's added to the bottom of the page.
21646     onRender : function(ct, position)
21647     {
21648         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21649         
21650         
21651         
21652         if(!this.el){
21653             var cfg = Roo.apply({},  this.getAutoCreate());
21654             cfg.id = Roo.id();
21655             
21656             if (this.cls) {
21657                 cfg.cls += ' ' + this.cls;
21658             }
21659             if (this.style) {
21660                 cfg.style = this.style;
21661             }
21662             //Roo.log("adding to ");
21663             this.el = Roo.get(document.body).createChild(cfg, position);
21664 //            Roo.log(this.el);
21665         }
21666         
21667         this.contentEl = this.el.select('.popover-content',true).first();
21668         this.headerEl =  this.el.select('.popover-title',true).first();
21669         
21670         var nitems = [];
21671         if(typeof(this.items) != 'undefined'){
21672             var items = this.items;
21673             delete this.items;
21674
21675             for(var i =0;i < items.length;i++) {
21676                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21677             }
21678         }
21679
21680         this.items = nitems;
21681         
21682         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21683         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21684         
21685         
21686         
21687         this.initEvents();
21688     },
21689     
21690     resizeMask : function()
21691     {
21692         this.maskEl.setSize(
21693             Roo.lib.Dom.getViewWidth(true),
21694             Roo.lib.Dom.getViewHeight(true)
21695         );
21696     },
21697     
21698     initEvents : function()
21699     {
21700         
21701         if (!this.modal) { 
21702             Roo.bootstrap.Popover.register(this);
21703         }
21704          
21705         this.arrowEl = this.el.select('.arrow',true).first();
21706         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21707         this.el.enableDisplayMode('block');
21708         this.el.hide();
21709  
21710         
21711         if (this.over === false && !this.parent()) {
21712             return; 
21713         }
21714         if (this.triggers === false) {
21715             return;
21716         }
21717          
21718         // support parent
21719         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21720         var triggers = this.trigger ? this.trigger.split(' ') : [];
21721         Roo.each(triggers, function(trigger) {
21722         
21723             if (trigger == 'click') {
21724                 on_el.on('click', this.toggle, this);
21725             } else if (trigger != 'manual') {
21726                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21727                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21728       
21729                 on_el.on(eventIn  ,this.enter, this);
21730                 on_el.on(eventOut, this.leave, this);
21731             }
21732         }, this);
21733     },
21734     
21735     
21736     // private
21737     timeout : null,
21738     hoverState : null,
21739     
21740     toggle : function () {
21741         this.hoverState == 'in' ? this.leave() : this.enter();
21742     },
21743     
21744     enter : function () {
21745         
21746         clearTimeout(this.timeout);
21747     
21748         this.hoverState = 'in';
21749     
21750         if (!this.delay || !this.delay.show) {
21751             this.show();
21752             return;
21753         }
21754         var _t = this;
21755         this.timeout = setTimeout(function () {
21756             if (_t.hoverState == 'in') {
21757                 _t.show();
21758             }
21759         }, this.delay.show)
21760     },
21761     
21762     leave : function() {
21763         clearTimeout(this.timeout);
21764     
21765         this.hoverState = 'out';
21766     
21767         if (!this.delay || !this.delay.hide) {
21768             this.hide();
21769             return;
21770         }
21771         var _t = this;
21772         this.timeout = setTimeout(function () {
21773             if (_t.hoverState == 'out') {
21774                 _t.hide();
21775             }
21776         }, this.delay.hide)
21777     },
21778     
21779     /**
21780      * update the position of the dialog
21781      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21782      * 
21783      *
21784      */
21785     
21786     doAlign : function()
21787     {
21788         
21789         if (this.alignEl) {
21790             this.updatePosition(this.placement, true);
21791              
21792         } else {
21793             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21794             var es = this.el.getSize();
21795             var x = Roo.lib.Dom.getViewWidth()/2;
21796             var y = Roo.lib.Dom.getViewHeight()/2;
21797             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21798             
21799         }
21800
21801          
21802          
21803         
21804         
21805     },
21806     
21807     /**
21808      * Show the popover
21809      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21810      * @param {string} (left|right|top|bottom) position
21811      */
21812     show : function (on_el, placement)
21813     {
21814         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21815         on_el = on_el || false; // default to false
21816          
21817         if (!on_el) {
21818             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21819                 on_el = this.parent().el;
21820             } else if (this.over) {
21821                 on_el = Roo.get(this.over);
21822             }
21823             
21824         }
21825         
21826         this.alignEl = Roo.get( on_el );
21827
21828         if (!this.el) {
21829             this.render(document.body);
21830         }
21831         
21832         
21833          
21834         
21835         if (this.title === false) {
21836             this.headerEl.hide();
21837         }
21838         
21839        
21840         this.el.show();
21841         this.el.dom.style.display = 'block';
21842          
21843         this.doAlign();
21844         
21845         //var arrow = this.el.select('.arrow',true).first();
21846         //arrow.set(align[2], 
21847         
21848         this.el.addClass('in');
21849         
21850          
21851         
21852         this.hoverState = 'in';
21853         
21854         if (this.modal) {
21855             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21856             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21857             this.maskEl.dom.style.display = 'block';
21858             this.maskEl.addClass('show');
21859         }
21860         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21861  
21862         this.fireEvent('show', this);
21863         
21864     },
21865     /**
21866      * fire this manually after loading a grid in the table for example
21867      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21868      * @param {Boolean} try and move it if we cant get right position.
21869      */
21870     updatePosition : function(placement, try_move)
21871     {
21872         // allow for calling with no parameters
21873         placement = placement   ? placement :  this.placement;
21874         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21875         
21876         this.el.removeClass([
21877             'fade','top','bottom', 'left', 'right','in',
21878             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21879         ]);
21880         this.el.addClass(placement + ' bs-popover-' + placement);
21881         
21882         if (!this.alignEl ) {
21883             return false;
21884         }
21885         
21886         switch (placement) {
21887             case 'right':
21888                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21889                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21890                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21891                     //normal display... or moved up/down.
21892                     this.el.setXY(offset);
21893                     var xy = this.alignEl.getAnchorXY('tr', false);
21894                     xy[0]+=2;xy[1]+=5;
21895                     this.arrowEl.setXY(xy);
21896                     return true;
21897                 }
21898                 // continue through...
21899                 return this.updatePosition('left', false);
21900                 
21901             
21902             case 'left':
21903                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21904                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21905                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21906                     //normal display... or moved up/down.
21907                     this.el.setXY(offset);
21908                     var xy = this.alignEl.getAnchorXY('tl', false);
21909                     xy[0]-=10;xy[1]+=5; // << fix me
21910                     this.arrowEl.setXY(xy);
21911                     return true;
21912                 }
21913                 // call self...
21914                 return this.updatePosition('right', false);
21915             
21916             case 'top':
21917                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21918                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21919                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21920                     //normal display... or moved up/down.
21921                     this.el.setXY(offset);
21922                     var xy = this.alignEl.getAnchorXY('t', false);
21923                     xy[1]-=10; // << fix me
21924                     this.arrowEl.setXY(xy);
21925                     return true;
21926                 }
21927                 // fall through
21928                return this.updatePosition('bottom', false);
21929             
21930             case 'bottom':
21931                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21932                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21933                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21934                     //normal display... or moved up/down.
21935                     this.el.setXY(offset);
21936                     var xy = this.alignEl.getAnchorXY('b', false);
21937                      xy[1]+=2; // << fix me
21938                     this.arrowEl.setXY(xy);
21939                     return true;
21940                 }
21941                 // fall through
21942                 return this.updatePosition('top', false);
21943                 
21944             
21945         }
21946         
21947         
21948         return false;
21949     },
21950     
21951     hide : function()
21952     {
21953         this.el.setXY([0,0]);
21954         this.el.removeClass('in');
21955         this.el.hide();
21956         this.hoverState = null;
21957         this.maskEl.hide(); // always..
21958         this.fireEvent('hide', this);
21959     }
21960     
21961 });
21962
21963
21964 Roo.apply(Roo.bootstrap.Popover, {
21965
21966     alignment : {
21967         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21968         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21969         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21970         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21971     },
21972     
21973     zIndex : 20001,
21974
21975     clickHander : false,
21976     
21977     
21978
21979     onMouseDown : function(e)
21980     {
21981         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21982             /// what is nothing is showing..
21983             this.hideAll();
21984         }
21985          
21986     },
21987     
21988     
21989     popups : [],
21990     
21991     register : function(popup)
21992     {
21993         if (!Roo.bootstrap.Popover.clickHandler) {
21994             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21995         }
21996         // hide other popups.
21997         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21998         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21999         this.hideAll(); //<< why?
22000         //this.popups.push(popup);
22001     },
22002     hideAll : function()
22003     {
22004         this.popups.forEach(function(p) {
22005             p.hide();
22006         });
22007     },
22008     onShow : function() {
22009         Roo.bootstrap.Popover.popups.push(this);
22010     },
22011     onHide : function() {
22012         Roo.bootstrap.Popover.popups.remove(this);
22013     } 
22014
22015 });
22016 /**
22017  * @class Roo.bootstrap.PopoverNav
22018  * @extends Roo.bootstrap.nav.Simplebar
22019  * @parent Roo.bootstrap.Popover
22020  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22021  * @licence LGPL
22022  * Bootstrap Popover header navigation class
22023  * FIXME? should this go under nav?
22024  *
22025  * 
22026  * @constructor
22027  * Create a new Popover Header Navigation 
22028  * @param {Object} config The config object
22029  */
22030
22031 Roo.bootstrap.PopoverNav = function(config){
22032     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22033 };
22034
22035 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22036     
22037     
22038     container_method : 'getPopoverHeader' 
22039     
22040      
22041     
22042     
22043    
22044 });
22045
22046  
22047
22048  /*
22049  * - LGPL
22050  *
22051  * Progress
22052  * 
22053  */
22054
22055 /**
22056  * @class Roo.bootstrap.Progress
22057  * @extends Roo.bootstrap.Component
22058  * @children Roo.bootstrap.ProgressBar
22059  * Bootstrap Progress class
22060  * @cfg {Boolean} striped striped of the progress bar
22061  * @cfg {Boolean} active animated of the progress bar
22062  * 
22063  * 
22064  * @constructor
22065  * Create a new Progress
22066  * @param {Object} config The config object
22067  */
22068
22069 Roo.bootstrap.Progress = function(config){
22070     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22071 };
22072
22073 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22074     
22075     striped : false,
22076     active: false,
22077     
22078     getAutoCreate : function(){
22079         var cfg = {
22080             tag: 'div',
22081             cls: 'progress'
22082         };
22083         
22084         
22085         if(this.striped){
22086             cfg.cls += ' progress-striped';
22087         }
22088       
22089         if(this.active){
22090             cfg.cls += ' active';
22091         }
22092         
22093         
22094         return cfg;
22095     }
22096    
22097 });
22098
22099  
22100
22101  /*
22102  * - LGPL
22103  *
22104  * ProgressBar
22105  * 
22106  */
22107
22108 /**
22109  * @class Roo.bootstrap.ProgressBar
22110  * @extends Roo.bootstrap.Component
22111  * Bootstrap ProgressBar class
22112  * @cfg {Number} aria_valuenow aria-value now
22113  * @cfg {Number} aria_valuemin aria-value min
22114  * @cfg {Number} aria_valuemax aria-value max
22115  * @cfg {String} label label for the progress bar
22116  * @cfg {String} panel (success | info | warning | danger )
22117  * @cfg {String} role role of the progress bar
22118  * @cfg {String} sr_only text
22119  * 
22120  * 
22121  * @constructor
22122  * Create a new ProgressBar
22123  * @param {Object} config The config object
22124  */
22125
22126 Roo.bootstrap.ProgressBar = function(config){
22127     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22128 };
22129
22130 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22131     
22132     aria_valuenow : 0,
22133     aria_valuemin : 0,
22134     aria_valuemax : 100,
22135     label : false,
22136     panel : false,
22137     role : false,
22138     sr_only: false,
22139     
22140     getAutoCreate : function()
22141     {
22142         
22143         var cfg = {
22144             tag: 'div',
22145             cls: 'progress-bar',
22146             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22147         };
22148         
22149         if(this.sr_only){
22150             cfg.cn = {
22151                 tag: 'span',
22152                 cls: 'sr-only',
22153                 html: this.sr_only
22154             }
22155         }
22156         
22157         if(this.role){
22158             cfg.role = this.role;
22159         }
22160         
22161         if(this.aria_valuenow){
22162             cfg['aria-valuenow'] = this.aria_valuenow;
22163         }
22164         
22165         if(this.aria_valuemin){
22166             cfg['aria-valuemin'] = this.aria_valuemin;
22167         }
22168         
22169         if(this.aria_valuemax){
22170             cfg['aria-valuemax'] = this.aria_valuemax;
22171         }
22172         
22173         if(this.label && !this.sr_only){
22174             cfg.html = this.label;
22175         }
22176         
22177         if(this.panel){
22178             cfg.cls += ' progress-bar-' + this.panel;
22179         }
22180         
22181         return cfg;
22182     },
22183     
22184     update : function(aria_valuenow)
22185     {
22186         this.aria_valuenow = aria_valuenow;
22187         
22188         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22189     }
22190    
22191 });
22192
22193  
22194
22195  /**
22196  * @class Roo.bootstrap.TabGroup
22197  * @extends Roo.bootstrap.Column
22198  * @children Roo.bootstrap.TabPanel
22199  * Bootstrap Column class
22200  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22201  * @cfg {Boolean} carousel true to make the group behave like a carousel
22202  * @cfg {Boolean} bullets show bullets for the panels
22203  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22204  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22205  * @cfg {Boolean} showarrow (true|false) show arrow default true
22206  * 
22207  * @constructor
22208  * Create a new TabGroup
22209  * @param {Object} config The config object
22210  */
22211
22212 Roo.bootstrap.TabGroup = function(config){
22213     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22214     if (!this.navId) {
22215         this.navId = Roo.id();
22216     }
22217     this.tabs = [];
22218     Roo.bootstrap.TabGroup.register(this);
22219     
22220 };
22221
22222 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22223     
22224     carousel : false,
22225     transition : false,
22226     bullets : 0,
22227     timer : 0,
22228     autoslide : false,
22229     slideFn : false,
22230     slideOnTouch : false,
22231     showarrow : true,
22232     
22233     getAutoCreate : function()
22234     {
22235         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22236         
22237         cfg.cls += ' tab-content';
22238         
22239         if (this.carousel) {
22240             cfg.cls += ' carousel slide';
22241             
22242             cfg.cn = [{
22243                cls : 'carousel-inner',
22244                cn : []
22245             }];
22246         
22247             if(this.bullets  && !Roo.isTouch){
22248                 
22249                 var bullets = {
22250                     cls : 'carousel-bullets',
22251                     cn : []
22252                 };
22253                
22254                 if(this.bullets_cls){
22255                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22256                 }
22257                 
22258                 bullets.cn.push({
22259                     cls : 'clear'
22260                 });
22261                 
22262                 cfg.cn[0].cn.push(bullets);
22263             }
22264             
22265             if(this.showarrow){
22266                 cfg.cn[0].cn.push({
22267                     tag : 'div',
22268                     class : 'carousel-arrow',
22269                     cn : [
22270                         {
22271                             tag : 'div',
22272                             class : 'carousel-prev',
22273                             cn : [
22274                                 {
22275                                     tag : 'i',
22276                                     class : 'fa fa-chevron-left'
22277                                 }
22278                             ]
22279                         },
22280                         {
22281                             tag : 'div',
22282                             class : 'carousel-next',
22283                             cn : [
22284                                 {
22285                                     tag : 'i',
22286                                     class : 'fa fa-chevron-right'
22287                                 }
22288                             ]
22289                         }
22290                     ]
22291                 });
22292             }
22293             
22294         }
22295         
22296         return cfg;
22297     },
22298     
22299     initEvents:  function()
22300     {
22301 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22302 //            this.el.on("touchstart", this.onTouchStart, this);
22303 //        }
22304         
22305         if(this.autoslide){
22306             var _this = this;
22307             
22308             this.slideFn = window.setInterval(function() {
22309                 _this.showPanelNext();
22310             }, this.timer);
22311         }
22312         
22313         if(this.showarrow){
22314             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22315             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22316         }
22317         
22318         
22319     },
22320     
22321 //    onTouchStart : function(e, el, o)
22322 //    {
22323 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22324 //            return;
22325 //        }
22326 //        
22327 //        this.showPanelNext();
22328 //    },
22329     
22330     
22331     getChildContainer : function()
22332     {
22333         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22334     },
22335     
22336     /**
22337     * register a Navigation item
22338     * @param {Roo.bootstrap.nav.Item} the navitem to add
22339     */
22340     register : function(item)
22341     {
22342         this.tabs.push( item);
22343         item.navId = this.navId; // not really needed..
22344         this.addBullet();
22345     
22346     },
22347     
22348     getActivePanel : function()
22349     {
22350         var r = false;
22351         Roo.each(this.tabs, function(t) {
22352             if (t.active) {
22353                 r = t;
22354                 return false;
22355             }
22356             return null;
22357         });
22358         return r;
22359         
22360     },
22361     getPanelByName : function(n)
22362     {
22363         var r = false;
22364         Roo.each(this.tabs, function(t) {
22365             if (t.tabId == n) {
22366                 r = t;
22367                 return false;
22368             }
22369             return null;
22370         });
22371         return r;
22372     },
22373     indexOfPanel : function(p)
22374     {
22375         var r = false;
22376         Roo.each(this.tabs, function(t,i) {
22377             if (t.tabId == p.tabId) {
22378                 r = i;
22379                 return false;
22380             }
22381             return null;
22382         });
22383         return r;
22384     },
22385     /**
22386      * show a specific panel
22387      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22388      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22389      */
22390     showPanel : function (pan)
22391     {
22392         if(this.transition || typeof(pan) == 'undefined'){
22393             Roo.log("waiting for the transitionend");
22394             return false;
22395         }
22396         
22397         if (typeof(pan) == 'number') {
22398             pan = this.tabs[pan];
22399         }
22400         
22401         if (typeof(pan) == 'string') {
22402             pan = this.getPanelByName(pan);
22403         }
22404         
22405         var cur = this.getActivePanel();
22406         
22407         if(!pan || !cur){
22408             Roo.log('pan or acitve pan is undefined');
22409             return false;
22410         }
22411         
22412         if (pan.tabId == this.getActivePanel().tabId) {
22413             return true;
22414         }
22415         
22416         if (false === cur.fireEvent('beforedeactivate')) {
22417             return false;
22418         }
22419         
22420         if(this.bullets > 0 && !Roo.isTouch){
22421             this.setActiveBullet(this.indexOfPanel(pan));
22422         }
22423         
22424         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22425             
22426             //class="carousel-item carousel-item-next carousel-item-left"
22427             
22428             this.transition = true;
22429             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22430             var lr = dir == 'next' ? 'left' : 'right';
22431             pan.el.addClass(dir); // or prev
22432             pan.el.addClass('carousel-item-' + dir); // or prev
22433             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22434             cur.el.addClass(lr); // or right
22435             pan.el.addClass(lr);
22436             cur.el.addClass('carousel-item-' +lr); // or right
22437             pan.el.addClass('carousel-item-' +lr);
22438             
22439             
22440             var _this = this;
22441             cur.el.on('transitionend', function() {
22442                 Roo.log("trans end?");
22443                 
22444                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22445                 pan.setActive(true);
22446                 
22447                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22448                 cur.setActive(false);
22449                 
22450                 _this.transition = false;
22451                 
22452             }, this, { single:  true } );
22453             
22454             return true;
22455         }
22456         
22457         cur.setActive(false);
22458         pan.setActive(true);
22459         
22460         return true;
22461         
22462     },
22463     showPanelNext : function()
22464     {
22465         var i = this.indexOfPanel(this.getActivePanel());
22466         
22467         if (i >= this.tabs.length - 1 && !this.autoslide) {
22468             return;
22469         }
22470         
22471         if (i >= this.tabs.length - 1 && this.autoslide) {
22472             i = -1;
22473         }
22474         
22475         this.showPanel(this.tabs[i+1]);
22476     },
22477     
22478     showPanelPrev : function()
22479     {
22480         var i = this.indexOfPanel(this.getActivePanel());
22481         
22482         if (i  < 1 && !this.autoslide) {
22483             return;
22484         }
22485         
22486         if (i < 1 && this.autoslide) {
22487             i = this.tabs.length;
22488         }
22489         
22490         this.showPanel(this.tabs[i-1]);
22491     },
22492     
22493     
22494     addBullet: function()
22495     {
22496         if(!this.bullets || Roo.isTouch){
22497             return;
22498         }
22499         var ctr = this.el.select('.carousel-bullets',true).first();
22500         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22501         var bullet = ctr.createChild({
22502             cls : 'bullet bullet-' + i
22503         },ctr.dom.lastChild);
22504         
22505         
22506         var _this = this;
22507         
22508         bullet.on('click', (function(e, el, o, ii, t){
22509
22510             e.preventDefault();
22511
22512             this.showPanel(ii);
22513
22514             if(this.autoslide && this.slideFn){
22515                 clearInterval(this.slideFn);
22516                 this.slideFn = window.setInterval(function() {
22517                     _this.showPanelNext();
22518                 }, this.timer);
22519             }
22520
22521         }).createDelegate(this, [i, bullet], true));
22522                 
22523         
22524     },
22525      
22526     setActiveBullet : function(i)
22527     {
22528         if(Roo.isTouch){
22529             return;
22530         }
22531         
22532         Roo.each(this.el.select('.bullet', true).elements, function(el){
22533             el.removeClass('selected');
22534         });
22535
22536         var bullet = this.el.select('.bullet-' + i, true).first();
22537         
22538         if(!bullet){
22539             return;
22540         }
22541         
22542         bullet.addClass('selected');
22543     }
22544     
22545     
22546   
22547 });
22548
22549  
22550
22551  
22552  
22553 Roo.apply(Roo.bootstrap.TabGroup, {
22554     
22555     groups: {},
22556      /**
22557     * register a Navigation Group
22558     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22559     */
22560     register : function(navgrp)
22561     {
22562         this.groups[navgrp.navId] = navgrp;
22563         
22564     },
22565     /**
22566     * fetch a Navigation Group based on the navigation ID
22567     * if one does not exist , it will get created.
22568     * @param {string} the navgroup to add
22569     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22570     */
22571     get: function(navId) {
22572         if (typeof(this.groups[navId]) == 'undefined') {
22573             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22574         }
22575         return this.groups[navId] ;
22576     }
22577     
22578     
22579     
22580 });
22581
22582  /*
22583  * - LGPL
22584  *
22585  * TabPanel
22586  * 
22587  */
22588
22589 /**
22590  * @class Roo.bootstrap.TabPanel
22591  * @extends Roo.bootstrap.Component
22592  * @children Roo.bootstrap.Component
22593  * Bootstrap TabPanel class
22594  * @cfg {Boolean} active panel active
22595  * @cfg {String} html panel content
22596  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22597  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22598  * @cfg {String} href click to link..
22599  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22600  * 
22601  * 
22602  * @constructor
22603  * Create a new TabPanel
22604  * @param {Object} config The config object
22605  */
22606
22607 Roo.bootstrap.TabPanel = function(config){
22608     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22609     this.addEvents({
22610         /**
22611              * @event changed
22612              * Fires when the active status changes
22613              * @param {Roo.bootstrap.TabPanel} this
22614              * @param {Boolean} state the new state
22615             
22616          */
22617         'changed': true,
22618         /**
22619              * @event beforedeactivate
22620              * Fires before a tab is de-activated - can be used to do validation on a form.
22621              * @param {Roo.bootstrap.TabPanel} this
22622              * @return {Boolean} false if there is an error
22623             
22624          */
22625         'beforedeactivate': true
22626      });
22627     
22628     this.tabId = this.tabId || Roo.id();
22629   
22630 };
22631
22632 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22633     
22634     active: false,
22635     html: false,
22636     tabId: false,
22637     navId : false,
22638     href : '',
22639     touchSlide : false,
22640     getAutoCreate : function(){
22641         
22642         
22643         var cfg = {
22644             tag: 'div',
22645             // item is needed for carousel - not sure if it has any effect otherwise
22646             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22647             html: this.html || ''
22648         };
22649         
22650         if(this.active){
22651             cfg.cls += ' active';
22652         }
22653         
22654         if(this.tabId){
22655             cfg.tabId = this.tabId;
22656         }
22657         
22658         
22659         
22660         return cfg;
22661     },
22662     
22663     initEvents:  function()
22664     {
22665         var p = this.parent();
22666         
22667         this.navId = this.navId || p.navId;
22668         
22669         if (typeof(this.navId) != 'undefined') {
22670             // not really needed.. but just in case.. parent should be a NavGroup.
22671             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22672             
22673             tg.register(this);
22674             
22675             var i = tg.tabs.length - 1;
22676             
22677             if(this.active && tg.bullets > 0 && i < tg.bullets){
22678                 tg.setActiveBullet(i);
22679             }
22680         }
22681         
22682         this.el.on('click', this.onClick, this);
22683         
22684         if(Roo.isTouch && this.touchSlide){
22685             this.el.on("touchstart", this.onTouchStart, this);
22686             this.el.on("touchmove", this.onTouchMove, this);
22687             this.el.on("touchend", this.onTouchEnd, this);
22688         }
22689         
22690     },
22691     
22692     onRender : function(ct, position)
22693     {
22694         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22695     },
22696     
22697     setActive : function(state)
22698     {
22699         Roo.log("panel - set active " + this.tabId + "=" + state);
22700         
22701         this.active = state;
22702         if (!state) {
22703             this.el.removeClass('active');
22704             
22705         } else  if (!this.el.hasClass('active')) {
22706             this.el.addClass('active');
22707         }
22708         
22709         this.fireEvent('changed', this, state);
22710     },
22711     
22712     onClick : function(e)
22713     {
22714         e.preventDefault();
22715         
22716         if(!this.href.length){
22717             return;
22718         }
22719         
22720         window.location.href = this.href;
22721     },
22722     
22723     startX : 0,
22724     startY : 0,
22725     endX : 0,
22726     endY : 0,
22727     swiping : false,
22728     
22729     onTouchStart : function(e)
22730     {
22731         this.swiping = false;
22732         
22733         this.startX = e.browserEvent.touches[0].clientX;
22734         this.startY = e.browserEvent.touches[0].clientY;
22735     },
22736     
22737     onTouchMove : function(e)
22738     {
22739         this.swiping = true;
22740         
22741         this.endX = e.browserEvent.touches[0].clientX;
22742         this.endY = e.browserEvent.touches[0].clientY;
22743     },
22744     
22745     onTouchEnd : function(e)
22746     {
22747         if(!this.swiping){
22748             this.onClick(e);
22749             return;
22750         }
22751         
22752         var tabGroup = this.parent();
22753         
22754         if(this.endX > this.startX){ // swiping right
22755             tabGroup.showPanelPrev();
22756             return;
22757         }
22758         
22759         if(this.startX > this.endX){ // swiping left
22760             tabGroup.showPanelNext();
22761             return;
22762         }
22763     }
22764     
22765     
22766 });
22767  
22768
22769  
22770
22771  /*
22772  * - LGPL
22773  *
22774  * DateField
22775  * 
22776  */
22777
22778 /**
22779  * @class Roo.bootstrap.form.DateField
22780  * @extends Roo.bootstrap.form.Input
22781  * Bootstrap DateField class
22782  * @cfg {Number} weekStart default 0
22783  * @cfg {String} viewMode default empty, (months|years)
22784  * @cfg {String} minViewMode default empty, (months|years)
22785  * @cfg {Number} startDate default -Infinity
22786  * @cfg {Number} endDate default Infinity
22787  * @cfg {Boolean} todayHighlight default false
22788  * @cfg {Boolean} todayBtn default false
22789  * @cfg {Boolean} calendarWeeks default false
22790  * @cfg {Object} daysOfWeekDisabled default empty
22791  * @cfg {Boolean} singleMode default false (true | false)
22792  * 
22793  * @cfg {Boolean} keyboardNavigation default true
22794  * @cfg {String} language default en
22795  * 
22796  * @constructor
22797  * Create a new DateField
22798  * @param {Object} config The config object
22799  */
22800
22801 Roo.bootstrap.form.DateField = function(config){
22802     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22803      this.addEvents({
22804             /**
22805              * @event show
22806              * Fires when this field show.
22807              * @param {Roo.bootstrap.form.DateField} this
22808              * @param {Mixed} date The date value
22809              */
22810             show : true,
22811             /**
22812              * @event show
22813              * Fires when this field hide.
22814              * @param {Roo.bootstrap.form.DateField} this
22815              * @param {Mixed} date The date value
22816              */
22817             hide : true,
22818             /**
22819              * @event select
22820              * Fires when select a date.
22821              * @param {Roo.bootstrap.form.DateField} this
22822              * @param {Mixed} date The date value
22823              */
22824             select : true,
22825             /**
22826              * @event beforeselect
22827              * Fires when before select a date.
22828              * @param {Roo.bootstrap.form.DateField} this
22829              * @param {Mixed} date The date value
22830              */
22831             beforeselect : true
22832         });
22833 };
22834
22835 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22836     
22837     /**
22838      * @cfg {String} format
22839      * The default date format string which can be overriden for localization support.  The format must be
22840      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22841      */
22842     format : "m/d/y",
22843     /**
22844      * @cfg {String} altFormats
22845      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22846      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22847      */
22848     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22849     
22850     weekStart : 0,
22851     
22852     viewMode : '',
22853     
22854     minViewMode : '',
22855     
22856     todayHighlight : false,
22857     
22858     todayBtn: false,
22859     
22860     language: 'en',
22861     
22862     keyboardNavigation: true,
22863     
22864     calendarWeeks: false,
22865     
22866     startDate: -Infinity,
22867     
22868     endDate: Infinity,
22869     
22870     daysOfWeekDisabled: [],
22871     
22872     _events: [],
22873     
22874     singleMode : false,
22875     
22876     UTCDate: function()
22877     {
22878         return new Date(Date.UTC.apply(Date, arguments));
22879     },
22880     
22881     UTCToday: function()
22882     {
22883         var today = new Date();
22884         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22885     },
22886     
22887     getDate: function() {
22888             var d = this.getUTCDate();
22889             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22890     },
22891     
22892     getUTCDate: function() {
22893             return this.date;
22894     },
22895     
22896     setDate: function(d) {
22897             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22898     },
22899     
22900     setUTCDate: function(d) {
22901             this.date = d;
22902             this.setValue(this.formatDate(this.date));
22903     },
22904         
22905     onRender: function(ct, position)
22906     {
22907         
22908         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22909         
22910         this.language = this.language || 'en';
22911         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22912         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22913         
22914         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22915         this.format = this.format || 'm/d/y';
22916         this.isInline = false;
22917         this.isInput = true;
22918         this.component = this.el.select('.add-on', true).first() || false;
22919         this.component = (this.component && this.component.length === 0) ? false : this.component;
22920         this.hasInput = this.component && this.inputEl().length;
22921         
22922         if (typeof(this.minViewMode === 'string')) {
22923             switch (this.minViewMode) {
22924                 case 'months':
22925                     this.minViewMode = 1;
22926                     break;
22927                 case 'years':
22928                     this.minViewMode = 2;
22929                     break;
22930                 default:
22931                     this.minViewMode = 0;
22932                     break;
22933             }
22934         }
22935         
22936         if (typeof(this.viewMode === 'string')) {
22937             switch (this.viewMode) {
22938                 case 'months':
22939                     this.viewMode = 1;
22940                     break;
22941                 case 'years':
22942                     this.viewMode = 2;
22943                     break;
22944                 default:
22945                     this.viewMode = 0;
22946                     break;
22947             }
22948         }
22949                 
22950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22951         
22952 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22953         
22954         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22955         
22956         this.picker().on('mousedown', this.onMousedown, this);
22957         this.picker().on('click', this.onClick, this);
22958         
22959         this.picker().addClass('datepicker-dropdown');
22960         
22961         this.startViewMode = this.viewMode;
22962         
22963         if(this.singleMode){
22964             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22965                 v.setVisibilityMode(Roo.Element.DISPLAY);
22966                 v.hide();
22967             });
22968             
22969             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22970                 v.setStyle('width', '189px');
22971             });
22972         }
22973         
22974         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22975             if(!this.calendarWeeks){
22976                 v.remove();
22977                 return;
22978             }
22979             
22980             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22981             v.attr('colspan', function(i, val){
22982                 return parseInt(val) + 1;
22983             });
22984         });
22985                         
22986         
22987         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22988         
22989         this.setStartDate(this.startDate);
22990         this.setEndDate(this.endDate);
22991         
22992         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22993         
22994         this.fillDow();
22995         this.fillMonths();
22996         this.update();
22997         this.showMode();
22998         
22999         if(this.isInline) {
23000             this.showPopup();
23001         }
23002     },
23003     
23004     picker : function()
23005     {
23006         return this.pickerEl;
23007 //        return this.el.select('.datepicker', true).first();
23008     },
23009     
23010     fillDow: function()
23011     {
23012         var dowCnt = this.weekStart;
23013         
23014         var dow = {
23015             tag: 'tr',
23016             cn: [
23017                 
23018             ]
23019         };
23020         
23021         if(this.calendarWeeks){
23022             dow.cn.push({
23023                 tag: 'th',
23024                 cls: 'cw',
23025                 html: '&nbsp;'
23026             })
23027         }
23028         
23029         while (dowCnt < this.weekStart + 7) {
23030             dow.cn.push({
23031                 tag: 'th',
23032                 cls: 'dow',
23033                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23034             });
23035         }
23036         
23037         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23038     },
23039     
23040     fillMonths: function()
23041     {    
23042         var i = 0;
23043         var months = this.picker().select('>.datepicker-months td', true).first();
23044         
23045         months.dom.innerHTML = '';
23046         
23047         while (i < 12) {
23048             var month = {
23049                 tag: 'span',
23050                 cls: 'month',
23051                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23052             };
23053             
23054             months.createChild(month);
23055         }
23056         
23057     },
23058     
23059     update: function()
23060     {
23061         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;
23062         
23063         if (this.date < this.startDate) {
23064             this.viewDate = new Date(this.startDate);
23065         } else if (this.date > this.endDate) {
23066             this.viewDate = new Date(this.endDate);
23067         } else {
23068             this.viewDate = new Date(this.date);
23069         }
23070         
23071         this.fill();
23072     },
23073     
23074     fill: function() 
23075     {
23076         var d = new Date(this.viewDate),
23077                 year = d.getUTCFullYear(),
23078                 month = d.getUTCMonth(),
23079                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23080                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23081                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23082                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23083                 currentDate = this.date && this.date.valueOf(),
23084                 today = this.UTCToday();
23085         
23086         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23087         
23088 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23089         
23090 //        this.picker.select('>tfoot th.today').
23091 //                                              .text(dates[this.language].today)
23092 //                                              .toggle(this.todayBtn !== false);
23093     
23094         this.updateNavArrows();
23095         this.fillMonths();
23096                                                 
23097         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23098         
23099         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23100          
23101         prevMonth.setUTCDate(day);
23102         
23103         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23104         
23105         var nextMonth = new Date(prevMonth);
23106         
23107         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23108         
23109         nextMonth = nextMonth.valueOf();
23110         
23111         var fillMonths = false;
23112         
23113         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23114         
23115         while(prevMonth.valueOf() <= nextMonth) {
23116             var clsName = '';
23117             
23118             if (prevMonth.getUTCDay() === this.weekStart) {
23119                 if(fillMonths){
23120                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23121                 }
23122                     
23123                 fillMonths = {
23124                     tag: 'tr',
23125                     cn: []
23126                 };
23127                 
23128                 if(this.calendarWeeks){
23129                     // ISO 8601: First week contains first thursday.
23130                     // ISO also states week starts on Monday, but we can be more abstract here.
23131                     var
23132                     // Start of current week: based on weekstart/current date
23133                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23134                     // Thursday of this week
23135                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23136                     // First Thursday of year, year from thursday
23137                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23138                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23139                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23140                     
23141                     fillMonths.cn.push({
23142                         tag: 'td',
23143                         cls: 'cw',
23144                         html: calWeek
23145                     });
23146                 }
23147             }
23148             
23149             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23150                 clsName += ' old';
23151             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23152                 clsName += ' new';
23153             }
23154             if (this.todayHighlight &&
23155                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23156                 prevMonth.getUTCMonth() == today.getMonth() &&
23157                 prevMonth.getUTCDate() == today.getDate()) {
23158                 clsName += ' today';
23159             }
23160             
23161             if (currentDate && prevMonth.valueOf() === currentDate) {
23162                 clsName += ' active';
23163             }
23164             
23165             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23166                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23167                     clsName += ' disabled';
23168             }
23169             
23170             fillMonths.cn.push({
23171                 tag: 'td',
23172                 cls: 'day ' + clsName,
23173                 html: prevMonth.getDate()
23174             });
23175             
23176             prevMonth.setDate(prevMonth.getDate()+1);
23177         }
23178           
23179         var currentYear = this.date && this.date.getUTCFullYear();
23180         var currentMonth = this.date && this.date.getUTCMonth();
23181         
23182         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23183         
23184         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23185             v.removeClass('active');
23186             
23187             if(currentYear === year && k === currentMonth){
23188                 v.addClass('active');
23189             }
23190             
23191             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23192                 v.addClass('disabled');
23193             }
23194             
23195         });
23196         
23197         
23198         year = parseInt(year/10, 10) * 10;
23199         
23200         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23201         
23202         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23203         
23204         year -= 1;
23205         for (var i = -1; i < 11; i++) {
23206             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23207                 tag: 'span',
23208                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23209                 html: year
23210             });
23211             
23212             year += 1;
23213         }
23214     },
23215     
23216     showMode: function(dir) 
23217     {
23218         if (dir) {
23219             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23220         }
23221         
23222         Roo.each(this.picker().select('>div',true).elements, function(v){
23223             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23224             v.hide();
23225         });
23226         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23227     },
23228     
23229     place: function()
23230     {
23231         if(this.isInline) {
23232             return;
23233         }
23234         
23235         this.picker().removeClass(['bottom', 'top']);
23236         
23237         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23238             /*
23239              * place to the top of element!
23240              *
23241              */
23242             
23243             this.picker().addClass('top');
23244             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23245             
23246             return;
23247         }
23248         
23249         this.picker().addClass('bottom');
23250         
23251         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23252     },
23253     
23254     parseDate : function(value)
23255     {
23256         if(!value || value instanceof Date){
23257             return value;
23258         }
23259         var v = Date.parseDate(value, this.format);
23260         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23261             v = Date.parseDate(value, 'Y-m-d');
23262         }
23263         if(!v && this.altFormats){
23264             if(!this.altFormatsArray){
23265                 this.altFormatsArray = this.altFormats.split("|");
23266             }
23267             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23268                 v = Date.parseDate(value, this.altFormatsArray[i]);
23269             }
23270         }
23271         return v;
23272     },
23273     
23274     formatDate : function(date, fmt)
23275     {   
23276         return (!date || !(date instanceof Date)) ?
23277         date : date.dateFormat(fmt || this.format);
23278     },
23279     
23280     onFocus : function()
23281     {
23282         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23283         this.showPopup();
23284     },
23285     
23286     onBlur : function()
23287     {
23288         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23289         
23290         var d = this.inputEl().getValue();
23291         
23292         this.setValue(d);
23293                 
23294         this.hidePopup();
23295     },
23296     
23297     showPopup : function()
23298     {
23299         this.picker().show();
23300         this.update();
23301         this.place();
23302         
23303         this.fireEvent('showpopup', this, this.date);
23304     },
23305     
23306     hidePopup : function()
23307     {
23308         if(this.isInline) {
23309             return;
23310         }
23311         this.picker().hide();
23312         this.viewMode = this.startViewMode;
23313         this.showMode();
23314         
23315         this.fireEvent('hidepopup', this, this.date);
23316         
23317     },
23318     
23319     onMousedown: function(e)
23320     {
23321         e.stopPropagation();
23322         e.preventDefault();
23323     },
23324     
23325     keyup: function(e)
23326     {
23327         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23328         this.update();
23329     },
23330
23331     setValue: function(v)
23332     {
23333         if(this.fireEvent('beforeselect', this, v) !== false){
23334             var d = new Date(this.parseDate(v) ).clearTime();
23335         
23336             if(isNaN(d.getTime())){
23337                 this.date = this.viewDate = '';
23338                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23339                 return;
23340             }
23341
23342             v = this.formatDate(d);
23343
23344             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23345
23346             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23347
23348             this.update();
23349
23350             this.fireEvent('select', this, this.date);
23351         }
23352     },
23353     
23354     getValue: function()
23355     {
23356         return this.formatDate(this.date);
23357     },
23358     
23359     fireKey: function(e)
23360     {
23361         if (!this.picker().isVisible()){
23362             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23363                 this.showPopup();
23364             }
23365             return;
23366         }
23367         
23368         var dateChanged = false,
23369         dir, day, month,
23370         newDate, newViewDate;
23371         
23372         switch(e.keyCode){
23373             case 27: // escape
23374                 this.hidePopup();
23375                 e.preventDefault();
23376                 break;
23377             case 37: // left
23378             case 39: // right
23379                 if (!this.keyboardNavigation) {
23380                     break;
23381                 }
23382                 dir = e.keyCode == 37 ? -1 : 1;
23383                 
23384                 if (e.ctrlKey){
23385                     newDate = this.moveYear(this.date, dir);
23386                     newViewDate = this.moveYear(this.viewDate, dir);
23387                 } else if (e.shiftKey){
23388                     newDate = this.moveMonth(this.date, dir);
23389                     newViewDate = this.moveMonth(this.viewDate, dir);
23390                 } else {
23391                     newDate = new Date(this.date);
23392                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23393                     newViewDate = new Date(this.viewDate);
23394                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23395                 }
23396                 if (this.dateWithinRange(newDate)){
23397                     this.date = newDate;
23398                     this.viewDate = newViewDate;
23399                     this.setValue(this.formatDate(this.date));
23400 //                    this.update();
23401                     e.preventDefault();
23402                     dateChanged = true;
23403                 }
23404                 break;
23405             case 38: // up
23406             case 40: // down
23407                 if (!this.keyboardNavigation) {
23408                     break;
23409                 }
23410                 dir = e.keyCode == 38 ? -1 : 1;
23411                 if (e.ctrlKey){
23412                     newDate = this.moveYear(this.date, dir);
23413                     newViewDate = this.moveYear(this.viewDate, dir);
23414                 } else if (e.shiftKey){
23415                     newDate = this.moveMonth(this.date, dir);
23416                     newViewDate = this.moveMonth(this.viewDate, dir);
23417                 } else {
23418                     newDate = new Date(this.date);
23419                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23420                     newViewDate = new Date(this.viewDate);
23421                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23422                 }
23423                 if (this.dateWithinRange(newDate)){
23424                     this.date = newDate;
23425                     this.viewDate = newViewDate;
23426                     this.setValue(this.formatDate(this.date));
23427 //                    this.update();
23428                     e.preventDefault();
23429                     dateChanged = true;
23430                 }
23431                 break;
23432             case 13: // enter
23433                 this.setValue(this.formatDate(this.date));
23434                 this.hidePopup();
23435                 e.preventDefault();
23436                 break;
23437             case 9: // tab
23438                 this.setValue(this.formatDate(this.date));
23439                 this.hidePopup();
23440                 break;
23441             case 16: // shift
23442             case 17: // ctrl
23443             case 18: // alt
23444                 break;
23445             default :
23446                 this.hidePopup();
23447                 
23448         }
23449     },
23450     
23451     
23452     onClick: function(e) 
23453     {
23454         e.stopPropagation();
23455         e.preventDefault();
23456         
23457         var target = e.getTarget();
23458         
23459         if(target.nodeName.toLowerCase() === 'i'){
23460             target = Roo.get(target).dom.parentNode;
23461         }
23462         
23463         var nodeName = target.nodeName;
23464         var className = target.className;
23465         var html = target.innerHTML;
23466         //Roo.log(nodeName);
23467         
23468         switch(nodeName.toLowerCase()) {
23469             case 'th':
23470                 switch(className) {
23471                     case 'switch':
23472                         this.showMode(1);
23473                         break;
23474                     case 'prev':
23475                     case 'next':
23476                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23477                         switch(this.viewMode){
23478                                 case 0:
23479                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23480                                         break;
23481                                 case 1:
23482                                 case 2:
23483                                         this.viewDate = this.moveYear(this.viewDate, dir);
23484                                         break;
23485                         }
23486                         this.fill();
23487                         break;
23488                     case 'today':
23489                         var date = new Date();
23490                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23491 //                        this.fill()
23492                         this.setValue(this.formatDate(this.date));
23493                         
23494                         this.hidePopup();
23495                         break;
23496                 }
23497                 break;
23498             case 'span':
23499                 if (className.indexOf('disabled') < 0) {
23500                 if (!this.viewDate) {
23501                     this.viewDate = new Date();
23502                 }
23503                 this.viewDate.setUTCDate(1);
23504                     if (className.indexOf('month') > -1) {
23505                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23506                     } else {
23507                         var year = parseInt(html, 10) || 0;
23508                         this.viewDate.setUTCFullYear(year);
23509                         
23510                     }
23511                     
23512                     if(this.singleMode){
23513                         this.setValue(this.formatDate(this.viewDate));
23514                         this.hidePopup();
23515                         return;
23516                     }
23517                     
23518                     this.showMode(-1);
23519                     this.fill();
23520                 }
23521                 break;
23522                 
23523             case 'td':
23524                 //Roo.log(className);
23525                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23526                     var day = parseInt(html, 10) || 1;
23527                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23528                         month = (this.viewDate || new Date()).getUTCMonth();
23529
23530                     if (className.indexOf('old') > -1) {
23531                         if(month === 0 ){
23532                             month = 11;
23533                             year -= 1;
23534                         }else{
23535                             month -= 1;
23536                         }
23537                     } else if (className.indexOf('new') > -1) {
23538                         if (month == 11) {
23539                             month = 0;
23540                             year += 1;
23541                         } else {
23542                             month += 1;
23543                         }
23544                     }
23545                     //Roo.log([year,month,day]);
23546                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23547                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23548 //                    this.fill();
23549                     //Roo.log(this.formatDate(this.date));
23550                     this.setValue(this.formatDate(this.date));
23551                     this.hidePopup();
23552                 }
23553                 break;
23554         }
23555     },
23556     
23557     setStartDate: function(startDate)
23558     {
23559         this.startDate = startDate || -Infinity;
23560         if (this.startDate !== -Infinity) {
23561             this.startDate = this.parseDate(this.startDate);
23562         }
23563         this.update();
23564         this.updateNavArrows();
23565     },
23566
23567     setEndDate: function(endDate)
23568     {
23569         this.endDate = endDate || Infinity;
23570         if (this.endDate !== Infinity) {
23571             this.endDate = this.parseDate(this.endDate);
23572         }
23573         this.update();
23574         this.updateNavArrows();
23575     },
23576     
23577     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23578     {
23579         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23580         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23581             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23582         }
23583         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23584             return parseInt(d, 10);
23585         });
23586         this.update();
23587         this.updateNavArrows();
23588     },
23589     
23590     updateNavArrows: function() 
23591     {
23592         if(this.singleMode){
23593             return;
23594         }
23595         
23596         var d = new Date(this.viewDate),
23597         year = d.getUTCFullYear(),
23598         month = d.getUTCMonth();
23599         
23600         Roo.each(this.picker().select('.prev', true).elements, function(v){
23601             v.show();
23602             switch (this.viewMode) {
23603                 case 0:
23604
23605                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23606                         v.hide();
23607                     }
23608                     break;
23609                 case 1:
23610                 case 2:
23611                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23612                         v.hide();
23613                     }
23614                     break;
23615             }
23616         });
23617         
23618         Roo.each(this.picker().select('.next', true).elements, function(v){
23619             v.show();
23620             switch (this.viewMode) {
23621                 case 0:
23622
23623                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23624                         v.hide();
23625                     }
23626                     break;
23627                 case 1:
23628                 case 2:
23629                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23630                         v.hide();
23631                     }
23632                     break;
23633             }
23634         })
23635     },
23636     
23637     moveMonth: function(date, dir)
23638     {
23639         if (!dir) {
23640             return date;
23641         }
23642         var new_date = new Date(date.valueOf()),
23643         day = new_date.getUTCDate(),
23644         month = new_date.getUTCMonth(),
23645         mag = Math.abs(dir),
23646         new_month, test;
23647         dir = dir > 0 ? 1 : -1;
23648         if (mag == 1){
23649             test = dir == -1
23650             // If going back one month, make sure month is not current month
23651             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23652             ? function(){
23653                 return new_date.getUTCMonth() == month;
23654             }
23655             // If going forward one month, make sure month is as expected
23656             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23657             : function(){
23658                 return new_date.getUTCMonth() != new_month;
23659             };
23660             new_month = month + dir;
23661             new_date.setUTCMonth(new_month);
23662             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23663             if (new_month < 0 || new_month > 11) {
23664                 new_month = (new_month + 12) % 12;
23665             }
23666         } else {
23667             // For magnitudes >1, move one month at a time...
23668             for (var i=0; i<mag; i++) {
23669                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23670                 new_date = this.moveMonth(new_date, dir);
23671             }
23672             // ...then reset the day, keeping it in the new month
23673             new_month = new_date.getUTCMonth();
23674             new_date.setUTCDate(day);
23675             test = function(){
23676                 return new_month != new_date.getUTCMonth();
23677             };
23678         }
23679         // Common date-resetting loop -- if date is beyond end of month, make it
23680         // end of month
23681         while (test()){
23682             new_date.setUTCDate(--day);
23683             new_date.setUTCMonth(new_month);
23684         }
23685         return new_date;
23686     },
23687
23688     moveYear: function(date, dir)
23689     {
23690         return this.moveMonth(date, dir*12);
23691     },
23692
23693     dateWithinRange: function(date)
23694     {
23695         return date >= this.startDate && date <= this.endDate;
23696     },
23697
23698     
23699     remove: function() 
23700     {
23701         this.picker().remove();
23702     },
23703     
23704     validateValue : function(value)
23705     {
23706         if(this.getVisibilityEl().hasClass('hidden')){
23707             return true;
23708         }
23709         
23710         if(value.length < 1)  {
23711             if(this.allowBlank){
23712                 return true;
23713             }
23714             return false;
23715         }
23716         
23717         if(value.length < this.minLength){
23718             return false;
23719         }
23720         if(value.length > this.maxLength){
23721             return false;
23722         }
23723         if(this.vtype){
23724             var vt = Roo.form.VTypes;
23725             if(!vt[this.vtype](value, this)){
23726                 return false;
23727             }
23728         }
23729         if(typeof this.validator == "function"){
23730             var msg = this.validator(value);
23731             if(msg !== true){
23732                 return false;
23733             }
23734         }
23735         
23736         if(this.regex && !this.regex.test(value)){
23737             return false;
23738         }
23739         
23740         if(typeof(this.parseDate(value)) == 'undefined'){
23741             return false;
23742         }
23743         
23744         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23745             return false;
23746         }      
23747         
23748         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23749             return false;
23750         } 
23751         
23752         
23753         return true;
23754     },
23755     
23756     reset : function()
23757     {
23758         this.date = this.viewDate = '';
23759         
23760         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23761     }
23762    
23763 });
23764
23765 Roo.apply(Roo.bootstrap.form.DateField,  {
23766     
23767     head : {
23768         tag: 'thead',
23769         cn: [
23770         {
23771             tag: 'tr',
23772             cn: [
23773             {
23774                 tag: 'th',
23775                 cls: 'prev',
23776                 html: '<i class="fa fa-arrow-left"/>'
23777             },
23778             {
23779                 tag: 'th',
23780                 cls: 'switch',
23781                 colspan: '5'
23782             },
23783             {
23784                 tag: 'th',
23785                 cls: 'next',
23786                 html: '<i class="fa fa-arrow-right"/>'
23787             }
23788
23789             ]
23790         }
23791         ]
23792     },
23793     
23794     content : {
23795         tag: 'tbody',
23796         cn: [
23797         {
23798             tag: 'tr',
23799             cn: [
23800             {
23801                 tag: 'td',
23802                 colspan: '7'
23803             }
23804             ]
23805         }
23806         ]
23807     },
23808     
23809     footer : {
23810         tag: 'tfoot',
23811         cn: [
23812         {
23813             tag: 'tr',
23814             cn: [
23815             {
23816                 tag: 'th',
23817                 colspan: '7',
23818                 cls: 'today'
23819             }
23820                     
23821             ]
23822         }
23823         ]
23824     },
23825     
23826     dates:{
23827         en: {
23828             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23829             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23830             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23831             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23832             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23833             today: "Today"
23834         }
23835     },
23836     
23837     modes: [
23838     {
23839         clsName: 'days',
23840         navFnc: 'Month',
23841         navStep: 1
23842     },
23843     {
23844         clsName: 'months',
23845         navFnc: 'FullYear',
23846         navStep: 1
23847     },
23848     {
23849         clsName: 'years',
23850         navFnc: 'FullYear',
23851         navStep: 10
23852     }]
23853 });
23854
23855 Roo.apply(Roo.bootstrap.form.DateField,  {
23856   
23857     template : {
23858         tag: 'div',
23859         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23860         cn: [
23861         {
23862             tag: 'div',
23863             cls: 'datepicker-days',
23864             cn: [
23865             {
23866                 tag: 'table',
23867                 cls: 'table-condensed',
23868                 cn:[
23869                 Roo.bootstrap.form.DateField.head,
23870                 {
23871                     tag: 'tbody'
23872                 },
23873                 Roo.bootstrap.form.DateField.footer
23874                 ]
23875             }
23876             ]
23877         },
23878         {
23879             tag: 'div',
23880             cls: 'datepicker-months',
23881             cn: [
23882             {
23883                 tag: 'table',
23884                 cls: 'table-condensed',
23885                 cn:[
23886                 Roo.bootstrap.form.DateField.head,
23887                 Roo.bootstrap.form.DateField.content,
23888                 Roo.bootstrap.form.DateField.footer
23889                 ]
23890             }
23891             ]
23892         },
23893         {
23894             tag: 'div',
23895             cls: 'datepicker-years',
23896             cn: [
23897             {
23898                 tag: 'table',
23899                 cls: 'table-condensed',
23900                 cn:[
23901                 Roo.bootstrap.form.DateField.head,
23902                 Roo.bootstrap.form.DateField.content,
23903                 Roo.bootstrap.form.DateField.footer
23904                 ]
23905             }
23906             ]
23907         }
23908         ]
23909     }
23910 });
23911
23912  
23913
23914  /*
23915  * - LGPL
23916  *
23917  * TimeField
23918  * 
23919  */
23920
23921 /**
23922  * @class Roo.bootstrap.form.TimeField
23923  * @extends Roo.bootstrap.form.Input
23924  * Bootstrap DateField class
23925  * 
23926  * 
23927  * @constructor
23928  * Create a new TimeField
23929  * @param {Object} config The config object
23930  */
23931
23932 Roo.bootstrap.form.TimeField = function(config){
23933     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23934     this.addEvents({
23935             /**
23936              * @event show
23937              * Fires when this field show.
23938              * @param {Roo.bootstrap.form.DateField} thisthis
23939              * @param {Mixed} date The date value
23940              */
23941             show : true,
23942             /**
23943              * @event show
23944              * Fires when this field hide.
23945              * @param {Roo.bootstrap.form.DateField} this
23946              * @param {Mixed} date The date value
23947              */
23948             hide : true,
23949             /**
23950              * @event select
23951              * Fires when select a date.
23952              * @param {Roo.bootstrap.form.DateField} this
23953              * @param {Mixed} date The date value
23954              */
23955             select : true
23956         });
23957 };
23958
23959 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23960     
23961     /**
23962      * @cfg {String} format
23963      * The default time format string which can be overriden for localization support.  The format must be
23964      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23965      */
23966     format : "H:i",
23967
23968     getAutoCreate : function()
23969     {
23970         this.after = '<i class="fa far fa-clock"></i>';
23971         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23972         
23973          
23974     },
23975     onRender: function(ct, position)
23976     {
23977         
23978         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23979                 
23980         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23981         
23982         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23983         
23984         this.pop = this.picker().select('>.datepicker-time',true).first();
23985         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23986         
23987         this.picker().on('mousedown', this.onMousedown, this);
23988         this.picker().on('click', this.onClick, this);
23989         
23990         this.picker().addClass('datepicker-dropdown');
23991     
23992         this.fillTime();
23993         this.update();
23994             
23995         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23996         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23997         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23998         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23999         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24000         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24001
24002     },
24003     
24004     fireKey: function(e){
24005         if (!this.picker().isVisible()){
24006             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24007                 this.show();
24008             }
24009             return;
24010         }
24011
24012         e.preventDefault();
24013         
24014         switch(e.keyCode){
24015             case 27: // escape
24016                 this.hide();
24017                 break;
24018             case 37: // left
24019             case 39: // right
24020                 this.onTogglePeriod();
24021                 break;
24022             case 38: // up
24023                 this.onIncrementMinutes();
24024                 break;
24025             case 40: // down
24026                 this.onDecrementMinutes();
24027                 break;
24028             case 13: // enter
24029             case 9: // tab
24030                 this.setTime();
24031                 break;
24032         }
24033     },
24034     
24035     onClick: function(e) {
24036         e.stopPropagation();
24037         e.preventDefault();
24038     },
24039     
24040     picker : function()
24041     {
24042         return this.pickerEl;
24043     },
24044     
24045     fillTime: function()
24046     {    
24047         var time = this.pop.select('tbody', true).first();
24048         
24049         time.dom.innerHTML = '';
24050         
24051         time.createChild({
24052             tag: 'tr',
24053             cn: [
24054                 {
24055                     tag: 'td',
24056                     cn: [
24057                         {
24058                             tag: 'a',
24059                             href: '#',
24060                             cls: 'btn',
24061                             cn: [
24062                                 {
24063                                     tag: 'i',
24064                                     cls: 'hours-up fa fas fa-chevron-up'
24065                                 }
24066                             ]
24067                         } 
24068                     ]
24069                 },
24070                 {
24071                     tag: 'td',
24072                     cls: 'separator'
24073                 },
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'minutes-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         }
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 }
24094             ]
24095         });
24096         
24097         time.createChild({
24098             tag: 'tr',
24099             cn: [
24100                 {
24101                     tag: 'td',
24102                     cn: [
24103                         {
24104                             tag: 'span',
24105                             cls: 'timepicker-hour',
24106                             html: '00'
24107                         }  
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator',
24113                     html: ':'
24114                 },
24115                 {
24116                     tag: 'td',
24117                     cn: [
24118                         {
24119                             tag: 'span',
24120                             cls: 'timepicker-minute',
24121                             html: '00'
24122                         }  
24123                     ]
24124                 },
24125                 {
24126                     tag: 'td',
24127                     cls: 'separator'
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cn: [
24132                         {
24133                             tag: 'button',
24134                             type: 'button',
24135                             cls: 'btn btn-primary period',
24136                             html: 'AM'
24137                             
24138                         }
24139                     ]
24140                 }
24141             ]
24142         });
24143         
24144         time.createChild({
24145             tag: 'tr',
24146             cn: [
24147                 {
24148                     tag: 'td',
24149                     cn: [
24150                         {
24151                             tag: 'a',
24152                             href: '#',
24153                             cls: 'btn',
24154                             cn: [
24155                                 {
24156                                     tag: 'span',
24157                                     cls: 'hours-down fa fas fa-chevron-down'
24158                                 }
24159                             ]
24160                         }
24161                     ]
24162                 },
24163                 {
24164                     tag: 'td',
24165                     cls: 'separator'
24166                 },
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'minutes-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 }
24187             ]
24188         });
24189         
24190     },
24191     
24192     update: function()
24193     {
24194         
24195         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24196         
24197         this.fill();
24198     },
24199     
24200     fill: function() 
24201     {
24202         var hours = this.time.getHours();
24203         var minutes = this.time.getMinutes();
24204         var period = 'AM';
24205         
24206         if(hours > 11){
24207             period = 'PM';
24208         }
24209         
24210         if(hours == 0){
24211             hours = 12;
24212         }
24213         
24214         
24215         if(hours > 12){
24216             hours = hours - 12;
24217         }
24218         
24219         if(hours < 10){
24220             hours = '0' + hours;
24221         }
24222         
24223         if(minutes < 10){
24224             minutes = '0' + minutes;
24225         }
24226         
24227         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24228         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24229         this.pop.select('button', true).first().dom.innerHTML = period;
24230         
24231     },
24232     
24233     place: function()
24234     {   
24235         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24236         
24237         var cls = ['bottom'];
24238         
24239         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24240             cls.pop();
24241             cls.push('top');
24242         }
24243         
24244         cls.push('right');
24245         
24246         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24247             cls.pop();
24248             cls.push('left');
24249         }
24250         //this.picker().setXY(20000,20000);
24251         this.picker().addClass(cls.join('-'));
24252         
24253         var _this = this;
24254         
24255         Roo.each(cls, function(c){
24256             if(c == 'bottom'){
24257                 (function() {
24258                  //  
24259                 }).defer(200);
24260                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24261                 //_this.picker().setTop(_this.inputEl().getHeight());
24262                 return;
24263             }
24264             if(c == 'top'){
24265                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24266                 
24267                 //_this.picker().setTop(0 - _this.picker().getHeight());
24268                 return;
24269             }
24270             /*
24271             if(c == 'left'){
24272                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24273                 return;
24274             }
24275             if(c == 'right'){
24276                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24277                 return;
24278             }
24279             */
24280         });
24281         
24282     },
24283   
24284     onFocus : function()
24285     {
24286         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24287         this.show();
24288     },
24289     
24290     onBlur : function()
24291     {
24292         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24293         this.hide();
24294     },
24295     
24296     show : function()
24297     {
24298         this.picker().show();
24299         this.pop.show();
24300         this.update();
24301         this.place();
24302         
24303         this.fireEvent('show', this, this.date);
24304     },
24305     
24306     hide : function()
24307     {
24308         this.picker().hide();
24309         this.pop.hide();
24310         
24311         this.fireEvent('hide', this, this.date);
24312     },
24313     
24314     setTime : function()
24315     {
24316         this.hide();
24317         this.setValue(this.time.format(this.format));
24318         
24319         this.fireEvent('select', this, this.date);
24320         
24321         
24322     },
24323     
24324     onMousedown: function(e){
24325         e.stopPropagation();
24326         e.preventDefault();
24327     },
24328     
24329     onIncrementHours: function()
24330     {
24331         Roo.log('onIncrementHours');
24332         this.time = this.time.add(Date.HOUR, 1);
24333         this.update();
24334         
24335     },
24336     
24337     onDecrementHours: function()
24338     {
24339         Roo.log('onDecrementHours');
24340         this.time = this.time.add(Date.HOUR, -1);
24341         this.update();
24342     },
24343     
24344     onIncrementMinutes: function()
24345     {
24346         Roo.log('onIncrementMinutes');
24347         this.time = this.time.add(Date.MINUTE, 1);
24348         this.update();
24349     },
24350     
24351     onDecrementMinutes: function()
24352     {
24353         Roo.log('onDecrementMinutes');
24354         this.time = this.time.add(Date.MINUTE, -1);
24355         this.update();
24356     },
24357     
24358     onTogglePeriod: function()
24359     {
24360         Roo.log('onTogglePeriod');
24361         this.time = this.time.add(Date.HOUR, 12);
24362         this.update();
24363     }
24364     
24365    
24366 });
24367  
24368
24369 Roo.apply(Roo.bootstrap.form.TimeField,  {
24370   
24371     template : {
24372         tag: 'div',
24373         cls: 'datepicker dropdown-menu',
24374         cn: [
24375             {
24376                 tag: 'div',
24377                 cls: 'datepicker-time',
24378                 cn: [
24379                 {
24380                     tag: 'table',
24381                     cls: 'table-condensed',
24382                     cn:[
24383                         {
24384                             tag: 'tbody',
24385                             cn: [
24386                                 {
24387                                     tag: 'tr',
24388                                     cn: [
24389                                     {
24390                                         tag: 'td',
24391                                         colspan: '7'
24392                                     }
24393                                     ]
24394                                 }
24395                             ]
24396                         },
24397                         {
24398                             tag: 'tfoot',
24399                             cn: [
24400                                 {
24401                                     tag: 'tr',
24402                                     cn: [
24403                                     {
24404                                         tag: 'th',
24405                                         colspan: '7',
24406                                         cls: '',
24407                                         cn: [
24408                                             {
24409                                                 tag: 'button',
24410                                                 cls: 'btn btn-info ok',
24411                                                 html: 'OK'
24412                                             }
24413                                         ]
24414                                     }
24415                     
24416                                     ]
24417                                 }
24418                             ]
24419                         }
24420                     ]
24421                 }
24422                 ]
24423             }
24424         ]
24425     }
24426 });
24427
24428  
24429
24430  /*
24431  * - LGPL
24432  *
24433  * MonthField
24434  * 
24435  */
24436
24437 /**
24438  * @class Roo.bootstrap.form.MonthField
24439  * @extends Roo.bootstrap.form.Input
24440  * Bootstrap MonthField class
24441  * 
24442  * @cfg {String} language default en
24443  * 
24444  * @constructor
24445  * Create a new MonthField
24446  * @param {Object} config The config object
24447  */
24448
24449 Roo.bootstrap.form.MonthField = function(config){
24450     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24451     
24452     this.addEvents({
24453         /**
24454          * @event show
24455          * Fires when this field show.
24456          * @param {Roo.bootstrap.form.MonthField} this
24457          * @param {Mixed} date The date value
24458          */
24459         show : true,
24460         /**
24461          * @event show
24462          * Fires when this field hide.
24463          * @param {Roo.bootstrap.form.MonthField} this
24464          * @param {Mixed} date The date value
24465          */
24466         hide : true,
24467         /**
24468          * @event select
24469          * Fires when select a date.
24470          * @param {Roo.bootstrap.form.MonthField} this
24471          * @param {String} oldvalue The old value
24472          * @param {String} newvalue The new value
24473          */
24474         select : true
24475     });
24476 };
24477
24478 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24479     
24480     onRender: function(ct, position)
24481     {
24482         
24483         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24484         
24485         this.language = this.language || 'en';
24486         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24487         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24488         
24489         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24490         this.isInline = false;
24491         this.isInput = true;
24492         this.component = this.el.select('.add-on', true).first() || false;
24493         this.component = (this.component && this.component.length === 0) ? false : this.component;
24494         this.hasInput = this.component && this.inputEL().length;
24495         
24496         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24497         
24498         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24499         
24500         this.picker().on('mousedown', this.onMousedown, this);
24501         this.picker().on('click', this.onClick, this);
24502         
24503         this.picker().addClass('datepicker-dropdown');
24504         
24505         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24506             v.setStyle('width', '189px');
24507         });
24508         
24509         this.fillMonths();
24510         
24511         this.update();
24512         
24513         if(this.isInline) {
24514             this.show();
24515         }
24516         
24517     },
24518     
24519     setValue: function(v, suppressEvent)
24520     {   
24521         var o = this.getValue();
24522         
24523         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24524         
24525         this.update();
24526
24527         if(suppressEvent !== true){
24528             this.fireEvent('select', this, o, v);
24529         }
24530         
24531     },
24532     
24533     getValue: function()
24534     {
24535         return this.value;
24536     },
24537     
24538     onClick: function(e) 
24539     {
24540         e.stopPropagation();
24541         e.preventDefault();
24542         
24543         var target = e.getTarget();
24544         
24545         if(target.nodeName.toLowerCase() === 'i'){
24546             target = Roo.get(target).dom.parentNode;
24547         }
24548         
24549         var nodeName = target.nodeName;
24550         var className = target.className;
24551         var html = target.innerHTML;
24552         
24553         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24554             return;
24555         }
24556         
24557         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24558         
24559         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24560         
24561         this.hide();
24562                         
24563     },
24564     
24565     picker : function()
24566     {
24567         return this.pickerEl;
24568     },
24569     
24570     fillMonths: function()
24571     {    
24572         var i = 0;
24573         var months = this.picker().select('>.datepicker-months td', true).first();
24574         
24575         months.dom.innerHTML = '';
24576         
24577         while (i < 12) {
24578             var month = {
24579                 tag: 'span',
24580                 cls: 'month',
24581                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24582             };
24583             
24584             months.createChild(month);
24585         }
24586         
24587     },
24588     
24589     update: function()
24590     {
24591         var _this = this;
24592         
24593         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24594             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24595         }
24596         
24597         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24598             e.removeClass('active');
24599             
24600             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24601                 e.addClass('active');
24602             }
24603         })
24604     },
24605     
24606     place: function()
24607     {
24608         if(this.isInline) {
24609             return;
24610         }
24611         
24612         this.picker().removeClass(['bottom', 'top']);
24613         
24614         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24615             /*
24616              * place to the top of element!
24617              *
24618              */
24619             
24620             this.picker().addClass('top');
24621             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24622             
24623             return;
24624         }
24625         
24626         this.picker().addClass('bottom');
24627         
24628         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24629     },
24630     
24631     onFocus : function()
24632     {
24633         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24634         this.show();
24635     },
24636     
24637     onBlur : function()
24638     {
24639         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24640         
24641         var d = this.inputEl().getValue();
24642         
24643         this.setValue(d);
24644                 
24645         this.hide();
24646     },
24647     
24648     show : function()
24649     {
24650         this.picker().show();
24651         this.picker().select('>.datepicker-months', true).first().show();
24652         this.update();
24653         this.place();
24654         
24655         this.fireEvent('show', this, this.date);
24656     },
24657     
24658     hide : function()
24659     {
24660         if(this.isInline) {
24661             return;
24662         }
24663         this.picker().hide();
24664         this.fireEvent('hide', this, this.date);
24665         
24666     },
24667     
24668     onMousedown: function(e)
24669     {
24670         e.stopPropagation();
24671         e.preventDefault();
24672     },
24673     
24674     keyup: function(e)
24675     {
24676         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24677         this.update();
24678     },
24679
24680     fireKey: function(e)
24681     {
24682         if (!this.picker().isVisible()){
24683             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24684                 this.show();
24685             }
24686             return;
24687         }
24688         
24689         var dir;
24690         
24691         switch(e.keyCode){
24692             case 27: // escape
24693                 this.hide();
24694                 e.preventDefault();
24695                 break;
24696             case 37: // left
24697             case 39: // right
24698                 dir = e.keyCode == 37 ? -1 : 1;
24699                 
24700                 this.vIndex = this.vIndex + dir;
24701                 
24702                 if(this.vIndex < 0){
24703                     this.vIndex = 0;
24704                 }
24705                 
24706                 if(this.vIndex > 11){
24707                     this.vIndex = 11;
24708                 }
24709                 
24710                 if(isNaN(this.vIndex)){
24711                     this.vIndex = 0;
24712                 }
24713                 
24714                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24715                 
24716                 break;
24717             case 38: // up
24718             case 40: // down
24719                 
24720                 dir = e.keyCode == 38 ? -1 : 1;
24721                 
24722                 this.vIndex = this.vIndex + dir * 4;
24723                 
24724                 if(this.vIndex < 0){
24725                     this.vIndex = 0;
24726                 }
24727                 
24728                 if(this.vIndex > 11){
24729                     this.vIndex = 11;
24730                 }
24731                 
24732                 if(isNaN(this.vIndex)){
24733                     this.vIndex = 0;
24734                 }
24735                 
24736                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24737                 break;
24738                 
24739             case 13: // enter
24740                 
24741                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24742                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24743                 }
24744                 
24745                 this.hide();
24746                 e.preventDefault();
24747                 break;
24748             case 9: // tab
24749                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24750                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24751                 }
24752                 this.hide();
24753                 break;
24754             case 16: // shift
24755             case 17: // ctrl
24756             case 18: // alt
24757                 break;
24758             default :
24759                 this.hide();
24760                 
24761         }
24762     },
24763     
24764     remove: function() 
24765     {
24766         this.picker().remove();
24767     }
24768    
24769 });
24770
24771 Roo.apply(Roo.bootstrap.form.MonthField,  {
24772     
24773     content : {
24774         tag: 'tbody',
24775         cn: [
24776         {
24777             tag: 'tr',
24778             cn: [
24779             {
24780                 tag: 'td',
24781                 colspan: '7'
24782             }
24783             ]
24784         }
24785         ]
24786     },
24787     
24788     dates:{
24789         en: {
24790             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24791             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24792         }
24793     }
24794 });
24795
24796 Roo.apply(Roo.bootstrap.form.MonthField,  {
24797   
24798     template : {
24799         tag: 'div',
24800         cls: 'datepicker dropdown-menu roo-dynamic',
24801         cn: [
24802             {
24803                 tag: 'div',
24804                 cls: 'datepicker-months',
24805                 cn: [
24806                 {
24807                     tag: 'table',
24808                     cls: 'table-condensed',
24809                     cn:[
24810                         Roo.bootstrap.form.DateField.content
24811                     ]
24812                 }
24813                 ]
24814             }
24815         ]
24816     }
24817 });
24818
24819  
24820
24821  
24822  /*
24823  * - LGPL
24824  *
24825  * CheckBox
24826  * 
24827  */
24828
24829 /**
24830  * @class Roo.bootstrap.form.CheckBox
24831  * @extends Roo.bootstrap.form.Input
24832  * Bootstrap CheckBox class
24833  * 
24834  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24835  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24836  * @cfg {String} boxLabel The text that appears beside the checkbox
24837  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24838  * @cfg {Boolean} checked initnal the element
24839  * @cfg {Boolean} inline inline the element (default false)
24840  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24841  * @cfg {String} tooltip label tooltip
24842  * 
24843  * @constructor
24844  * Create a new CheckBox
24845  * @param {Object} config The config object
24846  */
24847
24848 Roo.bootstrap.form.CheckBox = function(config){
24849     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24850    
24851     this.addEvents({
24852         /**
24853         * @event check
24854         * Fires when the element is checked or unchecked.
24855         * @param {Roo.bootstrap.form.CheckBox} this This input
24856         * @param {Boolean} checked The new checked value
24857         */
24858        check : true,
24859        /**
24860         * @event click
24861         * Fires when the element is click.
24862         * @param {Roo.bootstrap.form.CheckBox} this This input
24863         */
24864        click : true
24865     });
24866     
24867 };
24868
24869 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24870   
24871     inputType: 'checkbox',
24872     inputValue: 1,
24873     valueOff: 0,
24874     boxLabel: false,
24875     checked: false,
24876     weight : false,
24877     inline: false,
24878     tooltip : '',
24879     
24880     // checkbox success does not make any sense really.. 
24881     invalidClass : "",
24882     validClass : "",
24883     
24884     
24885     getAutoCreate : function()
24886     {
24887         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24888         
24889         var id = Roo.id();
24890         
24891         var cfg = {};
24892         
24893         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24894         
24895         if(this.inline){
24896             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24897         }
24898         
24899         var input =  {
24900             tag: 'input',
24901             id : id,
24902             type : this.inputType,
24903             value : this.inputValue,
24904             cls : 'roo-' + this.inputType, //'form-box',
24905             placeholder : this.placeholder || ''
24906             
24907         };
24908         
24909         if(this.inputType != 'radio'){
24910             var hidden =  {
24911                 tag: 'input',
24912                 type : 'hidden',
24913                 cls : 'roo-hidden-value',
24914                 value : this.checked ? this.inputValue : this.valueOff
24915             };
24916         }
24917         
24918             
24919         if (this.weight) { // Validity check?
24920             cfg.cls += " " + this.inputType + "-" + this.weight;
24921         }
24922         
24923         if (this.disabled) {
24924             input.disabled=true;
24925         }
24926         
24927         if(this.checked){
24928             input.checked = this.checked;
24929         }
24930         
24931         if (this.name) {
24932             
24933             input.name = this.name;
24934             
24935             if(this.inputType != 'radio'){
24936                 hidden.name = this.name;
24937                 input.name = '_hidden_' + this.name;
24938             }
24939         }
24940         
24941         if (this.size) {
24942             input.cls += ' input-' + this.size;
24943         }
24944         
24945         var settings=this;
24946         
24947         ['xs','sm','md','lg'].map(function(size){
24948             if (settings[size]) {
24949                 cfg.cls += ' col-' + size + '-' + settings[size];
24950             }
24951         });
24952         
24953         var inputblock = input;
24954          
24955         if (this.before || this.after) {
24956             
24957             inputblock = {
24958                 cls : 'input-group',
24959                 cn :  [] 
24960             };
24961             
24962             if (this.before) {
24963                 inputblock.cn.push({
24964                     tag :'span',
24965                     cls : 'input-group-addon',
24966                     html : this.before
24967                 });
24968             }
24969             
24970             inputblock.cn.push(input);
24971             
24972             if(this.inputType != 'radio'){
24973                 inputblock.cn.push(hidden);
24974             }
24975             
24976             if (this.after) {
24977                 inputblock.cn.push({
24978                     tag :'span',
24979                     cls : 'input-group-addon',
24980                     html : this.after
24981                 });
24982             }
24983             
24984         }
24985         var boxLabelCfg = false;
24986         
24987         if(this.boxLabel){
24988            
24989             boxLabelCfg = {
24990                 tag: 'label',
24991                 //'for': id, // box label is handled by onclick - so no for...
24992                 cls: 'box-label',
24993                 html: this.boxLabel
24994             };
24995             if(this.tooltip){
24996                 boxLabelCfg.tooltip = this.tooltip;
24997             }
24998              
24999         }
25000         
25001         
25002         if (align ==='left' && this.fieldLabel.length) {
25003 //                Roo.log("left and has label");
25004             cfg.cn = [
25005                 {
25006                     tag: 'label',
25007                     'for' :  id,
25008                     cls : 'control-label',
25009                     html : this.fieldLabel
25010                 },
25011                 {
25012                     cls : "", 
25013                     cn: [
25014                         inputblock
25015                     ]
25016                 }
25017             ];
25018             
25019             if (boxLabelCfg) {
25020                 cfg.cn[1].cn.push(boxLabelCfg);
25021             }
25022             
25023             if(this.labelWidth > 12){
25024                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25025             }
25026             
25027             if(this.labelWidth < 13 && this.labelmd == 0){
25028                 this.labelmd = this.labelWidth;
25029             }
25030             
25031             if(this.labellg > 0){
25032                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25033                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25034             }
25035             
25036             if(this.labelmd > 0){
25037                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25038                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25039             }
25040             
25041             if(this.labelsm > 0){
25042                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25043                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25044             }
25045             
25046             if(this.labelxs > 0){
25047                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25048                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25049             }
25050             
25051         } else if ( this.fieldLabel.length) {
25052 //                Roo.log(" label");
25053                 cfg.cn = [
25054                    
25055                     {
25056                         tag: this.boxLabel ? 'span' : 'label',
25057                         'for': id,
25058                         cls: 'control-label box-input-label',
25059                         //cls : 'input-group-addon',
25060                         html : this.fieldLabel
25061                     },
25062                     
25063                     inputblock
25064                     
25065                 ];
25066                 if (boxLabelCfg) {
25067                     cfg.cn.push(boxLabelCfg);
25068                 }
25069
25070         } else {
25071             
25072 //                Roo.log(" no label && no align");
25073                 cfg.cn = [  inputblock ] ;
25074                 if (boxLabelCfg) {
25075                     cfg.cn.push(boxLabelCfg);
25076                 }
25077
25078                 
25079         }
25080         
25081        
25082         
25083         if(this.inputType != 'radio'){
25084             cfg.cn.push(hidden);
25085         }
25086         
25087         return cfg;
25088         
25089     },
25090     
25091     /**
25092      * return the real input element.
25093      */
25094     inputEl: function ()
25095     {
25096         return this.el.select('input.roo-' + this.inputType,true).first();
25097     },
25098     hiddenEl: function ()
25099     {
25100         return this.el.select('input.roo-hidden-value',true).first();
25101     },
25102     
25103     labelEl: function()
25104     {
25105         return this.el.select('label.control-label',true).first();
25106     },
25107     /* depricated... */
25108     
25109     label: function()
25110     {
25111         return this.labelEl();
25112     },
25113     
25114     boxLabelEl: function()
25115     {
25116         return this.el.select('label.box-label',true).first();
25117     },
25118     
25119     initEvents : function()
25120     {
25121 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25122         
25123         this.inputEl().on('click', this.onClick,  this);
25124         
25125         if (this.boxLabel) { 
25126             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25127         }
25128         
25129         this.startValue = this.getValue();
25130         
25131         if(this.groupId){
25132             Roo.bootstrap.form.CheckBox.register(this);
25133         }
25134     },
25135     
25136     onClick : function(e)
25137     {   
25138         if(this.fireEvent('click', this, e) !== false){
25139             this.setChecked(!this.checked);
25140         }
25141         
25142     },
25143     
25144     setChecked : function(state,suppressEvent)
25145     {
25146         this.startValue = this.getValue();
25147
25148         if(this.inputType == 'radio'){
25149             
25150             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25151                 e.dom.checked = false;
25152             });
25153             
25154             this.inputEl().dom.checked = true;
25155             
25156             this.inputEl().dom.value = this.inputValue;
25157             
25158             if(suppressEvent !== true){
25159                 this.fireEvent('check', this, true);
25160             }
25161             
25162             this.validate();
25163             
25164             return;
25165         }
25166         
25167         this.checked = state;
25168         
25169         this.inputEl().dom.checked = state;
25170         
25171         
25172         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25173         
25174         if(suppressEvent !== true){
25175             this.fireEvent('check', this, state);
25176         }
25177         
25178         this.validate();
25179     },
25180     
25181     getValue : function()
25182     {
25183         if(this.inputType == 'radio'){
25184             return this.getGroupValue();
25185         }
25186         
25187         return this.hiddenEl().dom.value;
25188         
25189     },
25190     
25191     getGroupValue : function()
25192     {
25193         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25194             return '';
25195         }
25196         
25197         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25198     },
25199     
25200     setValue : function(v,suppressEvent)
25201     {
25202         if(this.inputType == 'radio'){
25203             this.setGroupValue(v, suppressEvent);
25204             return;
25205         }
25206         
25207         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25208         
25209         this.validate();
25210     },
25211     
25212     setGroupValue : function(v, suppressEvent)
25213     {
25214         this.startValue = this.getValue();
25215         
25216         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25217             e.dom.checked = false;
25218             
25219             if(e.dom.value == v){
25220                 e.dom.checked = true;
25221             }
25222         });
25223         
25224         if(suppressEvent !== true){
25225             this.fireEvent('check', this, true);
25226         }
25227
25228         this.validate();
25229         
25230         return;
25231     },
25232     
25233     validate : function()
25234     {
25235         if(this.getVisibilityEl().hasClass('hidden')){
25236             return true;
25237         }
25238         
25239         if(
25240                 this.disabled || 
25241                 (this.inputType == 'radio' && this.validateRadio()) ||
25242                 (this.inputType == 'checkbox' && this.validateCheckbox())
25243         ){
25244             this.markValid();
25245             return true;
25246         }
25247         
25248         this.markInvalid();
25249         return false;
25250     },
25251     
25252     validateRadio : function()
25253     {
25254         if(this.getVisibilityEl().hasClass('hidden')){
25255             return true;
25256         }
25257         
25258         if(this.allowBlank){
25259             return true;
25260         }
25261         
25262         var valid = false;
25263         
25264         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25265             if(!e.dom.checked){
25266                 return;
25267             }
25268             
25269             valid = true;
25270             
25271             return false;
25272         });
25273         
25274         return valid;
25275     },
25276     
25277     validateCheckbox : function()
25278     {
25279         if(!this.groupId){
25280             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25281             //return (this.getValue() == this.inputValue) ? true : false;
25282         }
25283         
25284         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25285         
25286         if(!group){
25287             return false;
25288         }
25289         
25290         var r = false;
25291         
25292         for(var i in group){
25293             if(group[i].el.isVisible(true)){
25294                 r = false;
25295                 break;
25296             }
25297             
25298             r = true;
25299         }
25300         
25301         for(var i in group){
25302             if(r){
25303                 break;
25304             }
25305             
25306             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25307         }
25308         
25309         return r;
25310     },
25311     
25312     /**
25313      * Mark this field as valid
25314      */
25315     markValid : function()
25316     {
25317         var _this = this;
25318         
25319         this.fireEvent('valid', this);
25320         
25321         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25322         
25323         if(this.groupId){
25324             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25325         }
25326         
25327         if(label){
25328             label.markValid();
25329         }
25330
25331         if(this.inputType == 'radio'){
25332             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25333                 var fg = e.findParent('.form-group', false, true);
25334                 if (Roo.bootstrap.version == 3) {
25335                     fg.removeClass([_this.invalidClass, _this.validClass]);
25336                     fg.addClass(_this.validClass);
25337                 } else {
25338                     fg.removeClass(['is-valid', 'is-invalid']);
25339                     fg.addClass('is-valid');
25340                 }
25341             });
25342             
25343             return;
25344         }
25345
25346         if(!this.groupId){
25347             var fg = this.el.findParent('.form-group', false, true);
25348             if (Roo.bootstrap.version == 3) {
25349                 fg.removeClass([this.invalidClass, this.validClass]);
25350                 fg.addClass(this.validClass);
25351             } else {
25352                 fg.removeClass(['is-valid', 'is-invalid']);
25353                 fg.addClass('is-valid');
25354             }
25355             return;
25356         }
25357         
25358         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25359         
25360         if(!group){
25361             return;
25362         }
25363         
25364         for(var i in group){
25365             var fg = group[i].el.findParent('.form-group', false, true);
25366             if (Roo.bootstrap.version == 3) {
25367                 fg.removeClass([this.invalidClass, this.validClass]);
25368                 fg.addClass(this.validClass);
25369             } else {
25370                 fg.removeClass(['is-valid', 'is-invalid']);
25371                 fg.addClass('is-valid');
25372             }
25373         }
25374     },
25375     
25376      /**
25377      * Mark this field as invalid
25378      * @param {String} msg The validation message
25379      */
25380     markInvalid : function(msg)
25381     {
25382         if(this.allowBlank){
25383             return;
25384         }
25385         
25386         var _this = this;
25387         
25388         this.fireEvent('invalid', this, msg);
25389         
25390         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25391         
25392         if(this.groupId){
25393             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25394         }
25395         
25396         if(label){
25397             label.markInvalid();
25398         }
25399             
25400         if(this.inputType == 'radio'){
25401             
25402             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25403                 var fg = e.findParent('.form-group', false, true);
25404                 if (Roo.bootstrap.version == 3) {
25405                     fg.removeClass([_this.invalidClass, _this.validClass]);
25406                     fg.addClass(_this.invalidClass);
25407                 } else {
25408                     fg.removeClass(['is-invalid', 'is-valid']);
25409                     fg.addClass('is-invalid');
25410                 }
25411             });
25412             
25413             return;
25414         }
25415         
25416         if(!this.groupId){
25417             var fg = this.el.findParent('.form-group', false, true);
25418             if (Roo.bootstrap.version == 3) {
25419                 fg.removeClass([_this.invalidClass, _this.validClass]);
25420                 fg.addClass(_this.invalidClass);
25421             } else {
25422                 fg.removeClass(['is-invalid', 'is-valid']);
25423                 fg.addClass('is-invalid');
25424             }
25425             return;
25426         }
25427         
25428         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25429         
25430         if(!group){
25431             return;
25432         }
25433         
25434         for(var i in group){
25435             var fg = group[i].el.findParent('.form-group', false, true);
25436             if (Roo.bootstrap.version == 3) {
25437                 fg.removeClass([_this.invalidClass, _this.validClass]);
25438                 fg.addClass(_this.invalidClass);
25439             } else {
25440                 fg.removeClass(['is-invalid', 'is-valid']);
25441                 fg.addClass('is-invalid');
25442             }
25443         }
25444         
25445     },
25446     
25447     clearInvalid : function()
25448     {
25449         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25450         
25451         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25452         
25453         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25454         
25455         if (label && label.iconEl) {
25456             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25457             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25458         }
25459     },
25460     
25461     disable : function()
25462     {
25463         if(this.inputType != 'radio'){
25464             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25465             return;
25466         }
25467         
25468         var _this = this;
25469         
25470         if(this.rendered){
25471             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25472                 _this.getActionEl().addClass(this.disabledClass);
25473                 e.dom.disabled = true;
25474             });
25475         }
25476         
25477         this.disabled = true;
25478         this.fireEvent("disable", this);
25479         return this;
25480     },
25481
25482     enable : function()
25483     {
25484         if(this.inputType != 'radio'){
25485             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25486             return;
25487         }
25488         
25489         var _this = this;
25490         
25491         if(this.rendered){
25492             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25493                 _this.getActionEl().removeClass(this.disabledClass);
25494                 e.dom.disabled = false;
25495             });
25496         }
25497         
25498         this.disabled = false;
25499         this.fireEvent("enable", this);
25500         return this;
25501     },
25502     
25503     setBoxLabel : function(v)
25504     {
25505         this.boxLabel = v;
25506         
25507         if(this.rendered){
25508             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25509         }
25510     }
25511
25512 });
25513
25514 Roo.apply(Roo.bootstrap.form.CheckBox, {
25515     
25516     groups: {},
25517     
25518      /**
25519     * register a CheckBox Group
25520     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25521     */
25522     register : function(checkbox)
25523     {
25524         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25525             this.groups[checkbox.groupId] = {};
25526         }
25527         
25528         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25529             return;
25530         }
25531         
25532         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25533         
25534     },
25535     /**
25536     * fetch a CheckBox Group based on the group ID
25537     * @param {string} the group ID
25538     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25539     */
25540     get: function(groupId) {
25541         if (typeof(this.groups[groupId]) == 'undefined') {
25542             return false;
25543         }
25544         
25545         return this.groups[groupId] ;
25546     }
25547     
25548     
25549 });
25550 /*
25551  * - LGPL
25552  *
25553  * RadioItem
25554  * 
25555  */
25556
25557 /**
25558  * @class Roo.bootstrap.form.Radio
25559  * @extends Roo.bootstrap.Component
25560  * Bootstrap Radio class
25561  * @cfg {String} boxLabel - the label associated
25562  * @cfg {String} value - the value of radio
25563  * 
25564  * @constructor
25565  * Create a new Radio
25566  * @param {Object} config The config object
25567  */
25568 Roo.bootstrap.form.Radio = function(config){
25569     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25570     
25571 };
25572
25573 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25574     
25575     boxLabel : '',
25576     
25577     value : '',
25578     
25579     getAutoCreate : function()
25580     {
25581         var cfg = {
25582             tag : 'div',
25583             cls : 'form-group radio',
25584             cn : [
25585                 {
25586                     tag : 'label',
25587                     cls : 'box-label',
25588                     html : this.boxLabel
25589                 }
25590             ]
25591         };
25592         
25593         return cfg;
25594     },
25595     
25596     initEvents : function() 
25597     {
25598         this.parent().register(this);
25599         
25600         this.el.on('click', this.onClick, this);
25601         
25602     },
25603     
25604     onClick : function(e)
25605     {
25606         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25607             this.setChecked(true);
25608         }
25609     },
25610     
25611     setChecked : function(state, suppressEvent)
25612     {
25613         this.parent().setValue(this.value, suppressEvent);
25614         
25615     },
25616     
25617     setBoxLabel : function(v)
25618     {
25619         this.boxLabel = v;
25620         
25621         if(this.rendered){
25622             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25623         }
25624     }
25625     
25626 });
25627  
25628
25629  /*
25630  * - LGPL
25631  *
25632  * Input
25633  * 
25634  */
25635
25636 /**
25637  * @class Roo.bootstrap.form.SecurePass
25638  * @extends Roo.bootstrap.form.Input
25639  * Bootstrap SecurePass class
25640  *
25641  * 
25642  * @constructor
25643  * Create a new SecurePass
25644  * @param {Object} config The config object
25645  */
25646  
25647 Roo.bootstrap.form.SecurePass = function (config) {
25648     // these go here, so the translation tool can replace them..
25649     this.errors = {
25650         PwdEmpty: "Please type a password, and then retype it to confirm.",
25651         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25652         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25653         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25654         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25655         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25656         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25657         TooWeak: "Your password is Too Weak."
25658     },
25659     this.meterLabel = "Password strength:";
25660     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25661     this.meterClass = [
25662         "roo-password-meter-tooweak", 
25663         "roo-password-meter-weak", 
25664         "roo-password-meter-medium", 
25665         "roo-password-meter-strong", 
25666         "roo-password-meter-grey"
25667     ];
25668     
25669     this.errors = {};
25670     
25671     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25672 }
25673
25674 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25675     /**
25676      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25677      * {
25678      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25679      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25680      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25681      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25682      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25683      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25684      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25685      * })
25686      */
25687     // private
25688     
25689     meterWidth: 300,
25690     errorMsg :'',    
25691     errors: false,
25692     imageRoot: '/',
25693     /**
25694      * @cfg {String/Object} Label for the strength meter (defaults to
25695      * 'Password strength:')
25696      */
25697     // private
25698     meterLabel: '',
25699     /**
25700      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25701      * ['Weak', 'Medium', 'Strong'])
25702      */
25703     // private    
25704     pwdStrengths: false,    
25705     // private
25706     strength: 0,
25707     // private
25708     _lastPwd: null,
25709     // private
25710     kCapitalLetter: 0,
25711     kSmallLetter: 1,
25712     kDigit: 2,
25713     kPunctuation: 3,
25714     
25715     insecure: false,
25716     // private
25717     initEvents: function ()
25718     {
25719         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25720
25721         if (this.el.is('input[type=password]') && Roo.isSafari) {
25722             this.el.on('keydown', this.SafariOnKeyDown, this);
25723         }
25724
25725         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25726     },
25727     // private
25728     onRender: function (ct, position)
25729     {
25730         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25731         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25732         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25733
25734         this.trigger.createChild({
25735                    cn: [
25736                     {
25737                     //id: 'PwdMeter',
25738                     tag: 'div',
25739                     cls: 'roo-password-meter-grey col-xs-12',
25740                     style: {
25741                         //width: 0,
25742                         //width: this.meterWidth + 'px'                                                
25743                         }
25744                     },
25745                     {                            
25746                          cls: 'roo-password-meter-text'                          
25747                     }
25748                 ]            
25749         });
25750
25751          
25752         if (this.hideTrigger) {
25753             this.trigger.setDisplayed(false);
25754         }
25755         this.setSize(this.width || '', this.height || '');
25756     },
25757     // private
25758     onDestroy: function ()
25759     {
25760         if (this.trigger) {
25761             this.trigger.removeAllListeners();
25762             this.trigger.remove();
25763         }
25764         if (this.wrap) {
25765             this.wrap.remove();
25766         }
25767         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25768     },
25769     // private
25770     checkStrength: function ()
25771     {
25772         var pwd = this.inputEl().getValue();
25773         if (pwd == this._lastPwd) {
25774             return;
25775         }
25776
25777         var strength;
25778         if (this.ClientSideStrongPassword(pwd)) {
25779             strength = 3;
25780         } else if (this.ClientSideMediumPassword(pwd)) {
25781             strength = 2;
25782         } else if (this.ClientSideWeakPassword(pwd)) {
25783             strength = 1;
25784         } else {
25785             strength = 0;
25786         }
25787         
25788         Roo.log('strength1: ' + strength);
25789         
25790         //var pm = this.trigger.child('div/div/div').dom;
25791         var pm = this.trigger.child('div/div');
25792         pm.removeClass(this.meterClass);
25793         pm.addClass(this.meterClass[strength]);
25794                 
25795         
25796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25797                 
25798         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25799         
25800         this._lastPwd = pwd;
25801     },
25802     reset: function ()
25803     {
25804         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25805         
25806         this._lastPwd = '';
25807         
25808         var pm = this.trigger.child('div/div');
25809         pm.removeClass(this.meterClass);
25810         pm.addClass('roo-password-meter-grey');        
25811         
25812         
25813         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25814         
25815         pt.innerHTML = '';
25816         this.inputEl().dom.type='password';
25817     },
25818     // private
25819     validateValue: function (value)
25820     {
25821         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25822             return false;
25823         }
25824         if (value.length == 0) {
25825             if (this.allowBlank) {
25826                 this.clearInvalid();
25827                 return true;
25828             }
25829
25830             this.markInvalid(this.errors.PwdEmpty);
25831             this.errorMsg = this.errors.PwdEmpty;
25832             return false;
25833         }
25834         
25835         if(this.insecure){
25836             return true;
25837         }
25838         
25839         if (!value.match(/[\x21-\x7e]+/)) {
25840             this.markInvalid(this.errors.PwdBadChar);
25841             this.errorMsg = this.errors.PwdBadChar;
25842             return false;
25843         }
25844         if (value.length < 6) {
25845             this.markInvalid(this.errors.PwdShort);
25846             this.errorMsg = this.errors.PwdShort;
25847             return false;
25848         }
25849         if (value.length > 16) {
25850             this.markInvalid(this.errors.PwdLong);
25851             this.errorMsg = this.errors.PwdLong;
25852             return false;
25853         }
25854         var strength;
25855         if (this.ClientSideStrongPassword(value)) {
25856             strength = 3;
25857         } else if (this.ClientSideMediumPassword(value)) {
25858             strength = 2;
25859         } else if (this.ClientSideWeakPassword(value)) {
25860             strength = 1;
25861         } else {
25862             strength = 0;
25863         }
25864
25865         
25866         if (strength < 2) {
25867             //this.markInvalid(this.errors.TooWeak);
25868             this.errorMsg = this.errors.TooWeak;
25869             //return false;
25870         }
25871         
25872         
25873         console.log('strength2: ' + strength);
25874         
25875         //var pm = this.trigger.child('div/div/div').dom;
25876         
25877         var pm = this.trigger.child('div/div');
25878         pm.removeClass(this.meterClass);
25879         pm.addClass(this.meterClass[strength]);
25880                 
25881         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25882                 
25883         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25884         
25885         this.errorMsg = ''; 
25886         return true;
25887     },
25888     // private
25889     CharacterSetChecks: function (type)
25890     {
25891         this.type = type;
25892         this.fResult = false;
25893     },
25894     // private
25895     isctype: function (character, type)
25896     {
25897         switch (type) {  
25898             case this.kCapitalLetter:
25899                 if (character >= 'A' && character <= 'Z') {
25900                     return true;
25901                 }
25902                 break;
25903             
25904             case this.kSmallLetter:
25905                 if (character >= 'a' && character <= 'z') {
25906                     return true;
25907                 }
25908                 break;
25909             
25910             case this.kDigit:
25911                 if (character >= '0' && character <= '9') {
25912                     return true;
25913                 }
25914                 break;
25915             
25916             case this.kPunctuation:
25917                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25918                     return true;
25919                 }
25920                 break;
25921             
25922             default:
25923                 return false;
25924         }
25925
25926     },
25927     // private
25928     IsLongEnough: function (pwd, size)
25929     {
25930         return !(pwd == null || isNaN(size) || pwd.length < size);
25931     },
25932     // private
25933     SpansEnoughCharacterSets: function (word, nb)
25934     {
25935         if (!this.IsLongEnough(word, nb))
25936         {
25937             return false;
25938         }
25939
25940         var characterSetChecks = new Array(
25941             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25942             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25943         );
25944         
25945         for (var index = 0; index < word.length; ++index) {
25946             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25947                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25948                     characterSetChecks[nCharSet].fResult = true;
25949                     break;
25950                 }
25951             }
25952         }
25953
25954         var nCharSets = 0;
25955         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25956             if (characterSetChecks[nCharSet].fResult) {
25957                 ++nCharSets;
25958             }
25959         }
25960
25961         if (nCharSets < nb) {
25962             return false;
25963         }
25964         return true;
25965     },
25966     // private
25967     ClientSideStrongPassword: function (pwd)
25968     {
25969         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25970     },
25971     // private
25972     ClientSideMediumPassword: function (pwd)
25973     {
25974         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25975     },
25976     // private
25977     ClientSideWeakPassword: function (pwd)
25978     {
25979         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25980     }
25981           
25982 })//<script type="text/javascript">
25983
25984 /*
25985  * Based  Ext JS Library 1.1.1
25986  * Copyright(c) 2006-2007, Ext JS, LLC.
25987  * LGPL
25988  *
25989  */
25990  
25991 /**
25992  * @class Roo.HtmlEditorCore
25993  * @extends Roo.Component
25994  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25995  *
25996  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25997  */
25998
25999 Roo.HtmlEditorCore = function(config){
26000     
26001     
26002     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
26003     
26004     
26005     this.addEvents({
26006         /**
26007          * @event initialize
26008          * Fires when the editor is fully initialized (including the iframe)
26009          * @param {Roo.HtmlEditorCore} this
26010          */
26011         initialize: true,
26012         /**
26013          * @event activate
26014          * Fires when the editor is first receives the focus. Any insertion must wait
26015          * until after this event.
26016          * @param {Roo.HtmlEditorCore} this
26017          */
26018         activate: true,
26019          /**
26020          * @event beforesync
26021          * Fires before the textarea is updated with content from the editor iframe. Return false
26022          * to cancel the sync.
26023          * @param {Roo.HtmlEditorCore} this
26024          * @param {String} html
26025          */
26026         beforesync: true,
26027          /**
26028          * @event beforepush
26029          * Fires before the iframe editor is updated with content from the textarea. Return false
26030          * to cancel the push.
26031          * @param {Roo.HtmlEditorCore} this
26032          * @param {String} html
26033          */
26034         beforepush: true,
26035          /**
26036          * @event sync
26037          * Fires when the textarea is updated with content from the editor iframe.
26038          * @param {Roo.HtmlEditorCore} this
26039          * @param {String} html
26040          */
26041         sync: true,
26042          /**
26043          * @event push
26044          * Fires when the iframe editor is updated with content from the textarea.
26045          * @param {Roo.HtmlEditorCore} this
26046          * @param {String} html
26047          */
26048         push: true,
26049         
26050         /**
26051          * @event editorevent
26052          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26053          * @param {Roo.HtmlEditorCore} this
26054          */
26055         editorevent: true
26056         
26057     });
26058     
26059     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26060     
26061     // defaults : white / black...
26062     this.applyBlacklists();
26063     
26064     
26065     
26066 };
26067
26068
26069 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26070
26071
26072      /**
26073      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26074      */
26075     
26076     owner : false,
26077     
26078      /**
26079      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26080      *                        Roo.resizable.
26081      */
26082     resizable : false,
26083      /**
26084      * @cfg {Number} height (in pixels)
26085      */   
26086     height: 300,
26087    /**
26088      * @cfg {Number} width (in pixels)
26089      */   
26090     width: 500,
26091     
26092     /**
26093      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26094      * 
26095      */
26096     stylesheets: false,
26097     
26098     /**
26099      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26100      */
26101     allowComments: false,
26102     // id of frame..
26103     frameId: false,
26104     
26105     // private properties
26106     validationEvent : false,
26107     deferHeight: true,
26108     initialized : false,
26109     activated : false,
26110     sourceEditMode : false,
26111     onFocus : Roo.emptyFn,
26112     iframePad:3,
26113     hideMode:'offsets',
26114     
26115     clearUp: true,
26116     
26117     // blacklist + whitelisted elements..
26118     black: false,
26119     white: false,
26120      
26121     bodyCls : '',
26122
26123     /**
26124      * Protected method that will not generally be called directly. It
26125      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26126      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26127      */
26128     getDocMarkup : function(){
26129         // body styles..
26130         var st = '';
26131         
26132         // inherit styels from page...?? 
26133         if (this.stylesheets === false) {
26134             
26135             Roo.get(document.head).select('style').each(function(node) {
26136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26137             });
26138             
26139             Roo.get(document.head).select('link').each(function(node) { 
26140                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26141             });
26142             
26143         } else if (!this.stylesheets.length) {
26144                 // simple..
26145                 st = '<style type="text/css">' +
26146                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26147                    '</style>';
26148         } else {
26149             for (var i in this.stylesheets) { 
26150                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26151             }
26152             
26153         }
26154         
26155         st +=  '<style type="text/css">' +
26156             'IMG { cursor: pointer } ' +
26157         '</style>';
26158
26159         var cls = 'roo-htmleditor-body';
26160         
26161         if(this.bodyCls.length){
26162             cls += ' ' + this.bodyCls;
26163         }
26164         
26165         return '<html><head>' + st  +
26166             //<style type="text/css">' +
26167             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26168             //'</style>' +
26169             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26170     },
26171
26172     // private
26173     onRender : function(ct, position)
26174     {
26175         var _t = this;
26176         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26177         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26178         
26179         
26180         this.el.dom.style.border = '0 none';
26181         this.el.dom.setAttribute('tabIndex', -1);
26182         this.el.addClass('x-hidden hide');
26183         
26184         
26185         
26186         if(Roo.isIE){ // fix IE 1px bogus margin
26187             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26188         }
26189        
26190         
26191         this.frameId = Roo.id();
26192         
26193          
26194         
26195         var iframe = this.owner.wrap.createChild({
26196             tag: 'iframe',
26197             cls: 'form-control', // bootstrap..
26198             id: this.frameId,
26199             name: this.frameId,
26200             frameBorder : 'no',
26201             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26202         }, this.el
26203         );
26204         
26205         
26206         this.iframe = iframe.dom;
26207
26208          this.assignDocWin();
26209         
26210         this.doc.designMode = 'on';
26211        
26212         this.doc.open();
26213         this.doc.write(this.getDocMarkup());
26214         this.doc.close();
26215
26216         
26217         var task = { // must defer to wait for browser to be ready
26218             run : function(){
26219                 //console.log("run task?" + this.doc.readyState);
26220                 this.assignDocWin();
26221                 if(this.doc.body || this.doc.readyState == 'complete'){
26222                     try {
26223                         this.doc.designMode="on";
26224                     } catch (e) {
26225                         return;
26226                     }
26227                     Roo.TaskMgr.stop(task);
26228                     this.initEditor.defer(10, this);
26229                 }
26230             },
26231             interval : 10,
26232             duration: 10000,
26233             scope: this
26234         };
26235         Roo.TaskMgr.start(task);
26236
26237     },
26238
26239     // private
26240     onResize : function(w, h)
26241     {
26242          Roo.log('resize: ' +w + ',' + h );
26243         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26244         if(!this.iframe){
26245             return;
26246         }
26247         if(typeof w == 'number'){
26248             
26249             this.iframe.style.width = w + 'px';
26250         }
26251         if(typeof h == 'number'){
26252             
26253             this.iframe.style.height = h + 'px';
26254             if(this.doc){
26255                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26256             }
26257         }
26258         
26259     },
26260
26261     /**
26262      * Toggles the editor between standard and source edit mode.
26263      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26264      */
26265     toggleSourceEdit : function(sourceEditMode){
26266         
26267         this.sourceEditMode = sourceEditMode === true;
26268         
26269         if(this.sourceEditMode){
26270  
26271             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26272             
26273         }else{
26274             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26275             //this.iframe.className = '';
26276             this.deferFocus();
26277         }
26278         //this.setSize(this.owner.wrap.getSize());
26279         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26280     },
26281
26282     
26283   
26284
26285     /**
26286      * Protected method that will not generally be called directly. If you need/want
26287      * custom HTML cleanup, this is the method you should override.
26288      * @param {String} html The HTML to be cleaned
26289      * return {String} The cleaned HTML
26290      */
26291     cleanHtml : function(html){
26292         html = String(html);
26293         if(html.length > 5){
26294             if(Roo.isSafari){ // strip safari nonsense
26295                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26296             }
26297         }
26298         if(html == '&nbsp;'){
26299             html = '';
26300         }
26301         return html;
26302     },
26303
26304     /**
26305      * HTML Editor -> Textarea
26306      * Protected method that will not generally be called directly. Syncs the contents
26307      * of the editor iframe with the textarea.
26308      */
26309     syncValue : function(){
26310         if(this.initialized){
26311             var bd = (this.doc.body || this.doc.documentElement);
26312             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26313             var html = bd.innerHTML;
26314             if(Roo.isSafari){
26315                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26316                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26317                 if(m && m[1]){
26318                     html = '<div style="'+m[0]+'">' + html + '</div>';
26319                 }
26320             }
26321             html = this.cleanHtml(html);
26322             // fix up the special chars.. normaly like back quotes in word...
26323             // however we do not want to do this with chinese..
26324             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26325                 
26326                 var cc = match.charCodeAt();
26327
26328                 // Get the character value, handling surrogate pairs
26329                 if (match.length == 2) {
26330                     // It's a surrogate pair, calculate the Unicode code point
26331                     var high = match.charCodeAt(0) - 0xD800;
26332                     var low  = match.charCodeAt(1) - 0xDC00;
26333                     cc = (high * 0x400) + low + 0x10000;
26334                 }  else if (
26335                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26336                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26337                     (cc >= 0xf900 && cc < 0xfb00 )
26338                 ) {
26339                         return match;
26340                 }  
26341          
26342                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26343                 return "&#" + cc + ";";
26344                 
26345                 
26346             });
26347             
26348             
26349              
26350             if(this.owner.fireEvent('beforesync', this, html) !== false){
26351                 this.el.dom.value = html;
26352                 this.owner.fireEvent('sync', this, html);
26353             }
26354         }
26355     },
26356
26357     /**
26358      * Protected method that will not generally be called directly. Pushes the value of the textarea
26359      * into the iframe editor.
26360      */
26361     pushValue : function(){
26362         if(this.initialized){
26363             var v = this.el.dom.value.trim();
26364             
26365 //            if(v.length < 1){
26366 //                v = '&#160;';
26367 //            }
26368             
26369             if(this.owner.fireEvent('beforepush', this, v) !== false){
26370                 var d = (this.doc.body || this.doc.documentElement);
26371                 d.innerHTML = v;
26372                 this.cleanUpPaste();
26373                 this.el.dom.value = d.innerHTML;
26374                 this.owner.fireEvent('push', this, v);
26375             }
26376         }
26377     },
26378
26379     // private
26380     deferFocus : function(){
26381         this.focus.defer(10, this);
26382     },
26383
26384     // doc'ed in Field
26385     focus : function(){
26386         if(this.win && !this.sourceEditMode){
26387             this.win.focus();
26388         }else{
26389             this.el.focus();
26390         }
26391     },
26392     
26393     assignDocWin: function()
26394     {
26395         var iframe = this.iframe;
26396         
26397          if(Roo.isIE){
26398             this.doc = iframe.contentWindow.document;
26399             this.win = iframe.contentWindow;
26400         } else {
26401 //            if (!Roo.get(this.frameId)) {
26402 //                return;
26403 //            }
26404 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26405 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26406             
26407             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26408                 return;
26409             }
26410             
26411             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26412             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26413         }
26414     },
26415     
26416     // private
26417     initEditor : function(){
26418         //console.log("INIT EDITOR");
26419         this.assignDocWin();
26420         
26421         
26422         
26423         this.doc.designMode="on";
26424         this.doc.open();
26425         this.doc.write(this.getDocMarkup());
26426         this.doc.close();
26427         
26428         var dbody = (this.doc.body || this.doc.documentElement);
26429         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26430         // this copies styles from the containing element into thsi one..
26431         // not sure why we need all of this..
26432         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26433         
26434         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26435         //ss['background-attachment'] = 'fixed'; // w3c
26436         dbody.bgProperties = 'fixed'; // ie
26437         //Roo.DomHelper.applyStyles(dbody, ss);
26438         Roo.EventManager.on(this.doc, {
26439             //'mousedown': this.onEditorEvent,
26440             'mouseup': this.onEditorEvent,
26441             'dblclick': this.onEditorEvent,
26442             'click': this.onEditorEvent,
26443             'keyup': this.onEditorEvent,
26444             buffer:100,
26445             scope: this
26446         });
26447         if(Roo.isGecko){
26448             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26449         }
26450         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26451             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26452         }
26453         this.initialized = true;
26454
26455         this.owner.fireEvent('initialize', this);
26456         this.pushValue();
26457     },
26458
26459     // private
26460     onDestroy : function(){
26461         
26462         
26463         
26464         if(this.rendered){
26465             
26466             //for (var i =0; i < this.toolbars.length;i++) {
26467             //    // fixme - ask toolbars for heights?
26468             //    this.toolbars[i].onDestroy();
26469            // }
26470             
26471             //this.wrap.dom.innerHTML = '';
26472             //this.wrap.remove();
26473         }
26474     },
26475
26476     // private
26477     onFirstFocus : function(){
26478         
26479         this.assignDocWin();
26480         
26481         
26482         this.activated = true;
26483          
26484     
26485         if(Roo.isGecko){ // prevent silly gecko errors
26486             this.win.focus();
26487             var s = this.win.getSelection();
26488             if(!s.focusNode || s.focusNode.nodeType != 3){
26489                 var r = s.getRangeAt(0);
26490                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26491                 r.collapse(true);
26492                 this.deferFocus();
26493             }
26494             try{
26495                 this.execCmd('useCSS', true);
26496                 this.execCmd('styleWithCSS', false);
26497             }catch(e){}
26498         }
26499         this.owner.fireEvent('activate', this);
26500     },
26501
26502     // private
26503     adjustFont: function(btn){
26504         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26505         //if(Roo.isSafari){ // safari
26506         //    adjust *= 2;
26507        // }
26508         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26509         if(Roo.isSafari){ // safari
26510             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26511             v =  (v < 10) ? 10 : v;
26512             v =  (v > 48) ? 48 : v;
26513             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26514             
26515         }
26516         
26517         
26518         v = Math.max(1, v+adjust);
26519         
26520         this.execCmd('FontSize', v  );
26521     },
26522
26523     onEditorEvent : function(e)
26524     {
26525         this.owner.fireEvent('editorevent', this, e);
26526       //  this.updateToolbar();
26527         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26528     },
26529
26530     insertTag : function(tg)
26531     {
26532         // could be a bit smarter... -> wrap the current selected tRoo..
26533         if (tg.toLowerCase() == 'span' ||
26534             tg.toLowerCase() == 'code' ||
26535             tg.toLowerCase() == 'sup' ||
26536             tg.toLowerCase() == 'sub' 
26537             ) {
26538             
26539             range = this.createRange(this.getSelection());
26540             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26541             wrappingNode.appendChild(range.extractContents());
26542             range.insertNode(wrappingNode);
26543
26544             return;
26545             
26546             
26547             
26548         }
26549         this.execCmd("formatblock",   tg);
26550         
26551     },
26552     
26553     insertText : function(txt)
26554     {
26555         
26556         
26557         var range = this.createRange();
26558         range.deleteContents();
26559                //alert(Sender.getAttribute('label'));
26560                
26561         range.insertNode(this.doc.createTextNode(txt));
26562     } ,
26563     
26564      
26565
26566     /**
26567      * Executes a Midas editor command on the editor document and performs necessary focus and
26568      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26569      * @param {String} cmd The Midas command
26570      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26571      */
26572     relayCmd : function(cmd, value){
26573         this.win.focus();
26574         this.execCmd(cmd, value);
26575         this.owner.fireEvent('editorevent', this);
26576         //this.updateToolbar();
26577         this.owner.deferFocus();
26578     },
26579
26580     /**
26581      * Executes a Midas editor command directly on the editor document.
26582      * For visual commands, you should use {@link #relayCmd} instead.
26583      * <b>This should only be called after the editor is initialized.</b>
26584      * @param {String} cmd The Midas command
26585      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26586      */
26587     execCmd : function(cmd, value){
26588         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26589         this.syncValue();
26590     },
26591  
26592  
26593    
26594     /**
26595      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26596      * to insert tRoo.
26597      * @param {String} text | dom node.. 
26598      */
26599     insertAtCursor : function(text)
26600     {
26601         
26602         if(!this.activated){
26603             return;
26604         }
26605         /*
26606         if(Roo.isIE){
26607             this.win.focus();
26608             var r = this.doc.selection.createRange();
26609             if(r){
26610                 r.collapse(true);
26611                 r.pasteHTML(text);
26612                 this.syncValue();
26613                 this.deferFocus();
26614             
26615             }
26616             return;
26617         }
26618         */
26619         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26620             this.win.focus();
26621             
26622             
26623             // from jquery ui (MIT licenced)
26624             var range, node;
26625             var win = this.win;
26626             
26627             if (win.getSelection && win.getSelection().getRangeAt) {
26628                 range = win.getSelection().getRangeAt(0);
26629                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26630                 range.insertNode(node);
26631             } else if (win.document.selection && win.document.selection.createRange) {
26632                 // no firefox support
26633                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26634                 win.document.selection.createRange().pasteHTML(txt);
26635             } else {
26636                 // no firefox support
26637                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26638                 this.execCmd('InsertHTML', txt);
26639             } 
26640             
26641             this.syncValue();
26642             
26643             this.deferFocus();
26644         }
26645     },
26646  // private
26647     mozKeyPress : function(e){
26648         if(e.ctrlKey){
26649             var c = e.getCharCode(), cmd;
26650           
26651             if(c > 0){
26652                 c = String.fromCharCode(c).toLowerCase();
26653                 switch(c){
26654                     case 'b':
26655                         cmd = 'bold';
26656                         break;
26657                     case 'i':
26658                         cmd = 'italic';
26659                         break;
26660                     
26661                     case 'u':
26662                         cmd = 'underline';
26663                         break;
26664                     
26665                     case 'v':
26666                         this.cleanUpPaste.defer(100, this);
26667                         return;
26668                         
26669                 }
26670                 if(cmd){
26671                     this.win.focus();
26672                     this.execCmd(cmd);
26673                     this.deferFocus();
26674                     e.preventDefault();
26675                 }
26676                 
26677             }
26678         }
26679     },
26680
26681     // private
26682     fixKeys : function(){ // load time branching for fastest keydown performance
26683         if(Roo.isIE){
26684             return function(e){
26685                 var k = e.getKey(), r;
26686                 if(k == e.TAB){
26687                     e.stopEvent();
26688                     r = this.doc.selection.createRange();
26689                     if(r){
26690                         r.collapse(true);
26691                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26692                         this.deferFocus();
26693                     }
26694                     return;
26695                 }
26696                 
26697                 if(k == e.ENTER){
26698                     r = this.doc.selection.createRange();
26699                     if(r){
26700                         var target = r.parentElement();
26701                         if(!target || target.tagName.toLowerCase() != 'li'){
26702                             e.stopEvent();
26703                             r.pasteHTML('<br />');
26704                             r.collapse(false);
26705                             r.select();
26706                         }
26707                     }
26708                 }
26709                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26710                     this.cleanUpPaste.defer(100, this);
26711                     return;
26712                 }
26713                 
26714                 
26715             };
26716         }else if(Roo.isOpera){
26717             return function(e){
26718                 var k = e.getKey();
26719                 if(k == e.TAB){
26720                     e.stopEvent();
26721                     this.win.focus();
26722                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26723                     this.deferFocus();
26724                 }
26725                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26726                     this.cleanUpPaste.defer(100, this);
26727                     return;
26728                 }
26729                 
26730             };
26731         }else if(Roo.isSafari){
26732             return function(e){
26733                 var k = e.getKey();
26734                 
26735                 if(k == e.TAB){
26736                     e.stopEvent();
26737                     this.execCmd('InsertText','\t');
26738                     this.deferFocus();
26739                     return;
26740                 }
26741                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26742                     this.cleanUpPaste.defer(100, this);
26743                     return;
26744                 }
26745                 
26746              };
26747         }
26748     }(),
26749     
26750     getAllAncestors: function()
26751     {
26752         var p = this.getSelectedNode();
26753         var a = [];
26754         if (!p) {
26755             a.push(p); // push blank onto stack..
26756             p = this.getParentElement();
26757         }
26758         
26759         
26760         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26761             a.push(p);
26762             p = p.parentNode;
26763         }
26764         a.push(this.doc.body);
26765         return a;
26766     },
26767     lastSel : false,
26768     lastSelNode : false,
26769     
26770     
26771     getSelection : function() 
26772     {
26773         this.assignDocWin();
26774         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26775     },
26776     
26777     getSelectedNode: function() 
26778     {
26779         // this may only work on Gecko!!!
26780         
26781         // should we cache this!!!!
26782         
26783         
26784         
26785          
26786         var range = this.createRange(this.getSelection()).cloneRange();
26787         
26788         if (Roo.isIE) {
26789             var parent = range.parentElement();
26790             while (true) {
26791                 var testRange = range.duplicate();
26792                 testRange.moveToElementText(parent);
26793                 if (testRange.inRange(range)) {
26794                     break;
26795                 }
26796                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26797                     break;
26798                 }
26799                 parent = parent.parentElement;
26800             }
26801             return parent;
26802         }
26803         
26804         // is ancestor a text element.
26805         var ac =  range.commonAncestorContainer;
26806         if (ac.nodeType == 3) {
26807             ac = ac.parentNode;
26808         }
26809         
26810         var ar = ac.childNodes;
26811          
26812         var nodes = [];
26813         var other_nodes = [];
26814         var has_other_nodes = false;
26815         for (var i=0;i<ar.length;i++) {
26816             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26817                 continue;
26818             }
26819             // fullly contained node.
26820             
26821             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26822                 nodes.push(ar[i]);
26823                 continue;
26824             }
26825             
26826             // probably selected..
26827             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26828                 other_nodes.push(ar[i]);
26829                 continue;
26830             }
26831             // outer..
26832             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26833                 continue;
26834             }
26835             
26836             
26837             has_other_nodes = true;
26838         }
26839         if (!nodes.length && other_nodes.length) {
26840             nodes= other_nodes;
26841         }
26842         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26843             return false;
26844         }
26845         
26846         return nodes[0];
26847     },
26848     createRange: function(sel)
26849     {
26850         // this has strange effects when using with 
26851         // top toolbar - not sure if it's a great idea.
26852         //this.editor.contentWindow.focus();
26853         if (typeof sel != "undefined") {
26854             try {
26855                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26856             } catch(e) {
26857                 return this.doc.createRange();
26858             }
26859         } else {
26860             return this.doc.createRange();
26861         }
26862     },
26863     getParentElement: function()
26864     {
26865         
26866         this.assignDocWin();
26867         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26868         
26869         var range = this.createRange(sel);
26870          
26871         try {
26872             var p = range.commonAncestorContainer;
26873             while (p.nodeType == 3) { // text node
26874                 p = p.parentNode;
26875             }
26876             return p;
26877         } catch (e) {
26878             return null;
26879         }
26880     
26881     },
26882     /***
26883      *
26884      * Range intersection.. the hard stuff...
26885      *  '-1' = before
26886      *  '0' = hits..
26887      *  '1' = after.
26888      *         [ -- selected range --- ]
26889      *   [fail]                        [fail]
26890      *
26891      *    basically..
26892      *      if end is before start or  hits it. fail.
26893      *      if start is after end or hits it fail.
26894      *
26895      *   if either hits (but other is outside. - then it's not 
26896      *   
26897      *    
26898      **/
26899     
26900     
26901     // @see http://www.thismuchiknow.co.uk/?p=64.
26902     rangeIntersectsNode : function(range, node)
26903     {
26904         var nodeRange = node.ownerDocument.createRange();
26905         try {
26906             nodeRange.selectNode(node);
26907         } catch (e) {
26908             nodeRange.selectNodeContents(node);
26909         }
26910     
26911         var rangeStartRange = range.cloneRange();
26912         rangeStartRange.collapse(true);
26913     
26914         var rangeEndRange = range.cloneRange();
26915         rangeEndRange.collapse(false);
26916     
26917         var nodeStartRange = nodeRange.cloneRange();
26918         nodeStartRange.collapse(true);
26919     
26920         var nodeEndRange = nodeRange.cloneRange();
26921         nodeEndRange.collapse(false);
26922     
26923         return rangeStartRange.compareBoundaryPoints(
26924                  Range.START_TO_START, nodeEndRange) == -1 &&
26925                rangeEndRange.compareBoundaryPoints(
26926                  Range.START_TO_START, nodeStartRange) == 1;
26927         
26928          
26929     },
26930     rangeCompareNode : function(range, node)
26931     {
26932         var nodeRange = node.ownerDocument.createRange();
26933         try {
26934             nodeRange.selectNode(node);
26935         } catch (e) {
26936             nodeRange.selectNodeContents(node);
26937         }
26938         
26939         
26940         range.collapse(true);
26941     
26942         nodeRange.collapse(true);
26943      
26944         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26945         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26946          
26947         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26948         
26949         var nodeIsBefore   =  ss == 1;
26950         var nodeIsAfter    = ee == -1;
26951         
26952         if (nodeIsBefore && nodeIsAfter) {
26953             return 0; // outer
26954         }
26955         if (!nodeIsBefore && nodeIsAfter) {
26956             return 1; //right trailed.
26957         }
26958         
26959         if (nodeIsBefore && !nodeIsAfter) {
26960             return 2;  // left trailed.
26961         }
26962         // fully contined.
26963         return 3;
26964     },
26965
26966     // private? - in a new class?
26967     cleanUpPaste :  function()
26968     {
26969         // cleans up the whole document..
26970         Roo.log('cleanuppaste');
26971         
26972         this.cleanUpChildren(this.doc.body);
26973         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26974         if (clean != this.doc.body.innerHTML) {
26975             this.doc.body.innerHTML = clean;
26976         }
26977         
26978     },
26979     
26980     cleanWordChars : function(input) {// change the chars to hex code
26981         var he = Roo.HtmlEditorCore;
26982         
26983         var output = input;
26984         Roo.each(he.swapCodes, function(sw) { 
26985             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26986             
26987             output = output.replace(swapper, sw[1]);
26988         });
26989         
26990         return output;
26991     },
26992     
26993     
26994     cleanUpChildren : function (n)
26995     {
26996         if (!n.childNodes.length) {
26997             return;
26998         }
26999         for (var i = n.childNodes.length-1; i > -1 ; i--) {
27000            this.cleanUpChild(n.childNodes[i]);
27001         }
27002     },
27003     
27004     
27005         
27006     
27007     cleanUpChild : function (node)
27008     {
27009         var ed = this;
27010         //console.log(node);
27011         if (node.nodeName == "#text") {
27012             // clean up silly Windows -- stuff?
27013             return; 
27014         }
27015         if (node.nodeName == "#comment") {
27016             if (!this.allowComments) {
27017                 node.parentNode.removeChild(node);
27018             }
27019             // clean up silly Windows -- stuff?
27020             return; 
27021         }
27022         var lcname = node.tagName.toLowerCase();
27023         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27024         // whitelist of tags..
27025         
27026         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27027             // remove node.
27028             node.parentNode.removeChild(node);
27029             return;
27030             
27031         }
27032         
27033         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27034         
27035         // spans with no attributes - just remove them..
27036         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27037             remove_keep_children = true;
27038         }
27039         
27040         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27041         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27042         
27043         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27044         //    remove_keep_children = true;
27045         //}
27046         
27047         if (remove_keep_children) {
27048             this.cleanUpChildren(node);
27049             // inserts everything just before this node...
27050             while (node.childNodes.length) {
27051                 var cn = node.childNodes[0];
27052                 node.removeChild(cn);
27053                 node.parentNode.insertBefore(cn, node);
27054             }
27055             node.parentNode.removeChild(node);
27056             return;
27057         }
27058         
27059         if (!node.attributes || !node.attributes.length) {
27060             
27061           
27062             
27063             
27064             this.cleanUpChildren(node);
27065             return;
27066         }
27067         
27068         function cleanAttr(n,v)
27069         {
27070             
27071             if (v.match(/^\./) || v.match(/^\//)) {
27072                 return;
27073             }
27074             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27075                 return;
27076             }
27077             if (v.match(/^#/)) {
27078                 return;
27079             }
27080             if (v.match(/^\{/)) { // allow template editing.
27081                 return;
27082             }
27083 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27084             node.removeAttribute(n);
27085             
27086         }
27087         
27088         var cwhite = this.cwhite;
27089         var cblack = this.cblack;
27090             
27091         function cleanStyle(n,v)
27092         {
27093             if (v.match(/expression/)) { //XSS?? should we even bother..
27094                 node.removeAttribute(n);
27095                 return;
27096             }
27097             
27098             var parts = v.split(/;/);
27099             var clean = [];
27100             
27101             Roo.each(parts, function(p) {
27102                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27103                 if (!p.length) {
27104                     return true;
27105                 }
27106                 var l = p.split(':').shift().replace(/\s+/g,'');
27107                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27108                 
27109                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27110 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27111                     //node.removeAttribute(n);
27112                     return true;
27113                 }
27114                 //Roo.log()
27115                 // only allow 'c whitelisted system attributes'
27116                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27117 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27118                     //node.removeAttribute(n);
27119                     return true;
27120                 }
27121                 
27122                 
27123                  
27124                 
27125                 clean.push(p);
27126                 return true;
27127             });
27128             if (clean.length) { 
27129                 node.setAttribute(n, clean.join(';'));
27130             } else {
27131                 node.removeAttribute(n);
27132             }
27133             
27134         }
27135         
27136         
27137         for (var i = node.attributes.length-1; i > -1 ; i--) {
27138             var a = node.attributes[i];
27139             //console.log(a);
27140             
27141             if (a.name.toLowerCase().substr(0,2)=='on')  {
27142                 node.removeAttribute(a.name);
27143                 continue;
27144             }
27145             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27146                 node.removeAttribute(a.name);
27147                 continue;
27148             }
27149             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27150                 cleanAttr(a.name,a.value); // fixme..
27151                 continue;
27152             }
27153             if (a.name == 'style') {
27154                 cleanStyle(a.name,a.value);
27155                 continue;
27156             }
27157             /// clean up MS crap..
27158             // tecnically this should be a list of valid class'es..
27159             
27160             
27161             if (a.name == 'class') {
27162                 if (a.value.match(/^Mso/)) {
27163                     node.removeAttribute('class');
27164                 }
27165                 
27166                 if (a.value.match(/^body$/)) {
27167                     node.removeAttribute('class');
27168                 }
27169                 continue;
27170             }
27171             
27172             // style cleanup!?
27173             // class cleanup?
27174             
27175         }
27176         
27177         
27178         this.cleanUpChildren(node);
27179         
27180         
27181     },
27182     
27183     /**
27184      * Clean up MS wordisms...
27185      */
27186     cleanWord : function(node)
27187     {
27188         if (!node) {
27189             this.cleanWord(this.doc.body);
27190             return;
27191         }
27192         
27193         if(
27194                 node.nodeName == 'SPAN' &&
27195                 !node.hasAttributes() &&
27196                 node.childNodes.length == 1 &&
27197                 node.firstChild.nodeName == "#text"  
27198         ) {
27199             var textNode = node.firstChild;
27200             node.removeChild(textNode);
27201             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27202                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27203             }
27204             node.parentNode.insertBefore(textNode, node);
27205             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27206                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27207             }
27208             node.parentNode.removeChild(node);
27209         }
27210         
27211         if (node.nodeName == "#text") {
27212             // clean up silly Windows -- stuff?
27213             return; 
27214         }
27215         if (node.nodeName == "#comment") {
27216             node.parentNode.removeChild(node);
27217             // clean up silly Windows -- stuff?
27218             return; 
27219         }
27220         
27221         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27222             node.parentNode.removeChild(node);
27223             return;
27224         }
27225         //Roo.log(node.tagName);
27226         // remove - but keep children..
27227         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27228             //Roo.log('-- removed');
27229             while (node.childNodes.length) {
27230                 var cn = node.childNodes[0];
27231                 node.removeChild(cn);
27232                 node.parentNode.insertBefore(cn, node);
27233                 // move node to parent - and clean it..
27234                 this.cleanWord(cn);
27235             }
27236             node.parentNode.removeChild(node);
27237             /// no need to iterate chidlren = it's got none..
27238             //this.iterateChildren(node, this.cleanWord);
27239             return;
27240         }
27241         // clean styles
27242         if (node.className.length) {
27243             
27244             var cn = node.className.split(/\W+/);
27245             var cna = [];
27246             Roo.each(cn, function(cls) {
27247                 if (cls.match(/Mso[a-zA-Z]+/)) {
27248                     return;
27249                 }
27250                 cna.push(cls);
27251             });
27252             node.className = cna.length ? cna.join(' ') : '';
27253             if (!cna.length) {
27254                 node.removeAttribute("class");
27255             }
27256         }
27257         
27258         if (node.hasAttribute("lang")) {
27259             node.removeAttribute("lang");
27260         }
27261         
27262         if (node.hasAttribute("style")) {
27263             
27264             var styles = node.getAttribute("style").split(";");
27265             var nstyle = [];
27266             Roo.each(styles, function(s) {
27267                 if (!s.match(/:/)) {
27268                     return;
27269                 }
27270                 var kv = s.split(":");
27271                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27272                     return;
27273                 }
27274                 // what ever is left... we allow.
27275                 nstyle.push(s);
27276             });
27277             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27278             if (!nstyle.length) {
27279                 node.removeAttribute('style');
27280             }
27281         }
27282         this.iterateChildren(node, this.cleanWord);
27283         
27284         
27285         
27286     },
27287     /**
27288      * iterateChildren of a Node, calling fn each time, using this as the scole..
27289      * @param {DomNode} node node to iterate children of.
27290      * @param {Function} fn method of this class to call on each item.
27291      */
27292     iterateChildren : function(node, fn)
27293     {
27294         if (!node.childNodes.length) {
27295                 return;
27296         }
27297         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27298            fn.call(this, node.childNodes[i])
27299         }
27300     },
27301     
27302     
27303     /**
27304      * cleanTableWidths.
27305      *
27306      * Quite often pasting from word etc.. results in tables with column and widths.
27307      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27308      *
27309      */
27310     cleanTableWidths : function(node)
27311     {
27312          
27313          
27314         if (!node) {
27315             this.cleanTableWidths(this.doc.body);
27316             return;
27317         }
27318         
27319         // ignore list...
27320         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27321             return; 
27322         }
27323         Roo.log(node.tagName);
27324         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27325             this.iterateChildren(node, this.cleanTableWidths);
27326             return;
27327         }
27328         if (node.hasAttribute('width')) {
27329             node.removeAttribute('width');
27330         }
27331         
27332          
27333         if (node.hasAttribute("style")) {
27334             // pretty basic...
27335             
27336             var styles = node.getAttribute("style").split(";");
27337             var nstyle = [];
27338             Roo.each(styles, function(s) {
27339                 if (!s.match(/:/)) {
27340                     return;
27341                 }
27342                 var kv = s.split(":");
27343                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27344                     return;
27345                 }
27346                 // what ever is left... we allow.
27347                 nstyle.push(s);
27348             });
27349             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27350             if (!nstyle.length) {
27351                 node.removeAttribute('style');
27352             }
27353         }
27354         
27355         this.iterateChildren(node, this.cleanTableWidths);
27356         
27357         
27358     },
27359     
27360     
27361     
27362     
27363     domToHTML : function(currentElement, depth, nopadtext) {
27364         
27365         depth = depth || 0;
27366         nopadtext = nopadtext || false;
27367     
27368         if (!currentElement) {
27369             return this.domToHTML(this.doc.body);
27370         }
27371         
27372         //Roo.log(currentElement);
27373         var j;
27374         var allText = false;
27375         var nodeName = currentElement.nodeName;
27376         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27377         
27378         if  (nodeName == '#text') {
27379             
27380             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27381         }
27382         
27383         
27384         var ret = '';
27385         if (nodeName != 'BODY') {
27386              
27387             var i = 0;
27388             // Prints the node tagName, such as <A>, <IMG>, etc
27389             if (tagName) {
27390                 var attr = [];
27391                 for(i = 0; i < currentElement.attributes.length;i++) {
27392                     // quoting?
27393                     var aname = currentElement.attributes.item(i).name;
27394                     if (!currentElement.attributes.item(i).value.length) {
27395                         continue;
27396                     }
27397                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27398                 }
27399                 
27400                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27401             } 
27402             else {
27403                 
27404                 // eack
27405             }
27406         } else {
27407             tagName = false;
27408         }
27409         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27410             return ret;
27411         }
27412         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27413             nopadtext = true;
27414         }
27415         
27416         
27417         // Traverse the tree
27418         i = 0;
27419         var currentElementChild = currentElement.childNodes.item(i);
27420         var allText = true;
27421         var innerHTML  = '';
27422         lastnode = '';
27423         while (currentElementChild) {
27424             // Formatting code (indent the tree so it looks nice on the screen)
27425             var nopad = nopadtext;
27426             if (lastnode == 'SPAN') {
27427                 nopad  = true;
27428             }
27429             // text
27430             if  (currentElementChild.nodeName == '#text') {
27431                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27432                 toadd = nopadtext ? toadd : toadd.trim();
27433                 if (!nopad && toadd.length > 80) {
27434                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27435                 }
27436                 innerHTML  += toadd;
27437                 
27438                 i++;
27439                 currentElementChild = currentElement.childNodes.item(i);
27440                 lastNode = '';
27441                 continue;
27442             }
27443             allText = false;
27444             
27445             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27446                 
27447             // Recursively traverse the tree structure of the child node
27448             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27449             lastnode = currentElementChild.nodeName;
27450             i++;
27451             currentElementChild=currentElement.childNodes.item(i);
27452         }
27453         
27454         ret += innerHTML;
27455         
27456         if (!allText) {
27457                 // The remaining code is mostly for formatting the tree
27458             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27459         }
27460         
27461         
27462         if (tagName) {
27463             ret+= "</"+tagName+">";
27464         }
27465         return ret;
27466         
27467     },
27468         
27469     applyBlacklists : function()
27470     {
27471         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27472         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27473         
27474         this.white = [];
27475         this.black = [];
27476         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27477             if (b.indexOf(tag) > -1) {
27478                 return;
27479             }
27480             this.white.push(tag);
27481             
27482         }, this);
27483         
27484         Roo.each(w, function(tag) {
27485             if (b.indexOf(tag) > -1) {
27486                 return;
27487             }
27488             if (this.white.indexOf(tag) > -1) {
27489                 return;
27490             }
27491             this.white.push(tag);
27492             
27493         }, this);
27494         
27495         
27496         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27497             if (w.indexOf(tag) > -1) {
27498                 return;
27499             }
27500             this.black.push(tag);
27501             
27502         }, this);
27503         
27504         Roo.each(b, function(tag) {
27505             if (w.indexOf(tag) > -1) {
27506                 return;
27507             }
27508             if (this.black.indexOf(tag) > -1) {
27509                 return;
27510             }
27511             this.black.push(tag);
27512             
27513         }, this);
27514         
27515         
27516         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27517         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27518         
27519         this.cwhite = [];
27520         this.cblack = [];
27521         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27522             if (b.indexOf(tag) > -1) {
27523                 return;
27524             }
27525             this.cwhite.push(tag);
27526             
27527         }, this);
27528         
27529         Roo.each(w, function(tag) {
27530             if (b.indexOf(tag) > -1) {
27531                 return;
27532             }
27533             if (this.cwhite.indexOf(tag) > -1) {
27534                 return;
27535             }
27536             this.cwhite.push(tag);
27537             
27538         }, this);
27539         
27540         
27541         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27542             if (w.indexOf(tag) > -1) {
27543                 return;
27544             }
27545             this.cblack.push(tag);
27546             
27547         }, this);
27548         
27549         Roo.each(b, function(tag) {
27550             if (w.indexOf(tag) > -1) {
27551                 return;
27552             }
27553             if (this.cblack.indexOf(tag) > -1) {
27554                 return;
27555             }
27556             this.cblack.push(tag);
27557             
27558         }, this);
27559     },
27560     
27561     setStylesheets : function(stylesheets)
27562     {
27563         if(typeof(stylesheets) == 'string'){
27564             Roo.get(this.iframe.contentDocument.head).createChild({
27565                 tag : 'link',
27566                 rel : 'stylesheet',
27567                 type : 'text/css',
27568                 href : stylesheets
27569             });
27570             
27571             return;
27572         }
27573         var _this = this;
27574      
27575         Roo.each(stylesheets, function(s) {
27576             if(!s.length){
27577                 return;
27578             }
27579             
27580             Roo.get(_this.iframe.contentDocument.head).createChild({
27581                 tag : 'link',
27582                 rel : 'stylesheet',
27583                 type : 'text/css',
27584                 href : s
27585             });
27586         });
27587
27588         
27589     },
27590     
27591     removeStylesheets : function()
27592     {
27593         var _this = this;
27594         
27595         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27596             s.remove();
27597         });
27598     },
27599     
27600     setStyle : function(style)
27601     {
27602         Roo.get(this.iframe.contentDocument.head).createChild({
27603             tag : 'style',
27604             type : 'text/css',
27605             html : style
27606         });
27607
27608         return;
27609     }
27610     
27611     // hide stuff that is not compatible
27612     /**
27613      * @event blur
27614      * @hide
27615      */
27616     /**
27617      * @event change
27618      * @hide
27619      */
27620     /**
27621      * @event focus
27622      * @hide
27623      */
27624     /**
27625      * @event specialkey
27626      * @hide
27627      */
27628     /**
27629      * @cfg {String} fieldClass @hide
27630      */
27631     /**
27632      * @cfg {String} focusClass @hide
27633      */
27634     /**
27635      * @cfg {String} autoCreate @hide
27636      */
27637     /**
27638      * @cfg {String} inputType @hide
27639      */
27640     /**
27641      * @cfg {String} invalidClass @hide
27642      */
27643     /**
27644      * @cfg {String} invalidText @hide
27645      */
27646     /**
27647      * @cfg {String} msgFx @hide
27648      */
27649     /**
27650      * @cfg {String} validateOnBlur @hide
27651      */
27652 });
27653
27654 Roo.HtmlEditorCore.white = [
27655         'area', 'br', 'img', 'input', 'hr', 'wbr',
27656         
27657        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27658        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27659        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27660        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27661        'table',   'ul',         'xmp', 
27662        
27663        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27664       'thead',   'tr', 
27665      
27666       'dir', 'menu', 'ol', 'ul', 'dl',
27667        
27668       'embed',  'object'
27669 ];
27670
27671
27672 Roo.HtmlEditorCore.black = [
27673     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27674         'applet', // 
27675         'base',   'basefont', 'bgsound', 'blink',  'body', 
27676         'frame',  'frameset', 'head',    'html',   'ilayer', 
27677         'iframe', 'layer',  'link',     'meta',    'object',   
27678         'script', 'style' ,'title',  'xml' // clean later..
27679 ];
27680 Roo.HtmlEditorCore.clean = [
27681     'script', 'style', 'title', 'xml'
27682 ];
27683 Roo.HtmlEditorCore.remove = [
27684     'font'
27685 ];
27686 // attributes..
27687
27688 Roo.HtmlEditorCore.ablack = [
27689     'on'
27690 ];
27691     
27692 Roo.HtmlEditorCore.aclean = [ 
27693     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27694 ];
27695
27696 // protocols..
27697 Roo.HtmlEditorCore.pwhite= [
27698         'http',  'https',  'mailto'
27699 ];
27700
27701 // white listed style attributes.
27702 Roo.HtmlEditorCore.cwhite= [
27703       //  'text-align', /// default is to allow most things..
27704       
27705          
27706 //        'font-size'//??
27707 ];
27708
27709 // black listed style attributes.
27710 Roo.HtmlEditorCore.cblack= [
27711       //  'font-size' -- this can be set by the project 
27712 ];
27713
27714
27715 Roo.HtmlEditorCore.swapCodes   =[ 
27716     [    8211, "&#8211;" ], 
27717     [    8212, "&#8212;" ], 
27718     [    8216,  "'" ],  
27719     [    8217, "'" ],  
27720     [    8220, '"' ],  
27721     [    8221, '"' ],  
27722     [    8226, "*" ],  
27723     [    8230, "..." ]
27724 ]; 
27725
27726     /*
27727  * - LGPL
27728  *
27729  * HtmlEditor
27730  * 
27731  */
27732
27733 /**
27734  * @class Roo.bootstrap.form.HtmlEditor
27735  * @extends Roo.bootstrap.form.TextArea
27736  * Bootstrap HtmlEditor class
27737
27738  * @constructor
27739  * Create a new HtmlEditor
27740  * @param {Object} config The config object
27741  */
27742
27743 Roo.bootstrap.form.HtmlEditor = function(config){
27744     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27745     if (!this.toolbars) {
27746         this.toolbars = [];
27747     }
27748     
27749     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27750     this.addEvents({
27751             /**
27752              * @event initialize
27753              * Fires when the editor is fully initialized (including the iframe)
27754              * @param {HtmlEditor} this
27755              */
27756             initialize: true,
27757             /**
27758              * @event activate
27759              * Fires when the editor is first receives the focus. Any insertion must wait
27760              * until after this event.
27761              * @param {HtmlEditor} this
27762              */
27763             activate: true,
27764              /**
27765              * @event beforesync
27766              * Fires before the textarea is updated with content from the editor iframe. Return false
27767              * to cancel the sync.
27768              * @param {HtmlEditor} this
27769              * @param {String} html
27770              */
27771             beforesync: true,
27772              /**
27773              * @event beforepush
27774              * Fires before the iframe editor is updated with content from the textarea. Return false
27775              * to cancel the push.
27776              * @param {HtmlEditor} this
27777              * @param {String} html
27778              */
27779             beforepush: true,
27780              /**
27781              * @event sync
27782              * Fires when the textarea is updated with content from the editor iframe.
27783              * @param {HtmlEditor} this
27784              * @param {String} html
27785              */
27786             sync: true,
27787              /**
27788              * @event push
27789              * Fires when the iframe editor is updated with content from the textarea.
27790              * @param {HtmlEditor} this
27791              * @param {String} html
27792              */
27793             push: true,
27794              /**
27795              * @event editmodechange
27796              * Fires when the editor switches edit modes
27797              * @param {HtmlEditor} this
27798              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27799              */
27800             editmodechange: true,
27801             /**
27802              * @event editorevent
27803              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27804              * @param {HtmlEditor} this
27805              */
27806             editorevent: true,
27807             /**
27808              * @event firstfocus
27809              * Fires when on first focus - needed by toolbars..
27810              * @param {HtmlEditor} this
27811              */
27812             firstfocus: true,
27813             /**
27814              * @event autosave
27815              * Auto save the htmlEditor value as a file into Events
27816              * @param {HtmlEditor} this
27817              */
27818             autosave: true,
27819             /**
27820              * @event savedpreview
27821              * preview the saved version of htmlEditor
27822              * @param {HtmlEditor} this
27823              */
27824             savedpreview: true
27825         });
27826 };
27827
27828
27829 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27830     
27831     
27832       /**
27833      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27834      */
27835     toolbars : false,
27836     
27837      /**
27838     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27839     */
27840     btns : [],
27841    
27842      /**
27843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27844      *                        Roo.resizable.
27845      */
27846     resizable : false,
27847      /**
27848      * @cfg {Number} height (in pixels)
27849      */   
27850     height: 300,
27851    /**
27852      * @cfg {Number} width (in pixels)
27853      */   
27854     width: false,
27855     
27856     /**
27857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27858      * 
27859      */
27860     stylesheets: false,
27861     
27862     // id of frame..
27863     frameId: false,
27864     
27865     // private properties
27866     validationEvent : false,
27867     deferHeight: true,
27868     initialized : false,
27869     activated : false,
27870     
27871     onFocus : Roo.emptyFn,
27872     iframePad:3,
27873     hideMode:'offsets',
27874     
27875     tbContainer : false,
27876     
27877     bodyCls : '',
27878     
27879     toolbarContainer :function() {
27880         return this.wrap.select('.x-html-editor-tb',true).first();
27881     },
27882
27883     /**
27884      * Protected method that will not generally be called directly. It
27885      * is called when the editor creates its toolbar. Override this method if you need to
27886      * add custom toolbar buttons.
27887      * @param {HtmlEditor} editor
27888      */
27889     createToolbar : function(){
27890         Roo.log('renewing');
27891         Roo.log("create toolbars");
27892         
27893         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27894         this.toolbars[0].render(this.toolbarContainer());
27895         
27896         return;
27897         
27898 //        if (!editor.toolbars || !editor.toolbars.length) {
27899 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27900 //        }
27901 //        
27902 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27903 //            editor.toolbars[i] = Roo.factory(
27904 //                    typeof(editor.toolbars[i]) == 'string' ?
27905 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27906 //                Roo.bootstrap.form.HtmlEditor);
27907 //            editor.toolbars[i].init(editor);
27908 //        }
27909     },
27910
27911      
27912     // private
27913     onRender : function(ct, position)
27914     {
27915        // Roo.log("Call onRender: " + this.xtype);
27916         var _t = this;
27917         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27918       
27919         this.wrap = this.inputEl().wrap({
27920             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27921         });
27922         
27923         this.editorcore.onRender(ct, position);
27924          
27925         if (this.resizable) {
27926             this.resizeEl = new Roo.Resizable(this.wrap, {
27927                 pinned : true,
27928                 wrap: true,
27929                 dynamic : true,
27930                 minHeight : this.height,
27931                 height: this.height,
27932                 handles : this.resizable,
27933                 width: this.width,
27934                 listeners : {
27935                     resize : function(r, w, h) {
27936                         _t.onResize(w,h); // -something
27937                     }
27938                 }
27939             });
27940             
27941         }
27942         this.createToolbar(this);
27943        
27944         
27945         if(!this.width && this.resizable){
27946             this.setSize(this.wrap.getSize());
27947         }
27948         if (this.resizeEl) {
27949             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27950             // should trigger onReize..
27951         }
27952         
27953     },
27954
27955     // private
27956     onResize : function(w, h)
27957     {
27958         Roo.log('resize: ' +w + ',' + h );
27959         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27960         var ew = false;
27961         var eh = false;
27962         
27963         if(this.inputEl() ){
27964             if(typeof w == 'number'){
27965                 var aw = w - this.wrap.getFrameWidth('lr');
27966                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27967                 ew = aw;
27968             }
27969             if(typeof h == 'number'){
27970                  var tbh = -11;  // fixme it needs to tool bar size!
27971                 for (var i =0; i < this.toolbars.length;i++) {
27972                     // fixme - ask toolbars for heights?
27973                     tbh += this.toolbars[i].el.getHeight();
27974                     //if (this.toolbars[i].footer) {
27975                     //    tbh += this.toolbars[i].footer.el.getHeight();
27976                     //}
27977                 }
27978               
27979                 
27980                 
27981                 
27982                 
27983                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27984                 ah -= 5; // knock a few pixes off for look..
27985                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27986                 var eh = ah;
27987             }
27988         }
27989         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27990         this.editorcore.onResize(ew,eh);
27991         
27992     },
27993
27994     /**
27995      * Toggles the editor between standard and source edit mode.
27996      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27997      */
27998     toggleSourceEdit : function(sourceEditMode)
27999     {
28000         this.editorcore.toggleSourceEdit(sourceEditMode);
28001         
28002         if(this.editorcore.sourceEditMode){
28003             Roo.log('editor - showing textarea');
28004             
28005 //            Roo.log('in');
28006 //            Roo.log(this.syncValue());
28007             this.syncValue();
28008             this.inputEl().removeClass(['hide', 'x-hidden']);
28009             this.inputEl().dom.removeAttribute('tabIndex');
28010             this.inputEl().focus();
28011         }else{
28012             Roo.log('editor - hiding textarea');
28013 //            Roo.log('out')
28014 //            Roo.log(this.pushValue()); 
28015             this.pushValue();
28016             
28017             this.inputEl().addClass(['hide', 'x-hidden']);
28018             this.inputEl().dom.setAttribute('tabIndex', -1);
28019             //this.deferFocus();
28020         }
28021          
28022         if(this.resizable){
28023             this.setSize(this.wrap.getSize());
28024         }
28025         
28026         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28027     },
28028  
28029     // private (for BoxComponent)
28030     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28031
28032     // private (for BoxComponent)
28033     getResizeEl : function(){
28034         return this.wrap;
28035     },
28036
28037     // private (for BoxComponent)
28038     getPositionEl : function(){
28039         return this.wrap;
28040     },
28041
28042     // private
28043     initEvents : function(){
28044         this.originalValue = this.getValue();
28045     },
28046
28047 //    /**
28048 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28049 //     * @method
28050 //     */
28051 //    markInvalid : Roo.emptyFn,
28052 //    /**
28053 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28054 //     * @method
28055 //     */
28056 //    clearInvalid : Roo.emptyFn,
28057
28058     setValue : function(v){
28059         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28060         this.editorcore.pushValue();
28061     },
28062
28063      
28064     // private
28065     deferFocus : function(){
28066         this.focus.defer(10, this);
28067     },
28068
28069     // doc'ed in Field
28070     focus : function(){
28071         this.editorcore.focus();
28072         
28073     },
28074       
28075
28076     // private
28077     onDestroy : function(){
28078         
28079         
28080         
28081         if(this.rendered){
28082             
28083             for (var i =0; i < this.toolbars.length;i++) {
28084                 // fixme - ask toolbars for heights?
28085                 this.toolbars[i].onDestroy();
28086             }
28087             
28088             this.wrap.dom.innerHTML = '';
28089             this.wrap.remove();
28090         }
28091     },
28092
28093     // private
28094     onFirstFocus : function(){
28095         //Roo.log("onFirstFocus");
28096         this.editorcore.onFirstFocus();
28097          for (var i =0; i < this.toolbars.length;i++) {
28098             this.toolbars[i].onFirstFocus();
28099         }
28100         
28101     },
28102     
28103     // private
28104     syncValue : function()
28105     {   
28106         this.editorcore.syncValue();
28107     },
28108     
28109     pushValue : function()
28110     {   
28111         this.editorcore.pushValue();
28112     }
28113      
28114     
28115     // hide stuff that is not compatible
28116     /**
28117      * @event blur
28118      * @hide
28119      */
28120     /**
28121      * @event change
28122      * @hide
28123      */
28124     /**
28125      * @event focus
28126      * @hide
28127      */
28128     /**
28129      * @event specialkey
28130      * @hide
28131      */
28132     /**
28133      * @cfg {String} fieldClass @hide
28134      */
28135     /**
28136      * @cfg {String} focusClass @hide
28137      */
28138     /**
28139      * @cfg {String} autoCreate @hide
28140      */
28141     /**
28142      * @cfg {String} inputType @hide
28143      */
28144      
28145     /**
28146      * @cfg {String} invalidText @hide
28147      */
28148     /**
28149      * @cfg {String} msgFx @hide
28150      */
28151     /**
28152      * @cfg {String} validateOnBlur @hide
28153      */
28154 });
28155  
28156     
28157    
28158    
28159    
28160       
28161 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28162 /**
28163  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28164  * @parent Roo.bootstrap.form.HtmlEditor
28165  * @extends Roo.bootstrap.nav.Simplebar
28166  * Basic Toolbar
28167  * 
28168  * @example
28169  * Usage:
28170  *
28171  new Roo.bootstrap.form.HtmlEditor({
28172     ....
28173     toolbars : [
28174         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28175             disable : { fonts: 1 , format: 1, ..., ... , ...],
28176             btns : [ .... ]
28177         })
28178     }
28179      
28180  * 
28181  * @cfg {Object} disable List of elements to disable..
28182  * @cfg {Array} btns List of additional buttons.
28183  * 
28184  * 
28185  * NEEDS Extra CSS? 
28186  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28187  */
28188  
28189 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28190 {
28191     
28192     Roo.apply(this, config);
28193     
28194     // default disabled, based on 'good practice'..
28195     this.disable = this.disable || {};
28196     Roo.applyIf(this.disable, {
28197         fontSize : true,
28198         colors : true,
28199         specialElements : true
28200     });
28201     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28202     
28203     this.editor = config.editor;
28204     this.editorcore = config.editor.editorcore;
28205     
28206     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28207     
28208     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28209     // dont call parent... till later.
28210 }
28211 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28212      
28213     bar : true,
28214     
28215     editor : false,
28216     editorcore : false,
28217     
28218     
28219     formats : [
28220         "p" ,  
28221         "h1","h2","h3","h4","h5","h6", 
28222         "pre", "code", 
28223         "abbr", "acronym", "address", "cite", "samp", "var",
28224         'div','span'
28225     ],
28226     
28227     onRender : function(ct, position)
28228     {
28229        // Roo.log("Call onRender: " + this.xtype);
28230         
28231        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28232        Roo.log(this.el);
28233        this.el.dom.style.marginBottom = '0';
28234        var _this = this;
28235        var editorcore = this.editorcore;
28236        var editor= this.editor;
28237        
28238        var children = [];
28239        var btn = function(id,cmd , toggle, handler, html){
28240        
28241             var  event = toggle ? 'toggle' : 'click';
28242        
28243             var a = {
28244                 size : 'sm',
28245                 xtype: 'Button',
28246                 xns: Roo.bootstrap,
28247                 //glyphicon : id,
28248                 fa: id,
28249                 cmd : id || cmd,
28250                 enableToggle:toggle !== false,
28251                 html : html || '',
28252                 pressed : toggle ? false : null,
28253                 listeners : {}
28254             };
28255             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28256                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28257             };
28258             children.push(a);
28259             return a;
28260        }
28261        
28262     //    var cb_box = function...
28263         
28264         var style = {
28265                 xtype: 'Button',
28266                 size : 'sm',
28267                 xns: Roo.bootstrap,
28268                 fa : 'font',
28269                 //html : 'submit'
28270                 menu : {
28271                     xtype: 'Menu',
28272                     xns: Roo.bootstrap,
28273                     items:  []
28274                 }
28275         };
28276         Roo.each(this.formats, function(f) {
28277             style.menu.items.push({
28278                 xtype :'MenuItem',
28279                 xns: Roo.bootstrap,
28280                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28281                 tagname : f,
28282                 listeners : {
28283                     click : function()
28284                     {
28285                         editorcore.insertTag(this.tagname);
28286                         editor.focus();
28287                     }
28288                 }
28289                 
28290             });
28291         });
28292         children.push(style);   
28293         
28294         btn('bold',false,true);
28295         btn('italic',false,true);
28296         btn('align-left', 'justifyleft',true);
28297         btn('align-center', 'justifycenter',true);
28298         btn('align-right' , 'justifyright',true);
28299         btn('link', false, false, function(btn) {
28300             //Roo.log("create link?");
28301             var url = prompt(this.createLinkText, this.defaultLinkValue);
28302             if(url && url != 'http:/'+'/'){
28303                 this.editorcore.relayCmd('createlink', url);
28304             }
28305         }),
28306         btn('list','insertunorderedlist',true);
28307         btn('pencil', false,true, function(btn){
28308                 Roo.log(this);
28309                 this.toggleSourceEdit(btn.pressed);
28310         });
28311         
28312         if (this.editor.btns.length > 0) {
28313             for (var i = 0; i<this.editor.btns.length; i++) {
28314                 children.push(this.editor.btns[i]);
28315             }
28316         }
28317         
28318         /*
28319         var cog = {
28320                 xtype: 'Button',
28321                 size : 'sm',
28322                 xns: Roo.bootstrap,
28323                 glyphicon : 'cog',
28324                 //html : 'submit'
28325                 menu : {
28326                     xtype: 'Menu',
28327                     xns: Roo.bootstrap,
28328                     items:  []
28329                 }
28330         };
28331         
28332         cog.menu.items.push({
28333             xtype :'MenuItem',
28334             xns: Roo.bootstrap,
28335             html : Clean styles,
28336             tagname : f,
28337             listeners : {
28338                 click : function()
28339                 {
28340                     editorcore.insertTag(this.tagname);
28341                     editor.focus();
28342                 }
28343             }
28344             
28345         });
28346        */
28347         
28348          
28349        this.xtype = 'NavSimplebar';
28350         
28351         for(var i=0;i< children.length;i++) {
28352             
28353             this.buttons.add(this.addxtypeChild(children[i]));
28354             
28355         }
28356         
28357         editor.on('editorevent', this.updateToolbar, this);
28358     },
28359     onBtnClick : function(id)
28360     {
28361        this.editorcore.relayCmd(id);
28362        this.editorcore.focus();
28363     },
28364     
28365     /**
28366      * Protected method that will not generally be called directly. It triggers
28367      * a toolbar update by reading the markup state of the current selection in the editor.
28368      */
28369     updateToolbar: function(){
28370
28371         if(!this.editorcore.activated){
28372             this.editor.onFirstFocus(); // is this neeed?
28373             return;
28374         }
28375
28376         var btns = this.buttons; 
28377         var doc = this.editorcore.doc;
28378         btns.get('bold').setActive(doc.queryCommandState('bold'));
28379         btns.get('italic').setActive(doc.queryCommandState('italic'));
28380         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28381         
28382         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28383         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28384         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28385         
28386         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28387         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28388          /*
28389         
28390         var ans = this.editorcore.getAllAncestors();
28391         if (this.formatCombo) {
28392             
28393             
28394             var store = this.formatCombo.store;
28395             this.formatCombo.setValue("");
28396             for (var i =0; i < ans.length;i++) {
28397                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28398                     // select it..
28399                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28400                     break;
28401                 }
28402             }
28403         }
28404         
28405         
28406         
28407         // hides menus... - so this cant be on a menu...
28408         Roo.bootstrap.MenuMgr.hideAll();
28409         */
28410         Roo.bootstrap.menu.Manager.hideAll();
28411         //this.editorsyncValue();
28412     },
28413     onFirstFocus: function() {
28414         this.buttons.each(function(item){
28415            item.enable();
28416         });
28417     },
28418     toggleSourceEdit : function(sourceEditMode){
28419         
28420           
28421         if(sourceEditMode){
28422             Roo.log("disabling buttons");
28423            this.buttons.each( function(item){
28424                 if(item.cmd != 'pencil'){
28425                     item.disable();
28426                 }
28427             });
28428           
28429         }else{
28430             Roo.log("enabling buttons");
28431             if(this.editorcore.initialized){
28432                 this.buttons.each( function(item){
28433                     item.enable();
28434                 });
28435             }
28436             
28437         }
28438         Roo.log("calling toggole on editor");
28439         // tell the editor that it's been pressed..
28440         this.editor.toggleSourceEdit(sourceEditMode);
28441        
28442     }
28443 });
28444
28445
28446
28447
28448  
28449 /*
28450  * - LGPL
28451  */
28452
28453 /**
28454  * @class Roo.bootstrap.form.Markdown
28455  * @extends Roo.bootstrap.form.TextArea
28456  * Bootstrap Showdown editable area
28457  * @cfg {string} content
28458  * 
28459  * @constructor
28460  * Create a new Showdown
28461  */
28462
28463 Roo.bootstrap.form.Markdown = function(config){
28464     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28465    
28466 };
28467
28468 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28469     
28470     editing :false,
28471     
28472     initEvents : function()
28473     {
28474         
28475         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28476         this.markdownEl = this.el.createChild({
28477             cls : 'roo-markdown-area'
28478         });
28479         this.inputEl().addClass('d-none');
28480         if (this.getValue() == '') {
28481             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28482             
28483         } else {
28484             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28485         }
28486         this.markdownEl.on('click', this.toggleTextEdit, this);
28487         this.on('blur', this.toggleTextEdit, this);
28488         this.on('specialkey', this.resizeTextArea, this);
28489     },
28490     
28491     toggleTextEdit : function()
28492     {
28493         var sh = this.markdownEl.getHeight();
28494         this.inputEl().addClass('d-none');
28495         this.markdownEl.addClass('d-none');
28496         if (!this.editing) {
28497             // show editor?
28498             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28499             this.inputEl().removeClass('d-none');
28500             this.inputEl().focus();
28501             this.editing = true;
28502             return;
28503         }
28504         // show showdown...
28505         this.updateMarkdown();
28506         this.markdownEl.removeClass('d-none');
28507         this.editing = false;
28508         return;
28509     },
28510     updateMarkdown : function()
28511     {
28512         if (this.getValue() == '') {
28513             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28514             return;
28515         }
28516  
28517         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28518     },
28519     
28520     resizeTextArea: function () {
28521         
28522         var sh = 100;
28523         Roo.log([sh, this.getValue().split("\n").length * 30]);
28524         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28525     },
28526     setValue : function(val)
28527     {
28528         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28529         if (!this.editing) {
28530             this.updateMarkdown();
28531         }
28532         
28533     },
28534     focus : function()
28535     {
28536         if (!this.editing) {
28537             this.toggleTextEdit();
28538         }
28539         
28540     }
28541
28542
28543 });/*
28544  * Based on:
28545  * Ext JS Library 1.1.1
28546  * Copyright(c) 2006-2007, Ext JS, LLC.
28547  *
28548  * Originally Released Under LGPL - original licence link has changed is not relivant.
28549  *
28550  * Fork - LGPL
28551  * <script type="text/javascript">
28552  */
28553  
28554 /**
28555  * @class Roo.bootstrap.PagingToolbar
28556  * @extends Roo.bootstrap.nav.Simplebar
28557  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28558  * @constructor
28559  * Create a new PagingToolbar
28560  * @param {Object} config The config object
28561  * @param {Roo.data.Store} store
28562  */
28563 Roo.bootstrap.PagingToolbar = function(config)
28564 {
28565     // old args format still supported... - xtype is prefered..
28566         // created from xtype...
28567     
28568     this.ds = config.dataSource;
28569     
28570     if (config.store && !this.ds) {
28571         this.store= Roo.factory(config.store, Roo.data);
28572         this.ds = this.store;
28573         this.ds.xmodule = this.xmodule || false;
28574     }
28575     
28576     this.toolbarItems = [];
28577     if (config.items) {
28578         this.toolbarItems = config.items;
28579     }
28580     
28581     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28582     
28583     this.cursor = 0;
28584     
28585     if (this.ds) { 
28586         this.bind(this.ds);
28587     }
28588     
28589     if (Roo.bootstrap.version == 4) {
28590         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28591     } else {
28592         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28593     }
28594     
28595 };
28596
28597 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28598     /**
28599      * @cfg {Roo.bootstrap.Button} buttons[]
28600      * Buttons for the toolbar
28601      */
28602      /**
28603      * @cfg {Roo.data.Store} store
28604      * The underlying data store providing the paged data
28605      */
28606     /**
28607      * @cfg {String/HTMLElement/Element} container
28608      * container The id or element that will contain the toolbar
28609      */
28610     /**
28611      * @cfg {Boolean} displayInfo
28612      * True to display the displayMsg (defaults to false)
28613      */
28614     /**
28615      * @cfg {Number} pageSize
28616      * The number of records to display per page (defaults to 20)
28617      */
28618     pageSize: 20,
28619     /**
28620      * @cfg {String} displayMsg
28621      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28622      */
28623     displayMsg : 'Displaying {0} - {1} of {2}',
28624     /**
28625      * @cfg {String} emptyMsg
28626      * The message to display when no records are found (defaults to "No data to display")
28627      */
28628     emptyMsg : 'No data to display',
28629     /**
28630      * Customizable piece of the default paging text (defaults to "Page")
28631      * @type String
28632      */
28633     beforePageText : "Page",
28634     /**
28635      * Customizable piece of the default paging text (defaults to "of %0")
28636      * @type String
28637      */
28638     afterPageText : "of {0}",
28639     /**
28640      * Customizable piece of the default paging text (defaults to "First Page")
28641      * @type String
28642      */
28643     firstText : "First Page",
28644     /**
28645      * Customizable piece of the default paging text (defaults to "Previous Page")
28646      * @type String
28647      */
28648     prevText : "Previous Page",
28649     /**
28650      * Customizable piece of the default paging text (defaults to "Next Page")
28651      * @type String
28652      */
28653     nextText : "Next Page",
28654     /**
28655      * Customizable piece of the default paging text (defaults to "Last Page")
28656      * @type String
28657      */
28658     lastText : "Last Page",
28659     /**
28660      * Customizable piece of the default paging text (defaults to "Refresh")
28661      * @type String
28662      */
28663     refreshText : "Refresh",
28664
28665     buttons : false,
28666     // private
28667     onRender : function(ct, position) 
28668     {
28669         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28670         this.navgroup.parentId = this.id;
28671         this.navgroup.onRender(this.el, null);
28672         // add the buttons to the navgroup
28673         
28674         if(this.displayInfo){
28675             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28676             this.displayEl = this.el.select('.x-paging-info', true).first();
28677 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28678 //            this.displayEl = navel.el.select('span',true).first();
28679         }
28680         
28681         var _this = this;
28682         
28683         if(this.buttons){
28684             Roo.each(_this.buttons, function(e){ // this might need to use render????
28685                Roo.factory(e).render(_this.el);
28686             });
28687         }
28688             
28689         Roo.each(_this.toolbarItems, function(e) {
28690             _this.navgroup.addItem(e);
28691         });
28692         
28693         
28694         this.first = this.navgroup.addItem({
28695             tooltip: this.firstText,
28696             cls: "prev btn-outline-secondary",
28697             html : ' <i class="fa fa-step-backward"></i>',
28698             disabled: true,
28699             preventDefault: true,
28700             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28701         });
28702         
28703         this.prev =  this.navgroup.addItem({
28704             tooltip: this.prevText,
28705             cls: "prev btn-outline-secondary",
28706             html : ' <i class="fa fa-backward"></i>',
28707             disabled: true,
28708             preventDefault: true,
28709             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28710         });
28711     //this.addSeparator();
28712         
28713         
28714         var field = this.navgroup.addItem( {
28715             tagtype : 'span',
28716             cls : 'x-paging-position  btn-outline-secondary',
28717              disabled: true,
28718             html : this.beforePageText  +
28719                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28720                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28721          } ); //?? escaped?
28722         
28723         this.field = field.el.select('input', true).first();
28724         this.field.on("keydown", this.onPagingKeydown, this);
28725         this.field.on("focus", function(){this.dom.select();});
28726     
28727     
28728         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28729         //this.field.setHeight(18);
28730         //this.addSeparator();
28731         this.next = this.navgroup.addItem({
28732             tooltip: this.nextText,
28733             cls: "next btn-outline-secondary",
28734             html : ' <i class="fa fa-forward"></i>',
28735             disabled: true,
28736             preventDefault: true,
28737             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28738         });
28739         this.last = this.navgroup.addItem({
28740             tooltip: this.lastText,
28741             html : ' <i class="fa fa-step-forward"></i>',
28742             cls: "next btn-outline-secondary",
28743             disabled: true,
28744             preventDefault: true,
28745             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28746         });
28747     //this.addSeparator();
28748         this.loading = this.navgroup.addItem({
28749             tooltip: this.refreshText,
28750             cls: "btn-outline-secondary",
28751             html : ' <i class="fa fa-refresh"></i>',
28752             preventDefault: true,
28753             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28754         });
28755         
28756     },
28757
28758     // private
28759     updateInfo : function(){
28760         if(this.displayEl){
28761             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28762             var msg = count == 0 ?
28763                 this.emptyMsg :
28764                 String.format(
28765                     this.displayMsg,
28766                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28767                 );
28768             this.displayEl.update(msg);
28769         }
28770     },
28771
28772     // private
28773     onLoad : function(ds, r, o)
28774     {
28775         this.cursor = o.params && o.params.start ? o.params.start : 0;
28776         
28777         var d = this.getPageData(),
28778             ap = d.activePage,
28779             ps = d.pages;
28780         
28781         
28782         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28783         this.field.dom.value = ap;
28784         this.first.setDisabled(ap == 1);
28785         this.prev.setDisabled(ap == 1);
28786         this.next.setDisabled(ap == ps);
28787         this.last.setDisabled(ap == ps);
28788         this.loading.enable();
28789         this.updateInfo();
28790     },
28791
28792     // private
28793     getPageData : function(){
28794         var total = this.ds.getTotalCount();
28795         return {
28796             total : total,
28797             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28798             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28799         };
28800     },
28801
28802     // private
28803     onLoadError : function(proxy, o){
28804         this.loading.enable();
28805         if (this.ds.events.loadexception.listeners.length  < 2) {
28806             // nothing has been assigned to loadexception except this...
28807             // so 
28808             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28809
28810         }
28811     },
28812
28813     // private
28814     onPagingKeydown : function(e){
28815         var k = e.getKey();
28816         var d = this.getPageData();
28817         if(k == e.RETURN){
28818             var v = this.field.dom.value, pageNum;
28819             if(!v || isNaN(pageNum = parseInt(v, 10))){
28820                 this.field.dom.value = d.activePage;
28821                 return;
28822             }
28823             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28824             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28825             e.stopEvent();
28826         }
28827         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))
28828         {
28829           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28830           this.field.dom.value = pageNum;
28831           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28832           e.stopEvent();
28833         }
28834         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28835         {
28836           var v = this.field.dom.value, pageNum; 
28837           var increment = (e.shiftKey) ? 10 : 1;
28838           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28839                 increment *= -1;
28840           }
28841           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28842             this.field.dom.value = d.activePage;
28843             return;
28844           }
28845           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28846           {
28847             this.field.dom.value = parseInt(v, 10) + increment;
28848             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28849             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28850           }
28851           e.stopEvent();
28852         }
28853     },
28854
28855     // private
28856     beforeLoad : function(){
28857         if(this.loading){
28858             this.loading.disable();
28859         }
28860     },
28861
28862     // private
28863     onClick : function(which){
28864         
28865         var ds = this.ds;
28866         if (!ds) {
28867             return;
28868         }
28869         
28870         switch(which){
28871             case "first":
28872                 ds.load({params:{start: 0, limit: this.pageSize}});
28873             break;
28874             case "prev":
28875                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28876             break;
28877             case "next":
28878                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28879             break;
28880             case "last":
28881                 var total = ds.getTotalCount();
28882                 var extra = total % this.pageSize;
28883                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28884                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28885             break;
28886             case "refresh":
28887                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28888             break;
28889         }
28890     },
28891
28892     /**
28893      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28894      * @param {Roo.data.Store} store The data store to unbind
28895      */
28896     unbind : function(ds){
28897         ds.un("beforeload", this.beforeLoad, this);
28898         ds.un("load", this.onLoad, this);
28899         ds.un("loadexception", this.onLoadError, this);
28900         ds.un("remove", this.updateInfo, this);
28901         ds.un("add", this.updateInfo, this);
28902         this.ds = undefined;
28903     },
28904
28905     /**
28906      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28907      * @param {Roo.data.Store} store The data store to bind
28908      */
28909     bind : function(ds){
28910         ds.on("beforeload", this.beforeLoad, this);
28911         ds.on("load", this.onLoad, this);
28912         ds.on("loadexception", this.onLoadError, this);
28913         ds.on("remove", this.updateInfo, this);
28914         ds.on("add", this.updateInfo, this);
28915         this.ds = ds;
28916     }
28917 });/*
28918  * - LGPL
28919  *
28920  * element
28921  * 
28922  */
28923
28924 /**
28925  * @class Roo.bootstrap.MessageBar
28926  * @extends Roo.bootstrap.Component
28927  * Bootstrap MessageBar class
28928  * @cfg {String} html contents of the MessageBar
28929  * @cfg {String} weight (info | success | warning | danger) default info
28930  * @cfg {String} beforeClass insert the bar before the given class
28931  * @cfg {Boolean} closable (true | false) default false
28932  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28933  * 
28934  * @constructor
28935  * Create a new Element
28936  * @param {Object} config The config object
28937  */
28938
28939 Roo.bootstrap.MessageBar = function(config){
28940     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28941 };
28942
28943 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28944     
28945     html: '',
28946     weight: 'info',
28947     closable: false,
28948     fixed: false,
28949     beforeClass: 'bootstrap-sticky-wrap',
28950     
28951     getAutoCreate : function(){
28952         
28953         var cfg = {
28954             tag: 'div',
28955             cls: 'alert alert-dismissable alert-' + this.weight,
28956             cn: [
28957                 {
28958                     tag: 'span',
28959                     cls: 'message',
28960                     html: this.html || ''
28961                 }
28962             ]
28963         };
28964         
28965         if(this.fixed){
28966             cfg.cls += ' alert-messages-fixed';
28967         }
28968         
28969         if(this.closable){
28970             cfg.cn.push({
28971                 tag: 'button',
28972                 cls: 'close',
28973                 html: 'x'
28974             });
28975         }
28976         
28977         return cfg;
28978     },
28979     
28980     onRender : function(ct, position)
28981     {
28982         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28983         
28984         if(!this.el){
28985             var cfg = Roo.apply({},  this.getAutoCreate());
28986             cfg.id = Roo.id();
28987             
28988             if (this.cls) {
28989                 cfg.cls += ' ' + this.cls;
28990             }
28991             if (this.style) {
28992                 cfg.style = this.style;
28993             }
28994             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28995             
28996             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28997         }
28998         
28999         this.el.select('>button.close').on('click', this.hide, this);
29000         
29001     },
29002     
29003     show : function()
29004     {
29005         if (!this.rendered) {
29006             this.render();
29007         }
29008         
29009         this.el.show();
29010         
29011         this.fireEvent('show', this);
29012         
29013     },
29014     
29015     hide : function()
29016     {
29017         if (!this.rendered) {
29018             this.render();
29019         }
29020         
29021         this.el.hide();
29022         
29023         this.fireEvent('hide', this);
29024     },
29025     
29026     update : function()
29027     {
29028 //        var e = this.el.dom.firstChild;
29029 //        
29030 //        if(this.closable){
29031 //            e = e.nextSibling;
29032 //        }
29033 //        
29034 //        e.data = this.html || '';
29035
29036         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29037     }
29038    
29039 });
29040
29041  
29042
29043      /*
29044  * - LGPL
29045  *
29046  * Graph
29047  * 
29048  */
29049
29050
29051 /**
29052  * @class Roo.bootstrap.Graph
29053  * @extends Roo.bootstrap.Component
29054  * Bootstrap Graph class
29055 > Prameters
29056  -sm {number} sm 4
29057  -md {number} md 5
29058  @cfg {String} graphtype  bar | vbar | pie
29059  @cfg {number} g_x coodinator | centre x (pie)
29060  @cfg {number} g_y coodinator | centre y (pie)
29061  @cfg {number} g_r radius (pie)
29062  @cfg {number} g_height height of the chart (respected by all elements in the set)
29063  @cfg {number} g_width width of the chart (respected by all elements in the set)
29064  @cfg {Object} title The title of the chart
29065     
29066  -{Array}  values
29067  -opts (object) options for the chart 
29068      o {
29069      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29070      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29071      o vgutter (number)
29072      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.
29073      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29074      o to
29075      o stretch (boolean)
29076      o }
29077  -opts (object) options for the pie
29078      o{
29079      o cut
29080      o startAngle (number)
29081      o endAngle (number)
29082      } 
29083  *
29084  * @constructor
29085  * Create a new Input
29086  * @param {Object} config The config object
29087  */
29088
29089 Roo.bootstrap.Graph = function(config){
29090     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29091     
29092     this.addEvents({
29093         // img events
29094         /**
29095          * @event click
29096          * The img click event for the img.
29097          * @param {Roo.EventObject} e
29098          */
29099         "click" : true
29100     });
29101 };
29102
29103 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29104     
29105     sm: 4,
29106     md: 5,
29107     graphtype: 'bar',
29108     g_height: 250,
29109     g_width: 400,
29110     g_x: 50,
29111     g_y: 50,
29112     g_r: 30,
29113     opts:{
29114         //g_colors: this.colors,
29115         g_type: 'soft',
29116         g_gutter: '20%'
29117
29118     },
29119     title : false,
29120
29121     getAutoCreate : function(){
29122         
29123         var cfg = {
29124             tag: 'div',
29125             html : null
29126         };
29127         
29128         
29129         return  cfg;
29130     },
29131
29132     onRender : function(ct,position){
29133         
29134         
29135         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29136         
29137         if (typeof(Raphael) == 'undefined') {
29138             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29139             return;
29140         }
29141         
29142         this.raphael = Raphael(this.el.dom);
29143         
29144                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29145                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29146                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29147                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29148                 /*
29149                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29150                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29151                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29152                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29153                 
29154                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29155                 r.barchart(330, 10, 300, 220, data1);
29156                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29157                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29158                 */
29159                 
29160                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29161                 // r.barchart(30, 30, 560, 250,  xdata, {
29162                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29163                 //     axis : "0 0 1 1",
29164                 //     axisxlabels :  xdata
29165                 //     //yvalues : cols,
29166                    
29167                 // });
29168 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29169 //        
29170 //        this.load(null,xdata,{
29171 //                axis : "0 0 1 1",
29172 //                axisxlabels :  xdata
29173 //                });
29174
29175     },
29176
29177     load : function(graphtype,xdata,opts)
29178     {
29179         this.raphael.clear();
29180         if(!graphtype) {
29181             graphtype = this.graphtype;
29182         }
29183         if(!opts){
29184             opts = this.opts;
29185         }
29186         var r = this.raphael,
29187             fin = function () {
29188                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29189             },
29190             fout = function () {
29191                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29192             },
29193             pfin = function() {
29194                 this.sector.stop();
29195                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29196
29197                 if (this.label) {
29198                     this.label[0].stop();
29199                     this.label[0].attr({ r: 7.5 });
29200                     this.label[1].attr({ "font-weight": 800 });
29201                 }
29202             },
29203             pfout = function() {
29204                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29205
29206                 if (this.label) {
29207                     this.label[0].animate({ r: 5 }, 500, "bounce");
29208                     this.label[1].attr({ "font-weight": 400 });
29209                 }
29210             };
29211
29212         switch(graphtype){
29213             case 'bar':
29214                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29215                 break;
29216             case 'hbar':
29217                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29218                 break;
29219             case 'pie':
29220 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29221 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29222 //            
29223                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29224                 
29225                 break;
29226
29227         }
29228         
29229         if(this.title){
29230             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29231         }
29232         
29233     },
29234     
29235     setTitle: function(o)
29236     {
29237         this.title = o;
29238     },
29239     
29240     initEvents: function() {
29241         
29242         if(!this.href){
29243             this.el.on('click', this.onClick, this);
29244         }
29245     },
29246     
29247     onClick : function(e)
29248     {
29249         Roo.log('img onclick');
29250         this.fireEvent('click', this, e);
29251     }
29252    
29253 });
29254
29255  
29256 /*
29257  * - LGPL
29258  *
29259  * numberBox
29260  * 
29261  */
29262 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29263
29264 /**
29265  * @class Roo.bootstrap.dash.NumberBox
29266  * @extends Roo.bootstrap.Component
29267  * Bootstrap NumberBox class
29268  * @cfg {String} headline Box headline
29269  * @cfg {String} content Box content
29270  * @cfg {String} icon Box icon
29271  * @cfg {String} footer Footer text
29272  * @cfg {String} fhref Footer href
29273  * 
29274  * @constructor
29275  * Create a new NumberBox
29276  * @param {Object} config The config object
29277  */
29278
29279
29280 Roo.bootstrap.dash.NumberBox = function(config){
29281     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29282     
29283 };
29284
29285 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29286     
29287     headline : '',
29288     content : '',
29289     icon : '',
29290     footer : '',
29291     fhref : '',
29292     ficon : '',
29293     
29294     getAutoCreate : function(){
29295         
29296         var cfg = {
29297             tag : 'div',
29298             cls : 'small-box ',
29299             cn : [
29300                 {
29301                     tag : 'div',
29302                     cls : 'inner',
29303                     cn :[
29304                         {
29305                             tag : 'h3',
29306                             cls : 'roo-headline',
29307                             html : this.headline
29308                         },
29309                         {
29310                             tag : 'p',
29311                             cls : 'roo-content',
29312                             html : this.content
29313                         }
29314                     ]
29315                 }
29316             ]
29317         };
29318         
29319         if(this.icon){
29320             cfg.cn.push({
29321                 tag : 'div',
29322                 cls : 'icon',
29323                 cn :[
29324                     {
29325                         tag : 'i',
29326                         cls : 'ion ' + this.icon
29327                     }
29328                 ]
29329             });
29330         }
29331         
29332         if(this.footer){
29333             var footer = {
29334                 tag : 'a',
29335                 cls : 'small-box-footer',
29336                 href : this.fhref || '#',
29337                 html : this.footer
29338             };
29339             
29340             cfg.cn.push(footer);
29341             
29342         }
29343         
29344         return  cfg;
29345     },
29346
29347     onRender : function(ct,position){
29348         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29349
29350
29351        
29352                 
29353     },
29354
29355     setHeadline: function (value)
29356     {
29357         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29358     },
29359     
29360     setFooter: function (value, href)
29361     {
29362         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29363         
29364         if(href){
29365             this.el.select('a.small-box-footer',true).first().attr('href', href);
29366         }
29367         
29368     },
29369
29370     setContent: function (value)
29371     {
29372         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29373     },
29374
29375     initEvents: function() 
29376     {   
29377         
29378     }
29379     
29380 });
29381
29382  
29383 /*
29384  * - LGPL
29385  *
29386  * TabBox
29387  * 
29388  */
29389 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29390
29391 /**
29392  * @class Roo.bootstrap.dash.TabBox
29393  * @extends Roo.bootstrap.Component
29394  * @children Roo.bootstrap.dash.TabPane
29395  * Bootstrap TabBox class
29396  * @cfg {String} title Title of the TabBox
29397  * @cfg {String} icon Icon of the TabBox
29398  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29399  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29400  * 
29401  * @constructor
29402  * Create a new TabBox
29403  * @param {Object} config The config object
29404  */
29405
29406
29407 Roo.bootstrap.dash.TabBox = function(config){
29408     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29409     this.addEvents({
29410         // raw events
29411         /**
29412          * @event addpane
29413          * When a pane is added
29414          * @param {Roo.bootstrap.dash.TabPane} pane
29415          */
29416         "addpane" : true,
29417         /**
29418          * @event activatepane
29419          * When a pane is activated
29420          * @param {Roo.bootstrap.dash.TabPane} pane
29421          */
29422         "activatepane" : true
29423         
29424          
29425     });
29426     
29427     this.panes = [];
29428 };
29429
29430 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29431
29432     title : '',
29433     icon : false,
29434     showtabs : true,
29435     tabScrollable : false,
29436     
29437     getChildContainer : function()
29438     {
29439         return this.el.select('.tab-content', true).first();
29440     },
29441     
29442     getAutoCreate : function(){
29443         
29444         var header = {
29445             tag: 'li',
29446             cls: 'pull-left header',
29447             html: this.title,
29448             cn : []
29449         };
29450         
29451         if(this.icon){
29452             header.cn.push({
29453                 tag: 'i',
29454                 cls: 'fa ' + this.icon
29455             });
29456         }
29457         
29458         var h = {
29459             tag: 'ul',
29460             cls: 'nav nav-tabs pull-right',
29461             cn: [
29462                 header
29463             ]
29464         };
29465         
29466         if(this.tabScrollable){
29467             h = {
29468                 tag: 'div',
29469                 cls: 'tab-header',
29470                 cn: [
29471                     {
29472                         tag: 'ul',
29473                         cls: 'nav nav-tabs pull-right',
29474                         cn: [
29475                             header
29476                         ]
29477                     }
29478                 ]
29479             };
29480         }
29481         
29482         var cfg = {
29483             tag: 'div',
29484             cls: 'nav-tabs-custom',
29485             cn: [
29486                 h,
29487                 {
29488                     tag: 'div',
29489                     cls: 'tab-content no-padding',
29490                     cn: []
29491                 }
29492             ]
29493         };
29494
29495         return  cfg;
29496     },
29497     initEvents : function()
29498     {
29499         //Roo.log('add add pane handler');
29500         this.on('addpane', this.onAddPane, this);
29501     },
29502      /**
29503      * Updates the box title
29504      * @param {String} html to set the title to.
29505      */
29506     setTitle : function(value)
29507     {
29508         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29509     },
29510     onAddPane : function(pane)
29511     {
29512         this.panes.push(pane);
29513         //Roo.log('addpane');
29514         //Roo.log(pane);
29515         // tabs are rendere left to right..
29516         if(!this.showtabs){
29517             return;
29518         }
29519         
29520         var ctr = this.el.select('.nav-tabs', true).first();
29521          
29522          
29523         var existing = ctr.select('.nav-tab',true);
29524         var qty = existing.getCount();;
29525         
29526         
29527         var tab = ctr.createChild({
29528             tag : 'li',
29529             cls : 'nav-tab' + (qty ? '' : ' active'),
29530             cn : [
29531                 {
29532                     tag : 'a',
29533                     href:'#',
29534                     html : pane.title
29535                 }
29536             ]
29537         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29538         pane.tab = tab;
29539         
29540         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29541         if (!qty) {
29542             pane.el.addClass('active');
29543         }
29544         
29545                 
29546     },
29547     onTabClick : function(ev,un,ob,pane)
29548     {
29549         //Roo.log('tab - prev default');
29550         ev.preventDefault();
29551         
29552         
29553         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29554         pane.tab.addClass('active');
29555         //Roo.log(pane.title);
29556         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29557         // technically we should have a deactivate event.. but maybe add later.
29558         // and it should not de-activate the selected tab...
29559         this.fireEvent('activatepane', pane);
29560         pane.el.addClass('active');
29561         pane.fireEvent('activate');
29562         
29563         
29564     },
29565     
29566     getActivePane : function()
29567     {
29568         var r = false;
29569         Roo.each(this.panes, function(p) {
29570             if(p.el.hasClass('active')){
29571                 r = p;
29572                 return false;
29573             }
29574             
29575             return;
29576         });
29577         
29578         return r;
29579     }
29580     
29581     
29582 });
29583
29584  
29585 /*
29586  * - LGPL
29587  *
29588  * Tab pane
29589  * 
29590  */
29591 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29592 /**
29593  * @class Roo.bootstrap.TabPane
29594  * @extends Roo.bootstrap.Component
29595  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29596  * Bootstrap TabPane class
29597  * @cfg {Boolean} active (false | true) Default false
29598  * @cfg {String} title title of panel
29599
29600  * 
29601  * @constructor
29602  * Create a new TabPane
29603  * @param {Object} config The config object
29604  */
29605
29606 Roo.bootstrap.dash.TabPane = function(config){
29607     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29608     
29609     this.addEvents({
29610         // raw events
29611         /**
29612          * @event activate
29613          * When a pane is activated
29614          * @param {Roo.bootstrap.dash.TabPane} pane
29615          */
29616         "activate" : true
29617          
29618     });
29619 };
29620
29621 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29622     
29623     active : false,
29624     title : '',
29625     
29626     // the tabBox that this is attached to.
29627     tab : false,
29628      
29629     getAutoCreate : function() 
29630     {
29631         var cfg = {
29632             tag: 'div',
29633             cls: 'tab-pane'
29634         };
29635         
29636         if(this.active){
29637             cfg.cls += ' active';
29638         }
29639         
29640         return cfg;
29641     },
29642     initEvents  : function()
29643     {
29644         //Roo.log('trigger add pane handler');
29645         this.parent().fireEvent('addpane', this)
29646     },
29647     
29648      /**
29649      * Updates the tab title 
29650      * @param {String} html to set the title to.
29651      */
29652     setTitle: function(str)
29653     {
29654         if (!this.tab) {
29655             return;
29656         }
29657         this.title = str;
29658         this.tab.select('a', true).first().dom.innerHTML = str;
29659         
29660     }
29661     
29662     
29663     
29664 });
29665
29666  
29667
29668
29669  /*
29670  * - LGPL
29671  *
29672  * Tooltip
29673  * 
29674  */
29675
29676 /**
29677  * @class Roo.bootstrap.Tooltip
29678  * Bootstrap Tooltip class
29679  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29680  * to determine which dom element triggers the tooltip.
29681  * 
29682  * It needs to add support for additional attributes like tooltip-position
29683  * 
29684  * @constructor
29685  * Create a new Toolti
29686  * @param {Object} config The config object
29687  */
29688
29689 Roo.bootstrap.Tooltip = function(config){
29690     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29691     
29692     this.alignment = Roo.bootstrap.Tooltip.alignment;
29693     
29694     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29695         this.alignment = config.alignment;
29696     }
29697     
29698 };
29699
29700 Roo.apply(Roo.bootstrap.Tooltip, {
29701     /**
29702      * @function init initialize tooltip monitoring.
29703      * @static
29704      */
29705     currentEl : false,
29706     currentTip : false,
29707     currentRegion : false,
29708     
29709     //  init : delay?
29710     
29711     init : function()
29712     {
29713         Roo.get(document).on('mouseover', this.enter ,this);
29714         Roo.get(document).on('mouseout', this.leave, this);
29715          
29716         
29717         this.currentTip = new Roo.bootstrap.Tooltip();
29718     },
29719     
29720     enter : function(ev)
29721     {
29722         var dom = ev.getTarget();
29723         
29724         //Roo.log(['enter',dom]);
29725         var el = Roo.fly(dom);
29726         if (this.currentEl) {
29727             //Roo.log(dom);
29728             //Roo.log(this.currentEl);
29729             //Roo.log(this.currentEl.contains(dom));
29730             if (this.currentEl == el) {
29731                 return;
29732             }
29733             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29734                 return;
29735             }
29736
29737         }
29738         
29739         if (this.currentTip.el) {
29740             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29741         }    
29742         //Roo.log(ev);
29743         
29744         if(!el || el.dom == document){
29745             return;
29746         }
29747         
29748         var bindEl = el; 
29749         var pel = false;
29750         if (!el.attr('tooltip')) {
29751             pel = el.findParent("[tooltip]");
29752             if (pel) {
29753                 bindEl = Roo.get(pel);
29754             }
29755         }
29756         
29757        
29758         
29759         // you can not look for children, as if el is the body.. then everythign is the child..
29760         if (!pel && !el.attr('tooltip')) { //
29761             if (!el.select("[tooltip]").elements.length) {
29762                 return;
29763             }
29764             // is the mouse over this child...?
29765             bindEl = el.select("[tooltip]").first();
29766             var xy = ev.getXY();
29767             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29768                 //Roo.log("not in region.");
29769                 return;
29770             }
29771             //Roo.log("child element over..");
29772             
29773         }
29774         this.currentEl = el;
29775         this.currentTip.bind(bindEl);
29776         this.currentRegion = Roo.lib.Region.getRegion(dom);
29777         this.currentTip.enter();
29778         
29779     },
29780     leave : function(ev)
29781     {
29782         var dom = ev.getTarget();
29783         //Roo.log(['leave',dom]);
29784         if (!this.currentEl) {
29785             return;
29786         }
29787         
29788         
29789         if (dom != this.currentEl.dom) {
29790             return;
29791         }
29792         var xy = ev.getXY();
29793         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29794             return;
29795         }
29796         // only activate leave if mouse cursor is outside... bounding box..
29797         
29798         
29799         
29800         
29801         if (this.currentTip) {
29802             this.currentTip.leave();
29803         }
29804         //Roo.log('clear currentEl');
29805         this.currentEl = false;
29806         
29807         
29808     },
29809     alignment : {
29810         'left' : ['r-l', [-2,0], 'right'],
29811         'right' : ['l-r', [2,0], 'left'],
29812         'bottom' : ['t-b', [0,2], 'top'],
29813         'top' : [ 'b-t', [0,-2], 'bottom']
29814     }
29815     
29816 });
29817
29818
29819 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29820     
29821     
29822     bindEl : false,
29823     
29824     delay : null, // can be { show : 300 , hide: 500}
29825     
29826     timeout : null,
29827     
29828     hoverState : null, //???
29829     
29830     placement : 'bottom', 
29831     
29832     alignment : false,
29833     
29834     getAutoCreate : function(){
29835     
29836         var cfg = {
29837            cls : 'tooltip',   
29838            role : 'tooltip',
29839            cn : [
29840                 {
29841                     cls : 'tooltip-arrow arrow'
29842                 },
29843                 {
29844                     cls : 'tooltip-inner'
29845                 }
29846            ]
29847         };
29848         
29849         return cfg;
29850     },
29851     bind : function(el)
29852     {
29853         this.bindEl = el;
29854     },
29855     
29856     initEvents : function()
29857     {
29858         this.arrowEl = this.el.select('.arrow', true).first();
29859         this.innerEl = this.el.select('.tooltip-inner', true).first();
29860     },
29861     
29862     enter : function () {
29863        
29864         if (this.timeout != null) {
29865             clearTimeout(this.timeout);
29866         }
29867         
29868         this.hoverState = 'in';
29869          //Roo.log("enter - show");
29870         if (!this.delay || !this.delay.show) {
29871             this.show();
29872             return;
29873         }
29874         var _t = this;
29875         this.timeout = setTimeout(function () {
29876             if (_t.hoverState == 'in') {
29877                 _t.show();
29878             }
29879         }, this.delay.show);
29880     },
29881     leave : function()
29882     {
29883         clearTimeout(this.timeout);
29884     
29885         this.hoverState = 'out';
29886          if (!this.delay || !this.delay.hide) {
29887             this.hide();
29888             return;
29889         }
29890        
29891         var _t = this;
29892         this.timeout = setTimeout(function () {
29893             //Roo.log("leave - timeout");
29894             
29895             if (_t.hoverState == 'out') {
29896                 _t.hide();
29897                 Roo.bootstrap.Tooltip.currentEl = false;
29898             }
29899         }, delay);
29900     },
29901     
29902     show : function (msg)
29903     {
29904         if (!this.el) {
29905             this.render(document.body);
29906         }
29907         // set content.
29908         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29909         
29910         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29911         
29912         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29913         
29914         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29915                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29916         
29917         var placement = typeof this.placement == 'function' ?
29918             this.placement.call(this, this.el, on_el) :
29919             this.placement;
29920             
29921         var autoToken = /\s?auto?\s?/i;
29922         var autoPlace = autoToken.test(placement);
29923         if (autoPlace) {
29924             placement = placement.replace(autoToken, '') || 'top';
29925         }
29926         
29927         //this.el.detach()
29928         //this.el.setXY([0,0]);
29929         this.el.show();
29930         //this.el.dom.style.display='block';
29931         
29932         //this.el.appendTo(on_el);
29933         
29934         var p = this.getPosition();
29935         var box = this.el.getBox();
29936         
29937         if (autoPlace) {
29938             // fixme..
29939         }
29940         
29941         var align = this.alignment[placement];
29942         
29943         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29944         
29945         if(placement == 'top' || placement == 'bottom'){
29946             if(xy[0] < 0){
29947                 placement = 'right';
29948             }
29949             
29950             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29951                 placement = 'left';
29952             }
29953             
29954             var scroll = Roo.select('body', true).first().getScroll();
29955             
29956             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29957                 placement = 'top';
29958             }
29959             
29960             align = this.alignment[placement];
29961             
29962             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29963             
29964         }
29965         
29966         var elems = document.getElementsByTagName('div');
29967         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29968         for (var i = 0; i < elems.length; i++) {
29969           var zindex = Number.parseInt(
29970                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29971                 10
29972           );
29973           if (zindex > highest) {
29974             highest = zindex;
29975           }
29976         }
29977         
29978         
29979         
29980         this.el.dom.style.zIndex = highest;
29981         
29982         this.el.alignTo(this.bindEl, align[0],align[1]);
29983         //var arrow = this.el.select('.arrow',true).first();
29984         //arrow.set(align[2], 
29985         
29986         this.el.addClass(placement);
29987         this.el.addClass("bs-tooltip-"+ placement);
29988         
29989         this.el.addClass('in fade show');
29990         
29991         this.hoverState = null;
29992         
29993         if (this.el.hasClass('fade')) {
29994             // fade it?
29995         }
29996         
29997         
29998         
29999         
30000         
30001     },
30002     hide : function()
30003     {
30004          
30005         if (!this.el) {
30006             return;
30007         }
30008         //this.el.setXY([0,0]);
30009         this.el.removeClass(['show', 'in']);
30010         //this.el.hide();
30011         
30012     }
30013     
30014 });
30015  
30016
30017  /*
30018  * - LGPL
30019  *
30020  * Location Picker
30021  * 
30022  */
30023
30024 /**
30025  * @class Roo.bootstrap.LocationPicker
30026  * @extends Roo.bootstrap.Component
30027  * Bootstrap LocationPicker class
30028  * @cfg {Number} latitude Position when init default 0
30029  * @cfg {Number} longitude Position when init default 0
30030  * @cfg {Number} zoom default 15
30031  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30032  * @cfg {Boolean} mapTypeControl default false
30033  * @cfg {Boolean} disableDoubleClickZoom default false
30034  * @cfg {Boolean} scrollwheel default true
30035  * @cfg {Boolean} streetViewControl default false
30036  * @cfg {Number} radius default 0
30037  * @cfg {String} locationName
30038  * @cfg {Boolean} draggable default true
30039  * @cfg {Boolean} enableAutocomplete default false
30040  * @cfg {Boolean} enableReverseGeocode default true
30041  * @cfg {String} markerTitle
30042  * 
30043  * @constructor
30044  * Create a new LocationPicker
30045  * @param {Object} config The config object
30046  */
30047
30048
30049 Roo.bootstrap.LocationPicker = function(config){
30050     
30051     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30052     
30053     this.addEvents({
30054         /**
30055          * @event initial
30056          * Fires when the picker initialized.
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          * @param {Google Location} location
30059          */
30060         initial : true,
30061         /**
30062          * @event positionchanged
30063          * Fires when the picker position changed.
30064          * @param {Roo.bootstrap.LocationPicker} this
30065          * @param {Google Location} location
30066          */
30067         positionchanged : true,
30068         /**
30069          * @event resize
30070          * Fires when the map resize.
30071          * @param {Roo.bootstrap.LocationPicker} this
30072          */
30073         resize : true,
30074         /**
30075          * @event show
30076          * Fires when the map show.
30077          * @param {Roo.bootstrap.LocationPicker} this
30078          */
30079         show : true,
30080         /**
30081          * @event hide
30082          * Fires when the map hide.
30083          * @param {Roo.bootstrap.LocationPicker} this
30084          */
30085         hide : true,
30086         /**
30087          * @event mapClick
30088          * Fires when click the map.
30089          * @param {Roo.bootstrap.LocationPicker} this
30090          * @param {Map event} e
30091          */
30092         mapClick : true,
30093         /**
30094          * @event mapRightClick
30095          * Fires when right click the map.
30096          * @param {Roo.bootstrap.LocationPicker} this
30097          * @param {Map event} e
30098          */
30099         mapRightClick : true,
30100         /**
30101          * @event markerClick
30102          * Fires when click the marker.
30103          * @param {Roo.bootstrap.LocationPicker} this
30104          * @param {Map event} e
30105          */
30106         markerClick : true,
30107         /**
30108          * @event markerRightClick
30109          * Fires when right click the marker.
30110          * @param {Roo.bootstrap.LocationPicker} this
30111          * @param {Map event} e
30112          */
30113         markerRightClick : true,
30114         /**
30115          * @event OverlayViewDraw
30116          * Fires when OverlayView Draw
30117          * @param {Roo.bootstrap.LocationPicker} this
30118          */
30119         OverlayViewDraw : true,
30120         /**
30121          * @event OverlayViewOnAdd
30122          * Fires when OverlayView Draw
30123          * @param {Roo.bootstrap.LocationPicker} this
30124          */
30125         OverlayViewOnAdd : true,
30126         /**
30127          * @event OverlayViewOnRemove
30128          * Fires when OverlayView Draw
30129          * @param {Roo.bootstrap.LocationPicker} this
30130          */
30131         OverlayViewOnRemove : true,
30132         /**
30133          * @event OverlayViewShow
30134          * Fires when OverlayView Draw
30135          * @param {Roo.bootstrap.LocationPicker} this
30136          * @param {Pixel} cpx
30137          */
30138         OverlayViewShow : true,
30139         /**
30140          * @event OverlayViewHide
30141          * Fires when OverlayView Draw
30142          * @param {Roo.bootstrap.LocationPicker} this
30143          */
30144         OverlayViewHide : true,
30145         /**
30146          * @event loadexception
30147          * Fires when load google lib failed.
30148          * @param {Roo.bootstrap.LocationPicker} this
30149          */
30150         loadexception : true
30151     });
30152         
30153 };
30154
30155 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30156     
30157     gMapContext: false,
30158     
30159     latitude: 0,
30160     longitude: 0,
30161     zoom: 15,
30162     mapTypeId: false,
30163     mapTypeControl: false,
30164     disableDoubleClickZoom: false,
30165     scrollwheel: true,
30166     streetViewControl: false,
30167     radius: 0,
30168     locationName: '',
30169     draggable: true,
30170     enableAutocomplete: false,
30171     enableReverseGeocode: true,
30172     markerTitle: '',
30173     
30174     getAutoCreate: function()
30175     {
30176
30177         var cfg = {
30178             tag: 'div',
30179             cls: 'roo-location-picker'
30180         };
30181         
30182         return cfg
30183     },
30184     
30185     initEvents: function(ct, position)
30186     {       
30187         if(!this.el.getWidth() || this.isApplied()){
30188             return;
30189         }
30190         
30191         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30192         
30193         this.initial();
30194     },
30195     
30196     initial: function()
30197     {
30198         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30199             this.fireEvent('loadexception', this);
30200             return;
30201         }
30202         
30203         if(!this.mapTypeId){
30204             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30205         }
30206         
30207         this.gMapContext = this.GMapContext();
30208         
30209         this.initOverlayView();
30210         
30211         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30212         
30213         var _this = this;
30214                 
30215         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30216             _this.setPosition(_this.gMapContext.marker.position);
30217         });
30218         
30219         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30220             _this.fireEvent('mapClick', this, event);
30221             
30222         });
30223
30224         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30225             _this.fireEvent('mapRightClick', this, event);
30226             
30227         });
30228         
30229         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30230             _this.fireEvent('markerClick', this, event);
30231             
30232         });
30233
30234         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30235             _this.fireEvent('markerRightClick', this, event);
30236             
30237         });
30238         
30239         this.setPosition(this.gMapContext.location);
30240         
30241         this.fireEvent('initial', this, this.gMapContext.location);
30242     },
30243     
30244     initOverlayView: function()
30245     {
30246         var _this = this;
30247         
30248         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30249             
30250             draw: function()
30251             {
30252                 _this.fireEvent('OverlayViewDraw', _this);
30253             },
30254             
30255             onAdd: function()
30256             {
30257                 _this.fireEvent('OverlayViewOnAdd', _this);
30258             },
30259             
30260             onRemove: function()
30261             {
30262                 _this.fireEvent('OverlayViewOnRemove', _this);
30263             },
30264             
30265             show: function(cpx)
30266             {
30267                 _this.fireEvent('OverlayViewShow', _this, cpx);
30268             },
30269             
30270             hide: function()
30271             {
30272                 _this.fireEvent('OverlayViewHide', _this);
30273             }
30274             
30275         });
30276     },
30277     
30278     fromLatLngToContainerPixel: function(event)
30279     {
30280         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30281     },
30282     
30283     isApplied: function() 
30284     {
30285         return this.getGmapContext() == false ? false : true;
30286     },
30287     
30288     getGmapContext: function() 
30289     {
30290         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30291     },
30292     
30293     GMapContext: function() 
30294     {
30295         var position = new google.maps.LatLng(this.latitude, this.longitude);
30296         
30297         var _map = new google.maps.Map(this.el.dom, {
30298             center: position,
30299             zoom: this.zoom,
30300             mapTypeId: this.mapTypeId,
30301             mapTypeControl: this.mapTypeControl,
30302             disableDoubleClickZoom: this.disableDoubleClickZoom,
30303             scrollwheel: this.scrollwheel,
30304             streetViewControl: this.streetViewControl,
30305             locationName: this.locationName,
30306             draggable: this.draggable,
30307             enableAutocomplete: this.enableAutocomplete,
30308             enableReverseGeocode: this.enableReverseGeocode
30309         });
30310         
30311         var _marker = new google.maps.Marker({
30312             position: position,
30313             map: _map,
30314             title: this.markerTitle,
30315             draggable: this.draggable
30316         });
30317         
30318         return {
30319             map: _map,
30320             marker: _marker,
30321             circle: null,
30322             location: position,
30323             radius: this.radius,
30324             locationName: this.locationName,
30325             addressComponents: {
30326                 formatted_address: null,
30327                 addressLine1: null,
30328                 addressLine2: null,
30329                 streetName: null,
30330                 streetNumber: null,
30331                 city: null,
30332                 district: null,
30333                 state: null,
30334                 stateOrProvince: null
30335             },
30336             settings: this,
30337             domContainer: this.el.dom,
30338             geodecoder: new google.maps.Geocoder()
30339         };
30340     },
30341     
30342     drawCircle: function(center, radius, options) 
30343     {
30344         if (this.gMapContext.circle != null) {
30345             this.gMapContext.circle.setMap(null);
30346         }
30347         if (radius > 0) {
30348             radius *= 1;
30349             options = Roo.apply({}, options, {
30350                 strokeColor: "#0000FF",
30351                 strokeOpacity: .35,
30352                 strokeWeight: 2,
30353                 fillColor: "#0000FF",
30354                 fillOpacity: .2
30355             });
30356             
30357             options.map = this.gMapContext.map;
30358             options.radius = radius;
30359             options.center = center;
30360             this.gMapContext.circle = new google.maps.Circle(options);
30361             return this.gMapContext.circle;
30362         }
30363         
30364         return null;
30365     },
30366     
30367     setPosition: function(location) 
30368     {
30369         this.gMapContext.location = location;
30370         this.gMapContext.marker.setPosition(location);
30371         this.gMapContext.map.panTo(location);
30372         this.drawCircle(location, this.gMapContext.radius, {});
30373         
30374         var _this = this;
30375         
30376         if (this.gMapContext.settings.enableReverseGeocode) {
30377             this.gMapContext.geodecoder.geocode({
30378                 latLng: this.gMapContext.location
30379             }, function(results, status) {
30380                 
30381                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30382                     _this.gMapContext.locationName = results[0].formatted_address;
30383                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30384                     
30385                     _this.fireEvent('positionchanged', this, location);
30386                 }
30387             });
30388             
30389             return;
30390         }
30391         
30392         this.fireEvent('positionchanged', this, location);
30393     },
30394     
30395     resize: function()
30396     {
30397         google.maps.event.trigger(this.gMapContext.map, "resize");
30398         
30399         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30400         
30401         this.fireEvent('resize', this);
30402     },
30403     
30404     setPositionByLatLng: function(latitude, longitude)
30405     {
30406         this.setPosition(new google.maps.LatLng(latitude, longitude));
30407     },
30408     
30409     getCurrentPosition: function() 
30410     {
30411         return {
30412             latitude: this.gMapContext.location.lat(),
30413             longitude: this.gMapContext.location.lng()
30414         };
30415     },
30416     
30417     getAddressName: function() 
30418     {
30419         return this.gMapContext.locationName;
30420     },
30421     
30422     getAddressComponents: function() 
30423     {
30424         return this.gMapContext.addressComponents;
30425     },
30426     
30427     address_component_from_google_geocode: function(address_components) 
30428     {
30429         var result = {};
30430         
30431         for (var i = 0; i < address_components.length; i++) {
30432             var component = address_components[i];
30433             if (component.types.indexOf("postal_code") >= 0) {
30434                 result.postalCode = component.short_name;
30435             } else if (component.types.indexOf("street_number") >= 0) {
30436                 result.streetNumber = component.short_name;
30437             } else if (component.types.indexOf("route") >= 0) {
30438                 result.streetName = component.short_name;
30439             } else if (component.types.indexOf("neighborhood") >= 0) {
30440                 result.city = component.short_name;
30441             } else if (component.types.indexOf("locality") >= 0) {
30442                 result.city = component.short_name;
30443             } else if (component.types.indexOf("sublocality") >= 0) {
30444                 result.district = component.short_name;
30445             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30446                 result.stateOrProvince = component.short_name;
30447             } else if (component.types.indexOf("country") >= 0) {
30448                 result.country = component.short_name;
30449             }
30450         }
30451         
30452         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30453         result.addressLine2 = "";
30454         return result;
30455     },
30456     
30457     setZoomLevel: function(zoom)
30458     {
30459         this.gMapContext.map.setZoom(zoom);
30460     },
30461     
30462     show: function()
30463     {
30464         if(!this.el){
30465             return;
30466         }
30467         
30468         this.el.show();
30469         
30470         this.resize();
30471         
30472         this.fireEvent('show', this);
30473     },
30474     
30475     hide: function()
30476     {
30477         if(!this.el){
30478             return;
30479         }
30480         
30481         this.el.hide();
30482         
30483         this.fireEvent('hide', this);
30484     }
30485     
30486 });
30487
30488 Roo.apply(Roo.bootstrap.LocationPicker, {
30489     
30490     OverlayView : function(map, options)
30491     {
30492         options = options || {};
30493         
30494         this.setMap(map);
30495     }
30496     
30497     
30498 });/**
30499  * @class Roo.bootstrap.Alert
30500  * @extends Roo.bootstrap.Component
30501  * Bootstrap Alert class - shows an alert area box
30502  * eg
30503  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30504   Enter a valid email address
30505 </div>
30506  * @licence LGPL
30507  * @cfg {String} title The title of alert
30508  * @cfg {String} html The content of alert
30509  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30510  * @cfg {String} fa font-awesomeicon
30511  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30512  * @cfg {Boolean} close true to show a x closer
30513  * 
30514  * 
30515  * @constructor
30516  * Create a new alert
30517  * @param {Object} config The config object
30518  */
30519
30520
30521 Roo.bootstrap.Alert = function(config){
30522     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30523     
30524 };
30525
30526 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30527     
30528     title: '',
30529     html: '',
30530     weight: false,
30531     fa: false,
30532     faicon: false, // BC
30533     close : false,
30534     
30535     
30536     getAutoCreate : function()
30537     {
30538         
30539         var cfg = {
30540             tag : 'div',
30541             cls : 'alert',
30542             cn : [
30543                 {
30544                     tag: 'button',
30545                     type :  "button",
30546                     cls: "close",
30547                     html : '×',
30548                     style : this.close ? '' : 'display:none'
30549                 },
30550                 {
30551                     tag : 'i',
30552                     cls : 'roo-alert-icon'
30553                     
30554                 },
30555                 {
30556                     tag : 'b',
30557                     cls : 'roo-alert-title',
30558                     html : this.title
30559                 },
30560                 {
30561                     tag : 'span',
30562                     cls : 'roo-alert-text',
30563                     html : this.html
30564                 }
30565             ]
30566         };
30567         
30568         if(this.faicon){
30569             cfg.cn[0].cls += ' fa ' + this.faicon;
30570         }
30571         if(this.fa){
30572             cfg.cn[0].cls += ' fa ' + this.fa;
30573         }
30574         
30575         if(this.weight){
30576             cfg.cls += ' alert-' + this.weight;
30577         }
30578         
30579         return cfg;
30580     },
30581     
30582     initEvents: function() 
30583     {
30584         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30585         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30586         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30587         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30588         if (this.seconds > 0) {
30589             this.hide.defer(this.seconds, this);
30590         }
30591     },
30592     /**
30593      * Set the Title Message HTML
30594      * @param {String} html
30595      */
30596     setTitle : function(str)
30597     {
30598         this.titleEl.dom.innerHTML = str;
30599     },
30600      
30601      /**
30602      * Set the Body Message HTML
30603      * @param {String} html
30604      */
30605     setHtml : function(str)
30606     {
30607         this.htmlEl.dom.innerHTML = str;
30608     },
30609     /**
30610      * Set the Weight of the alert
30611      * @param {String} (success|info|warning|danger) weight
30612      */
30613     
30614     setWeight : function(weight)
30615     {
30616         if(this.weight){
30617             this.el.removeClass('alert-' + this.weight);
30618         }
30619         
30620         this.weight = weight;
30621         
30622         this.el.addClass('alert-' + this.weight);
30623     },
30624       /**
30625      * Set the Icon of the alert
30626      * @param {String} see fontawsome names (name without the 'fa-' bit)
30627      */
30628     setIcon : function(icon)
30629     {
30630         if(this.faicon){
30631             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30632         }
30633         
30634         this.faicon = icon;
30635         
30636         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30637     },
30638     /**
30639      * Hide the Alert
30640      */
30641     hide: function() 
30642     {
30643         this.el.hide();   
30644     },
30645     /**
30646      * Show the Alert
30647      */
30648     show: function() 
30649     {  
30650         this.el.show();   
30651     }
30652     
30653 });
30654
30655  
30656 /*
30657 * Licence: LGPL
30658 */
30659
30660 /**
30661  * @class Roo.bootstrap.UploadCropbox
30662  * @extends Roo.bootstrap.Component
30663  * Bootstrap UploadCropbox class
30664  * @cfg {String} emptyText show when image has been loaded
30665  * @cfg {String} rotateNotify show when image too small to rotate
30666  * @cfg {Number} errorTimeout default 3000
30667  * @cfg {Number} minWidth default 300
30668  * @cfg {Number} minHeight default 300
30669  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30670  * @cfg {Boolean} isDocument (true|false) default false
30671  * @cfg {String} url action url
30672  * @cfg {String} paramName default 'imageUpload'
30673  * @cfg {String} method default POST
30674  * @cfg {Boolean} loadMask (true|false) default true
30675  * @cfg {Boolean} loadingText default 'Loading...'
30676  * 
30677  * @constructor
30678  * Create a new UploadCropbox
30679  * @param {Object} config The config object
30680  */
30681
30682 Roo.bootstrap.UploadCropbox = function(config){
30683     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30684     
30685     this.addEvents({
30686         /**
30687          * @event beforeselectfile
30688          * Fire before select file
30689          * @param {Roo.bootstrap.UploadCropbox} this
30690          */
30691         "beforeselectfile" : true,
30692         /**
30693          * @event initial
30694          * Fire after initEvent
30695          * @param {Roo.bootstrap.UploadCropbox} this
30696          */
30697         "initial" : true,
30698         /**
30699          * @event crop
30700          * Fire after initEvent
30701          * @param {Roo.bootstrap.UploadCropbox} this
30702          * @param {String} data
30703          */
30704         "crop" : true,
30705         /**
30706          * @event prepare
30707          * Fire when preparing the file data
30708          * @param {Roo.bootstrap.UploadCropbox} this
30709          * @param {Object} file
30710          */
30711         "prepare" : true,
30712         /**
30713          * @event exception
30714          * Fire when get exception
30715          * @param {Roo.bootstrap.UploadCropbox} this
30716          * @param {XMLHttpRequest} xhr
30717          */
30718         "exception" : true,
30719         /**
30720          * @event beforeloadcanvas
30721          * Fire before load the canvas
30722          * @param {Roo.bootstrap.UploadCropbox} this
30723          * @param {String} src
30724          */
30725         "beforeloadcanvas" : true,
30726         /**
30727          * @event trash
30728          * Fire when trash image
30729          * @param {Roo.bootstrap.UploadCropbox} this
30730          */
30731         "trash" : true,
30732         /**
30733          * @event download
30734          * Fire when download the image
30735          * @param {Roo.bootstrap.UploadCropbox} this
30736          */
30737         "download" : true,
30738         /**
30739          * @event footerbuttonclick
30740          * Fire when footerbuttonclick
30741          * @param {Roo.bootstrap.UploadCropbox} this
30742          * @param {String} type
30743          */
30744         "footerbuttonclick" : true,
30745         /**
30746          * @event resize
30747          * Fire when resize
30748          * @param {Roo.bootstrap.UploadCropbox} this
30749          */
30750         "resize" : true,
30751         /**
30752          * @event rotate
30753          * Fire when rotate the image
30754          * @param {Roo.bootstrap.UploadCropbox} this
30755          * @param {String} pos
30756          */
30757         "rotate" : true,
30758         /**
30759          * @event inspect
30760          * Fire when inspect the file
30761          * @param {Roo.bootstrap.UploadCropbox} this
30762          * @param {Object} file
30763          */
30764         "inspect" : true,
30765         /**
30766          * @event upload
30767          * Fire when xhr upload the file
30768          * @param {Roo.bootstrap.UploadCropbox} this
30769          * @param {Object} data
30770          */
30771         "upload" : true,
30772         /**
30773          * @event arrange
30774          * Fire when arrange the file data
30775          * @param {Roo.bootstrap.UploadCropbox} this
30776          * @param {Object} formData
30777          */
30778         "arrange" : true
30779     });
30780     
30781     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30782 };
30783
30784 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30785     
30786     emptyText : 'Click to upload image',
30787     rotateNotify : 'Image is too small to rotate',
30788     errorTimeout : 3000,
30789     scale : 0,
30790     baseScale : 1,
30791     rotate : 0,
30792     dragable : false,
30793     pinching : false,
30794     mouseX : 0,
30795     mouseY : 0,
30796     cropData : false,
30797     minWidth : 300,
30798     minHeight : 300,
30799     file : false,
30800     exif : {},
30801     baseRotate : 1,
30802     cropType : 'image/jpeg',
30803     buttons : false,
30804     canvasLoaded : false,
30805     isDocument : false,
30806     method : 'POST',
30807     paramName : 'imageUpload',
30808     loadMask : true,
30809     loadingText : 'Loading...',
30810     maskEl : false,
30811     
30812     getAutoCreate : function()
30813     {
30814         var cfg = {
30815             tag : 'div',
30816             cls : 'roo-upload-cropbox',
30817             cn : [
30818                 {
30819                     tag : 'input',
30820                     cls : 'roo-upload-cropbox-selector',
30821                     type : 'file'
30822                 },
30823                 {
30824                     tag : 'div',
30825                     cls : 'roo-upload-cropbox-body',
30826                     style : 'cursor:pointer',
30827                     cn : [
30828                         {
30829                             tag : 'div',
30830                             cls : 'roo-upload-cropbox-preview'
30831                         },
30832                         {
30833                             tag : 'div',
30834                             cls : 'roo-upload-cropbox-thumb'
30835                         },
30836                         {
30837                             tag : 'div',
30838                             cls : 'roo-upload-cropbox-empty-notify',
30839                             html : this.emptyText
30840                         },
30841                         {
30842                             tag : 'div',
30843                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30844                             html : this.rotateNotify
30845                         }
30846                     ]
30847                 },
30848                 {
30849                     tag : 'div',
30850                     cls : 'roo-upload-cropbox-footer',
30851                     cn : {
30852                         tag : 'div',
30853                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30854                         cn : []
30855                     }
30856                 }
30857             ]
30858         };
30859         
30860         return cfg;
30861     },
30862     
30863     onRender : function(ct, position)
30864     {
30865         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30866         
30867         if (this.buttons.length) {
30868             
30869             Roo.each(this.buttons, function(bb) {
30870                 
30871                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30872                 
30873                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30874                 
30875             }, this);
30876         }
30877         
30878         if(this.loadMask){
30879             this.maskEl = this.el;
30880         }
30881     },
30882     
30883     initEvents : function()
30884     {
30885         this.urlAPI = (window.createObjectURL && window) || 
30886                                 (window.URL && URL.revokeObjectURL && URL) || 
30887                                 (window.webkitURL && webkitURL);
30888                         
30889         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30890         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30891         
30892         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30893         this.selectorEl.hide();
30894         
30895         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30896         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30897         
30898         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30899         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30900         this.thumbEl.hide();
30901         
30902         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30903         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30904         
30905         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30906         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30907         this.errorEl.hide();
30908         
30909         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30910         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30911         this.footerEl.hide();
30912         
30913         this.setThumbBoxSize();
30914         
30915         this.bind();
30916         
30917         this.resize();
30918         
30919         this.fireEvent('initial', this);
30920     },
30921
30922     bind : function()
30923     {
30924         var _this = this;
30925         
30926         window.addEventListener("resize", function() { _this.resize(); } );
30927         
30928         this.bodyEl.on('click', this.beforeSelectFile, this);
30929         
30930         if(Roo.isTouch){
30931             this.bodyEl.on('touchstart', this.onTouchStart, this);
30932             this.bodyEl.on('touchmove', this.onTouchMove, this);
30933             this.bodyEl.on('touchend', this.onTouchEnd, this);
30934         }
30935         
30936         if(!Roo.isTouch){
30937             this.bodyEl.on('mousedown', this.onMouseDown, this);
30938             this.bodyEl.on('mousemove', this.onMouseMove, this);
30939             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30940             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30941             Roo.get(document).on('mouseup', this.onMouseUp, this);
30942         }
30943         
30944         this.selectorEl.on('change', this.onFileSelected, this);
30945     },
30946     
30947     reset : function()
30948     {    
30949         this.scale = 0;
30950         this.baseScale = 1;
30951         this.rotate = 0;
30952         this.baseRotate = 1;
30953         this.dragable = false;
30954         this.pinching = false;
30955         this.mouseX = 0;
30956         this.mouseY = 0;
30957         this.cropData = false;
30958         this.notifyEl.dom.innerHTML = this.emptyText;
30959         
30960         this.selectorEl.dom.value = '';
30961         
30962     },
30963     
30964     resize : function()
30965     {
30966         if(this.fireEvent('resize', this) != false){
30967             this.setThumbBoxPosition();
30968             this.setCanvasPosition();
30969         }
30970     },
30971     
30972     onFooterButtonClick : function(e, el, o, type)
30973     {
30974         switch (type) {
30975             case 'rotate-left' :
30976                 this.onRotateLeft(e);
30977                 break;
30978             case 'rotate-right' :
30979                 this.onRotateRight(e);
30980                 break;
30981             case 'picture' :
30982                 this.beforeSelectFile(e);
30983                 break;
30984             case 'trash' :
30985                 this.trash(e);
30986                 break;
30987             case 'crop' :
30988                 this.crop(e);
30989                 break;
30990             case 'download' :
30991                 this.download(e);
30992                 break;
30993             default :
30994                 break;
30995         }
30996         
30997         this.fireEvent('footerbuttonclick', this, type);
30998     },
30999     
31000     beforeSelectFile : function(e)
31001     {
31002         e.preventDefault();
31003         
31004         if(this.fireEvent('beforeselectfile', this) != false){
31005             this.selectorEl.dom.click();
31006         }
31007     },
31008     
31009     onFileSelected : function(e)
31010     {
31011         e.preventDefault();
31012         
31013         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31014             return;
31015         }
31016         
31017         var file = this.selectorEl.dom.files[0];
31018         
31019         if(this.fireEvent('inspect', this, file) != false){
31020             this.prepare(file);
31021         }
31022         
31023     },
31024     
31025     trash : function(e)
31026     {
31027         this.fireEvent('trash', this);
31028     },
31029     
31030     download : function(e)
31031     {
31032         this.fireEvent('download', this);
31033     },
31034     
31035     loadCanvas : function(src)
31036     {   
31037         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31038             
31039             this.reset();
31040             
31041             this.imageEl = document.createElement('img');
31042             
31043             var _this = this;
31044             
31045             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31046             
31047             this.imageEl.src = src;
31048         }
31049     },
31050     
31051     onLoadCanvas : function()
31052     {   
31053         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31054         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31055         
31056         this.bodyEl.un('click', this.beforeSelectFile, this);
31057         
31058         this.notifyEl.hide();
31059         this.thumbEl.show();
31060         this.footerEl.show();
31061         
31062         this.baseRotateLevel();
31063         
31064         if(this.isDocument){
31065             this.setThumbBoxSize();
31066         }
31067         
31068         this.setThumbBoxPosition();
31069         
31070         this.baseScaleLevel();
31071         
31072         this.draw();
31073         
31074         this.resize();
31075         
31076         this.canvasLoaded = true;
31077         
31078         if(this.loadMask){
31079             this.maskEl.unmask();
31080         }
31081         
31082     },
31083     
31084     setCanvasPosition : function()
31085     {   
31086         if(!this.canvasEl){
31087             return;
31088         }
31089         
31090         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31091         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31092         
31093         this.previewEl.setLeft(pw);
31094         this.previewEl.setTop(ph);
31095         
31096     },
31097     
31098     onMouseDown : function(e)
31099     {   
31100         e.stopEvent();
31101         
31102         this.dragable = true;
31103         this.pinching = false;
31104         
31105         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31106             this.dragable = false;
31107             return;
31108         }
31109         
31110         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31112         
31113     },
31114     
31115     onMouseMove : function(e)
31116     {   
31117         e.stopEvent();
31118         
31119         if(!this.canvasLoaded){
31120             return;
31121         }
31122         
31123         if (!this.dragable){
31124             return;
31125         }
31126         
31127         var minX = Math.ceil(this.thumbEl.getLeft(true));
31128         var minY = Math.ceil(this.thumbEl.getTop(true));
31129         
31130         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31131         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31132         
31133         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31134         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31135         
31136         x = x - this.mouseX;
31137         y = y - this.mouseY;
31138         
31139         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31140         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31141         
31142         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31143         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31144         
31145         this.previewEl.setLeft(bgX);
31146         this.previewEl.setTop(bgY);
31147         
31148         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31149         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31150     },
31151     
31152     onMouseUp : function(e)
31153     {   
31154         e.stopEvent();
31155         
31156         this.dragable = false;
31157     },
31158     
31159     onMouseWheel : function(e)
31160     {   
31161         e.stopEvent();
31162         
31163         this.startScale = this.scale;
31164         
31165         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31166         
31167         if(!this.zoomable()){
31168             this.scale = this.startScale;
31169             return;
31170         }
31171         
31172         this.draw();
31173         
31174         return;
31175     },
31176     
31177     zoomable : function()
31178     {
31179         var minScale = this.thumbEl.getWidth() / this.minWidth;
31180         
31181         if(this.minWidth < this.minHeight){
31182             minScale = this.thumbEl.getHeight() / this.minHeight;
31183         }
31184         
31185         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31186         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31187         
31188         if(
31189                 this.isDocument &&
31190                 (this.rotate == 0 || this.rotate == 180) && 
31191                 (
31192                     width > this.imageEl.OriginWidth || 
31193                     height > this.imageEl.OriginHeight ||
31194                     (width < this.minWidth && height < this.minHeight)
31195                 )
31196         ){
31197             return false;
31198         }
31199         
31200         if(
31201                 this.isDocument &&
31202                 (this.rotate == 90 || this.rotate == 270) && 
31203                 (
31204                     width > this.imageEl.OriginWidth || 
31205                     height > this.imageEl.OriginHeight ||
31206                     (width < this.minHeight && height < this.minWidth)
31207                 )
31208         ){
31209             return false;
31210         }
31211         
31212         if(
31213                 !this.isDocument &&
31214                 (this.rotate == 0 || this.rotate == 180) && 
31215                 (
31216                     width < this.minWidth || 
31217                     width > this.imageEl.OriginWidth || 
31218                     height < this.minHeight || 
31219                     height > this.imageEl.OriginHeight
31220                 )
31221         ){
31222             return false;
31223         }
31224         
31225         if(
31226                 !this.isDocument &&
31227                 (this.rotate == 90 || this.rotate == 270) && 
31228                 (
31229                     width < this.minHeight || 
31230                     width > this.imageEl.OriginWidth || 
31231                     height < this.minWidth || 
31232                     height > this.imageEl.OriginHeight
31233                 )
31234         ){
31235             return false;
31236         }
31237         
31238         return true;
31239         
31240     },
31241     
31242     onRotateLeft : function(e)
31243     {   
31244         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31245             
31246             var minScale = this.thumbEl.getWidth() / this.minWidth;
31247             
31248             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31249             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31250             
31251             this.startScale = this.scale;
31252             
31253             while (this.getScaleLevel() < minScale){
31254             
31255                 this.scale = this.scale + 1;
31256                 
31257                 if(!this.zoomable()){
31258                     break;
31259                 }
31260                 
31261                 if(
31262                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31263                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31264                 ){
31265                     continue;
31266                 }
31267                 
31268                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31269
31270                 this.draw();
31271                 
31272                 return;
31273             }
31274             
31275             this.scale = this.startScale;
31276             
31277             this.onRotateFail();
31278             
31279             return false;
31280         }
31281         
31282         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31283
31284         if(this.isDocument){
31285             this.setThumbBoxSize();
31286             this.setThumbBoxPosition();
31287             this.setCanvasPosition();
31288         }
31289         
31290         this.draw();
31291         
31292         this.fireEvent('rotate', this, 'left');
31293         
31294     },
31295     
31296     onRotateRight : function(e)
31297     {
31298         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31299             
31300             var minScale = this.thumbEl.getWidth() / this.minWidth;
31301         
31302             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31303             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31304             
31305             this.startScale = this.scale;
31306             
31307             while (this.getScaleLevel() < minScale){
31308             
31309                 this.scale = this.scale + 1;
31310                 
31311                 if(!this.zoomable()){
31312                     break;
31313                 }
31314                 
31315                 if(
31316                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31317                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31318                 ){
31319                     continue;
31320                 }
31321                 
31322                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31323
31324                 this.draw();
31325                 
31326                 return;
31327             }
31328             
31329             this.scale = this.startScale;
31330             
31331             this.onRotateFail();
31332             
31333             return false;
31334         }
31335         
31336         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31337
31338         if(this.isDocument){
31339             this.setThumbBoxSize();
31340             this.setThumbBoxPosition();
31341             this.setCanvasPosition();
31342         }
31343         
31344         this.draw();
31345         
31346         this.fireEvent('rotate', this, 'right');
31347     },
31348     
31349     onRotateFail : function()
31350     {
31351         this.errorEl.show(true);
31352         
31353         var _this = this;
31354         
31355         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31356     },
31357     
31358     draw : function()
31359     {
31360         this.previewEl.dom.innerHTML = '';
31361         
31362         var canvasEl = document.createElement("canvas");
31363         
31364         var contextEl = canvasEl.getContext("2d");
31365         
31366         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31367         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31368         var center = this.imageEl.OriginWidth / 2;
31369         
31370         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31371             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31372             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31373             center = this.imageEl.OriginHeight / 2;
31374         }
31375         
31376         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31377         
31378         contextEl.translate(center, center);
31379         contextEl.rotate(this.rotate * Math.PI / 180);
31380
31381         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31382         
31383         this.canvasEl = document.createElement("canvas");
31384         
31385         this.contextEl = this.canvasEl.getContext("2d");
31386         
31387         switch (this.rotate) {
31388             case 0 :
31389                 
31390                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31391                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31392                 
31393                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31394                 
31395                 break;
31396             case 90 : 
31397                 
31398                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31399                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31400                 
31401                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31402                     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);
31403                     break;
31404                 }
31405                 
31406                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31407                 
31408                 break;
31409             case 180 :
31410                 
31411                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31412                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31413                 
31414                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31415                     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);
31416                     break;
31417                 }
31418                 
31419                 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);
31420                 
31421                 break;
31422             case 270 :
31423                 
31424                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31425                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31426         
31427                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31428                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31429                     break;
31430                 }
31431                 
31432                 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);
31433                 
31434                 break;
31435             default : 
31436                 break;
31437         }
31438         
31439         this.previewEl.appendChild(this.canvasEl);
31440         
31441         this.setCanvasPosition();
31442     },
31443     
31444     crop : function()
31445     {
31446         if(!this.canvasLoaded){
31447             return;
31448         }
31449         
31450         var imageCanvas = document.createElement("canvas");
31451         
31452         var imageContext = imageCanvas.getContext("2d");
31453         
31454         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31455         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31456         
31457         var center = imageCanvas.width / 2;
31458         
31459         imageContext.translate(center, center);
31460         
31461         imageContext.rotate(this.rotate * Math.PI / 180);
31462         
31463         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31464         
31465         var canvas = document.createElement("canvas");
31466         
31467         var context = canvas.getContext("2d");
31468                 
31469         canvas.width = this.minWidth;
31470         canvas.height = this.minHeight;
31471
31472         switch (this.rotate) {
31473             case 0 :
31474                 
31475                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31476                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31477                 
31478                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31479                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31480                 
31481                 var targetWidth = this.minWidth - 2 * x;
31482                 var targetHeight = this.minHeight - 2 * y;
31483                 
31484                 var scale = 1;
31485                 
31486                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31487                     scale = targetWidth / width;
31488                 }
31489                 
31490                 if(x > 0 && y == 0){
31491                     scale = targetHeight / height;
31492                 }
31493                 
31494                 if(x > 0 && y > 0){
31495                     scale = targetWidth / width;
31496                     
31497                     if(width < height){
31498                         scale = targetHeight / height;
31499                     }
31500                 }
31501                 
31502                 context.scale(scale, scale);
31503                 
31504                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31505                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31506
31507                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31508                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31509
31510                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31511                 
31512                 break;
31513             case 90 : 
31514                 
31515                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31516                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31517                 
31518                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31519                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31520                 
31521                 var targetWidth = this.minWidth - 2 * x;
31522                 var targetHeight = this.minHeight - 2 * y;
31523                 
31524                 var scale = 1;
31525                 
31526                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31527                     scale = targetWidth / width;
31528                 }
31529                 
31530                 if(x > 0 && y == 0){
31531                     scale = targetHeight / height;
31532                 }
31533                 
31534                 if(x > 0 && y > 0){
31535                     scale = targetWidth / width;
31536                     
31537                     if(width < height){
31538                         scale = targetHeight / height;
31539                     }
31540                 }
31541                 
31542                 context.scale(scale, scale);
31543                 
31544                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31545                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31546
31547                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31548                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31549                 
31550                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31551                 
31552                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31553                 
31554                 break;
31555             case 180 :
31556                 
31557                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31558                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31559                 
31560                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31561                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31562                 
31563                 var targetWidth = this.minWidth - 2 * x;
31564                 var targetHeight = this.minHeight - 2 * y;
31565                 
31566                 var scale = 1;
31567                 
31568                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31569                     scale = targetWidth / width;
31570                 }
31571                 
31572                 if(x > 0 && y == 0){
31573                     scale = targetHeight / height;
31574                 }
31575                 
31576                 if(x > 0 && y > 0){
31577                     scale = targetWidth / width;
31578                     
31579                     if(width < height){
31580                         scale = targetHeight / height;
31581                     }
31582                 }
31583                 
31584                 context.scale(scale, scale);
31585                 
31586                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31587                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31588
31589                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31590                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31591
31592                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31593                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31594                 
31595                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31596                 
31597                 break;
31598             case 270 :
31599                 
31600                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31601                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31602                 
31603                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31604                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31605                 
31606                 var targetWidth = this.minWidth - 2 * x;
31607                 var targetHeight = this.minHeight - 2 * y;
31608                 
31609                 var scale = 1;
31610                 
31611                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31612                     scale = targetWidth / width;
31613                 }
31614                 
31615                 if(x > 0 && y == 0){
31616                     scale = targetHeight / height;
31617                 }
31618                 
31619                 if(x > 0 && y > 0){
31620                     scale = targetWidth / width;
31621                     
31622                     if(width < height){
31623                         scale = targetHeight / height;
31624                     }
31625                 }
31626                 
31627                 context.scale(scale, scale);
31628                 
31629                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31630                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31631
31632                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31633                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31634                 
31635                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31636                 
31637                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31638                 
31639                 break;
31640             default : 
31641                 break;
31642         }
31643         
31644         this.cropData = canvas.toDataURL(this.cropType);
31645         
31646         if(this.fireEvent('crop', this, this.cropData) !== false){
31647             this.process(this.file, this.cropData);
31648         }
31649         
31650         return;
31651         
31652     },
31653     
31654     setThumbBoxSize : function()
31655     {
31656         var width, height;
31657         
31658         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31659             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31660             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31661             
31662             this.minWidth = width;
31663             this.minHeight = height;
31664             
31665             if(this.rotate == 90 || this.rotate == 270){
31666                 this.minWidth = height;
31667                 this.minHeight = width;
31668             }
31669         }
31670         
31671         height = 300;
31672         width = Math.ceil(this.minWidth * height / this.minHeight);
31673         
31674         if(this.minWidth > this.minHeight){
31675             width = 300;
31676             height = Math.ceil(this.minHeight * width / this.minWidth);
31677         }
31678         
31679         this.thumbEl.setStyle({
31680             width : width + 'px',
31681             height : height + 'px'
31682         });
31683
31684         return;
31685             
31686     },
31687     
31688     setThumbBoxPosition : function()
31689     {
31690         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31691         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31692         
31693         this.thumbEl.setLeft(x);
31694         this.thumbEl.setTop(y);
31695         
31696     },
31697     
31698     baseRotateLevel : function()
31699     {
31700         this.baseRotate = 1;
31701         
31702         if(
31703                 typeof(this.exif) != 'undefined' &&
31704                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31705                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31706         ){
31707             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31708         }
31709         
31710         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31711         
31712     },
31713     
31714     baseScaleLevel : function()
31715     {
31716         var width, height;
31717         
31718         if(this.isDocument){
31719             
31720             if(this.baseRotate == 6 || this.baseRotate == 8){
31721             
31722                 height = this.thumbEl.getHeight();
31723                 this.baseScale = height / this.imageEl.OriginWidth;
31724
31725                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31726                     width = this.thumbEl.getWidth();
31727                     this.baseScale = width / this.imageEl.OriginHeight;
31728                 }
31729
31730                 return;
31731             }
31732
31733             height = this.thumbEl.getHeight();
31734             this.baseScale = height / this.imageEl.OriginHeight;
31735
31736             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31737                 width = this.thumbEl.getWidth();
31738                 this.baseScale = width / this.imageEl.OriginWidth;
31739             }
31740
31741             return;
31742         }
31743         
31744         if(this.baseRotate == 6 || this.baseRotate == 8){
31745             
31746             width = this.thumbEl.getHeight();
31747             this.baseScale = width / this.imageEl.OriginHeight;
31748             
31749             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31750                 height = this.thumbEl.getWidth();
31751                 this.baseScale = height / this.imageEl.OriginHeight;
31752             }
31753             
31754             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31755                 height = this.thumbEl.getWidth();
31756                 this.baseScale = height / this.imageEl.OriginHeight;
31757                 
31758                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31759                     width = this.thumbEl.getHeight();
31760                     this.baseScale = width / this.imageEl.OriginWidth;
31761                 }
31762             }
31763             
31764             return;
31765         }
31766         
31767         width = this.thumbEl.getWidth();
31768         this.baseScale = width / this.imageEl.OriginWidth;
31769         
31770         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31771             height = this.thumbEl.getHeight();
31772             this.baseScale = height / this.imageEl.OriginHeight;
31773         }
31774         
31775         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31776             
31777             height = this.thumbEl.getHeight();
31778             this.baseScale = height / this.imageEl.OriginHeight;
31779             
31780             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31781                 width = this.thumbEl.getWidth();
31782                 this.baseScale = width / this.imageEl.OriginWidth;
31783             }
31784             
31785         }
31786         
31787         return;
31788     },
31789     
31790     getScaleLevel : function()
31791     {
31792         return this.baseScale * Math.pow(1.1, this.scale);
31793     },
31794     
31795     onTouchStart : function(e)
31796     {
31797         if(!this.canvasLoaded){
31798             this.beforeSelectFile(e);
31799             return;
31800         }
31801         
31802         var touches = e.browserEvent.touches;
31803         
31804         if(!touches){
31805             return;
31806         }
31807         
31808         if(touches.length == 1){
31809             this.onMouseDown(e);
31810             return;
31811         }
31812         
31813         if(touches.length != 2){
31814             return;
31815         }
31816         
31817         var coords = [];
31818         
31819         for(var i = 0, finger; finger = touches[i]; i++){
31820             coords.push(finger.pageX, finger.pageY);
31821         }
31822         
31823         var x = Math.pow(coords[0] - coords[2], 2);
31824         var y = Math.pow(coords[1] - coords[3], 2);
31825         
31826         this.startDistance = Math.sqrt(x + y);
31827         
31828         this.startScale = this.scale;
31829         
31830         this.pinching = true;
31831         this.dragable = false;
31832         
31833     },
31834     
31835     onTouchMove : function(e)
31836     {
31837         if(!this.pinching && !this.dragable){
31838             return;
31839         }
31840         
31841         var touches = e.browserEvent.touches;
31842         
31843         if(!touches){
31844             return;
31845         }
31846         
31847         if(this.dragable){
31848             this.onMouseMove(e);
31849             return;
31850         }
31851         
31852         var coords = [];
31853         
31854         for(var i = 0, finger; finger = touches[i]; i++){
31855             coords.push(finger.pageX, finger.pageY);
31856         }
31857         
31858         var x = Math.pow(coords[0] - coords[2], 2);
31859         var y = Math.pow(coords[1] - coords[3], 2);
31860         
31861         this.endDistance = Math.sqrt(x + y);
31862         
31863         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31864         
31865         if(!this.zoomable()){
31866             this.scale = this.startScale;
31867             return;
31868         }
31869         
31870         this.draw();
31871         
31872     },
31873     
31874     onTouchEnd : function(e)
31875     {
31876         this.pinching = false;
31877         this.dragable = false;
31878         
31879     },
31880     
31881     process : function(file, crop)
31882     {
31883         if(this.loadMask){
31884             this.maskEl.mask(this.loadingText);
31885         }
31886         
31887         this.xhr = new XMLHttpRequest();
31888         
31889         file.xhr = this.xhr;
31890
31891         this.xhr.open(this.method, this.url, true);
31892         
31893         var headers = {
31894             "Accept": "application/json",
31895             "Cache-Control": "no-cache",
31896             "X-Requested-With": "XMLHttpRequest"
31897         };
31898         
31899         for (var headerName in headers) {
31900             var headerValue = headers[headerName];
31901             if (headerValue) {
31902                 this.xhr.setRequestHeader(headerName, headerValue);
31903             }
31904         }
31905         
31906         var _this = this;
31907         
31908         this.xhr.onload = function()
31909         {
31910             _this.xhrOnLoad(_this.xhr);
31911         }
31912         
31913         this.xhr.onerror = function()
31914         {
31915             _this.xhrOnError(_this.xhr);
31916         }
31917         
31918         var formData = new FormData();
31919
31920         formData.append('returnHTML', 'NO');
31921         
31922         if(crop){
31923             formData.append('crop', crop);
31924         }
31925         
31926         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31927             formData.append(this.paramName, file, file.name);
31928         }
31929         
31930         if(typeof(file.filename) != 'undefined'){
31931             formData.append('filename', file.filename);
31932         }
31933         
31934         if(typeof(file.mimetype) != 'undefined'){
31935             formData.append('mimetype', file.mimetype);
31936         }
31937         
31938         if(this.fireEvent('arrange', this, formData) != false){
31939             this.xhr.send(formData);
31940         };
31941     },
31942     
31943     xhrOnLoad : function(xhr)
31944     {
31945         if(this.loadMask){
31946             this.maskEl.unmask();
31947         }
31948         
31949         if (xhr.readyState !== 4) {
31950             this.fireEvent('exception', this, xhr);
31951             return;
31952         }
31953
31954         var response = Roo.decode(xhr.responseText);
31955         
31956         if(!response.success){
31957             this.fireEvent('exception', this, xhr);
31958             return;
31959         }
31960         
31961         var response = Roo.decode(xhr.responseText);
31962         
31963         this.fireEvent('upload', this, response);
31964         
31965     },
31966     
31967     xhrOnError : function()
31968     {
31969         if(this.loadMask){
31970             this.maskEl.unmask();
31971         }
31972         
31973         Roo.log('xhr on error');
31974         
31975         var response = Roo.decode(xhr.responseText);
31976           
31977         Roo.log(response);
31978         
31979     },
31980     
31981     prepare : function(file)
31982     {   
31983         if(this.loadMask){
31984             this.maskEl.mask(this.loadingText);
31985         }
31986         
31987         this.file = false;
31988         this.exif = {};
31989         
31990         if(typeof(file) === 'string'){
31991             this.loadCanvas(file);
31992             return;
31993         }
31994         
31995         if(!file || !this.urlAPI){
31996             return;
31997         }
31998         
31999         this.file = file;
32000         this.cropType = file.type;
32001         
32002         var _this = this;
32003         
32004         if(this.fireEvent('prepare', this, this.file) != false){
32005             
32006             var reader = new FileReader();
32007             
32008             reader.onload = function (e) {
32009                 if (e.target.error) {
32010                     Roo.log(e.target.error);
32011                     return;
32012                 }
32013                 
32014                 var buffer = e.target.result,
32015                     dataView = new DataView(buffer),
32016                     offset = 2,
32017                     maxOffset = dataView.byteLength - 4,
32018                     markerBytes,
32019                     markerLength;
32020                 
32021                 if (dataView.getUint16(0) === 0xffd8) {
32022                     while (offset < maxOffset) {
32023                         markerBytes = dataView.getUint16(offset);
32024                         
32025                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32026                             markerLength = dataView.getUint16(offset + 2) + 2;
32027                             if (offset + markerLength > dataView.byteLength) {
32028                                 Roo.log('Invalid meta data: Invalid segment size.');
32029                                 break;
32030                             }
32031                             
32032                             if(markerBytes == 0xffe1){
32033                                 _this.parseExifData(
32034                                     dataView,
32035                                     offset,
32036                                     markerLength
32037                                 );
32038                             }
32039                             
32040                             offset += markerLength;
32041                             
32042                             continue;
32043                         }
32044                         
32045                         break;
32046                     }
32047                     
32048                 }
32049                 
32050                 var url = _this.urlAPI.createObjectURL(_this.file);
32051                 
32052                 _this.loadCanvas(url);
32053                 
32054                 return;
32055             }
32056             
32057             reader.readAsArrayBuffer(this.file);
32058             
32059         }
32060         
32061     },
32062     
32063     parseExifData : function(dataView, offset, length)
32064     {
32065         var tiffOffset = offset + 10,
32066             littleEndian,
32067             dirOffset;
32068     
32069         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32070             // No Exif data, might be XMP data instead
32071             return;
32072         }
32073         
32074         // Check for the ASCII code for "Exif" (0x45786966):
32075         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32076             // No Exif data, might be XMP data instead
32077             return;
32078         }
32079         if (tiffOffset + 8 > dataView.byteLength) {
32080             Roo.log('Invalid Exif data: Invalid segment size.');
32081             return;
32082         }
32083         // Check for the two null bytes:
32084         if (dataView.getUint16(offset + 8) !== 0x0000) {
32085             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32086             return;
32087         }
32088         // Check the byte alignment:
32089         switch (dataView.getUint16(tiffOffset)) {
32090         case 0x4949:
32091             littleEndian = true;
32092             break;
32093         case 0x4D4D:
32094             littleEndian = false;
32095             break;
32096         default:
32097             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32098             return;
32099         }
32100         // Check for the TIFF tag marker (0x002A):
32101         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32102             Roo.log('Invalid Exif data: Missing TIFF marker.');
32103             return;
32104         }
32105         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32106         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32107         
32108         this.parseExifTags(
32109             dataView,
32110             tiffOffset,
32111             tiffOffset + dirOffset,
32112             littleEndian
32113         );
32114     },
32115     
32116     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32117     {
32118         var tagsNumber,
32119             dirEndOffset,
32120             i;
32121         if (dirOffset + 6 > dataView.byteLength) {
32122             Roo.log('Invalid Exif data: Invalid directory offset.');
32123             return;
32124         }
32125         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32126         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32127         if (dirEndOffset + 4 > dataView.byteLength) {
32128             Roo.log('Invalid Exif data: Invalid directory size.');
32129             return;
32130         }
32131         for (i = 0; i < tagsNumber; i += 1) {
32132             this.parseExifTag(
32133                 dataView,
32134                 tiffOffset,
32135                 dirOffset + 2 + 12 * i, // tag offset
32136                 littleEndian
32137             );
32138         }
32139         // Return the offset to the next directory:
32140         return dataView.getUint32(dirEndOffset, littleEndian);
32141     },
32142     
32143     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32144     {
32145         var tag = dataView.getUint16(offset, littleEndian);
32146         
32147         this.exif[tag] = this.getExifValue(
32148             dataView,
32149             tiffOffset,
32150             offset,
32151             dataView.getUint16(offset + 2, littleEndian), // tag type
32152             dataView.getUint32(offset + 4, littleEndian), // tag length
32153             littleEndian
32154         );
32155     },
32156     
32157     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32158     {
32159         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32160             tagSize,
32161             dataOffset,
32162             values,
32163             i,
32164             str,
32165             c;
32166     
32167         if (!tagType) {
32168             Roo.log('Invalid Exif data: Invalid tag type.');
32169             return;
32170         }
32171         
32172         tagSize = tagType.size * length;
32173         // Determine if the value is contained in the dataOffset bytes,
32174         // or if the value at the dataOffset is a pointer to the actual data:
32175         dataOffset = tagSize > 4 ?
32176                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32177         if (dataOffset + tagSize > dataView.byteLength) {
32178             Roo.log('Invalid Exif data: Invalid data offset.');
32179             return;
32180         }
32181         if (length === 1) {
32182             return tagType.getValue(dataView, dataOffset, littleEndian);
32183         }
32184         values = [];
32185         for (i = 0; i < length; i += 1) {
32186             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32187         }
32188         
32189         if (tagType.ascii) {
32190             str = '';
32191             // Concatenate the chars:
32192             for (i = 0; i < values.length; i += 1) {
32193                 c = values[i];
32194                 // Ignore the terminating NULL byte(s):
32195                 if (c === '\u0000') {
32196                     break;
32197                 }
32198                 str += c;
32199             }
32200             return str;
32201         }
32202         return values;
32203     }
32204     
32205 });
32206
32207 Roo.apply(Roo.bootstrap.UploadCropbox, {
32208     tags : {
32209         'Orientation': 0x0112
32210     },
32211     
32212     Orientation: {
32213             1: 0, //'top-left',
32214 //            2: 'top-right',
32215             3: 180, //'bottom-right',
32216 //            4: 'bottom-left',
32217 //            5: 'left-top',
32218             6: 90, //'right-top',
32219 //            7: 'right-bottom',
32220             8: 270 //'left-bottom'
32221     },
32222     
32223     exifTagTypes : {
32224         // byte, 8-bit unsigned int:
32225         1: {
32226             getValue: function (dataView, dataOffset) {
32227                 return dataView.getUint8(dataOffset);
32228             },
32229             size: 1
32230         },
32231         // ascii, 8-bit byte:
32232         2: {
32233             getValue: function (dataView, dataOffset) {
32234                 return String.fromCharCode(dataView.getUint8(dataOffset));
32235             },
32236             size: 1,
32237             ascii: true
32238         },
32239         // short, 16 bit int:
32240         3: {
32241             getValue: function (dataView, dataOffset, littleEndian) {
32242                 return dataView.getUint16(dataOffset, littleEndian);
32243             },
32244             size: 2
32245         },
32246         // long, 32 bit int:
32247         4: {
32248             getValue: function (dataView, dataOffset, littleEndian) {
32249                 return dataView.getUint32(dataOffset, littleEndian);
32250             },
32251             size: 4
32252         },
32253         // rational = two long values, first is numerator, second is denominator:
32254         5: {
32255             getValue: function (dataView, dataOffset, littleEndian) {
32256                 return dataView.getUint32(dataOffset, littleEndian) /
32257                     dataView.getUint32(dataOffset + 4, littleEndian);
32258             },
32259             size: 8
32260         },
32261         // slong, 32 bit signed int:
32262         9: {
32263             getValue: function (dataView, dataOffset, littleEndian) {
32264                 return dataView.getInt32(dataOffset, littleEndian);
32265             },
32266             size: 4
32267         },
32268         // srational, two slongs, first is numerator, second is denominator:
32269         10: {
32270             getValue: function (dataView, dataOffset, littleEndian) {
32271                 return dataView.getInt32(dataOffset, littleEndian) /
32272                     dataView.getInt32(dataOffset + 4, littleEndian);
32273             },
32274             size: 8
32275         }
32276     },
32277     
32278     footer : {
32279         STANDARD : [
32280             {
32281                 tag : 'div',
32282                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32283                 action : 'rotate-left',
32284                 cn : [
32285                     {
32286                         tag : 'button',
32287                         cls : 'btn btn-default',
32288                         html : '<i class="fa fa-undo"></i>'
32289                     }
32290                 ]
32291             },
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-picture',
32295                 action : 'picture',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-picture-o"></i>'
32301                     }
32302                 ]
32303             },
32304             {
32305                 tag : 'div',
32306                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32307                 action : 'rotate-right',
32308                 cn : [
32309                     {
32310                         tag : 'button',
32311                         cls : 'btn btn-default',
32312                         html : '<i class="fa fa-repeat"></i>'
32313                     }
32314                 ]
32315             }
32316         ],
32317         DOCUMENT : [
32318             {
32319                 tag : 'div',
32320                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32321                 action : 'rotate-left',
32322                 cn : [
32323                     {
32324                         tag : 'button',
32325                         cls : 'btn btn-default',
32326                         html : '<i class="fa fa-undo"></i>'
32327                     }
32328                 ]
32329             },
32330             {
32331                 tag : 'div',
32332                 cls : 'btn-group roo-upload-cropbox-download',
32333                 action : 'download',
32334                 cn : [
32335                     {
32336                         tag : 'button',
32337                         cls : 'btn btn-default',
32338                         html : '<i class="fa fa-download"></i>'
32339                     }
32340                 ]
32341             },
32342             {
32343                 tag : 'div',
32344                 cls : 'btn-group roo-upload-cropbox-crop',
32345                 action : 'crop',
32346                 cn : [
32347                     {
32348                         tag : 'button',
32349                         cls : 'btn btn-default',
32350                         html : '<i class="fa fa-crop"></i>'
32351                     }
32352                 ]
32353             },
32354             {
32355                 tag : 'div',
32356                 cls : 'btn-group roo-upload-cropbox-trash',
32357                 action : 'trash',
32358                 cn : [
32359                     {
32360                         tag : 'button',
32361                         cls : 'btn btn-default',
32362                         html : '<i class="fa fa-trash"></i>'
32363                     }
32364                 ]
32365             },
32366             {
32367                 tag : 'div',
32368                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32369                 action : 'rotate-right',
32370                 cn : [
32371                     {
32372                         tag : 'button',
32373                         cls : 'btn btn-default',
32374                         html : '<i class="fa fa-repeat"></i>'
32375                     }
32376                 ]
32377             }
32378         ],
32379         ROTATOR : [
32380             {
32381                 tag : 'div',
32382                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32383                 action : 'rotate-left',
32384                 cn : [
32385                     {
32386                         tag : 'button',
32387                         cls : 'btn btn-default',
32388                         html : '<i class="fa fa-undo"></i>'
32389                     }
32390                 ]
32391             },
32392             {
32393                 tag : 'div',
32394                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32395                 action : 'rotate-right',
32396                 cn : [
32397                     {
32398                         tag : 'button',
32399                         cls : 'btn btn-default',
32400                         html : '<i class="fa fa-repeat"></i>'
32401                     }
32402                 ]
32403             }
32404         ]
32405     }
32406 });
32407
32408 /*
32409 * Licence: LGPL
32410 */
32411
32412 /**
32413  * @class Roo.bootstrap.DocumentManager
32414  * @extends Roo.bootstrap.Component
32415  * Bootstrap DocumentManager class
32416  * @cfg {String} paramName default 'imageUpload'
32417  * @cfg {String} toolTipName default 'filename'
32418  * @cfg {String} method default POST
32419  * @cfg {String} url action url
32420  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32421  * @cfg {Boolean} multiple multiple upload default true
32422  * @cfg {Number} thumbSize default 300
32423  * @cfg {String} fieldLabel
32424  * @cfg {Number} labelWidth default 4
32425  * @cfg {String} labelAlign (left|top) default left
32426  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32427 * @cfg {Number} labellg set the width of label (1-12)
32428  * @cfg {Number} labelmd set the width of label (1-12)
32429  * @cfg {Number} labelsm set the width of label (1-12)
32430  * @cfg {Number} labelxs set the width of label (1-12)
32431  * 
32432  * @constructor
32433  * Create a new DocumentManager
32434  * @param {Object} config The config object
32435  */
32436
32437 Roo.bootstrap.DocumentManager = function(config){
32438     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32439     
32440     this.files = [];
32441     this.delegates = [];
32442     
32443     this.addEvents({
32444         /**
32445          * @event initial
32446          * Fire when initial the DocumentManager
32447          * @param {Roo.bootstrap.DocumentManager} this
32448          */
32449         "initial" : true,
32450         /**
32451          * @event inspect
32452          * inspect selected file
32453          * @param {Roo.bootstrap.DocumentManager} this
32454          * @param {File} file
32455          */
32456         "inspect" : true,
32457         /**
32458          * @event exception
32459          * Fire when xhr load exception
32460          * @param {Roo.bootstrap.DocumentManager} this
32461          * @param {XMLHttpRequest} xhr
32462          */
32463         "exception" : true,
32464         /**
32465          * @event afterupload
32466          * Fire when xhr load exception
32467          * @param {Roo.bootstrap.DocumentManager} this
32468          * @param {XMLHttpRequest} xhr
32469          */
32470         "afterupload" : true,
32471         /**
32472          * @event prepare
32473          * prepare the form data
32474          * @param {Roo.bootstrap.DocumentManager} this
32475          * @param {Object} formData
32476          */
32477         "prepare" : true,
32478         /**
32479          * @event remove
32480          * Fire when remove the file
32481          * @param {Roo.bootstrap.DocumentManager} this
32482          * @param {Object} file
32483          */
32484         "remove" : true,
32485         /**
32486          * @event refresh
32487          * Fire after refresh the file
32488          * @param {Roo.bootstrap.DocumentManager} this
32489          */
32490         "refresh" : true,
32491         /**
32492          * @event click
32493          * Fire after click the image
32494          * @param {Roo.bootstrap.DocumentManager} this
32495          * @param {Object} file
32496          */
32497         "click" : true,
32498         /**
32499          * @event edit
32500          * Fire when upload a image and editable set to true
32501          * @param {Roo.bootstrap.DocumentManager} this
32502          * @param {Object} file
32503          */
32504         "edit" : true,
32505         /**
32506          * @event beforeselectfile
32507          * Fire before select file
32508          * @param {Roo.bootstrap.DocumentManager} this
32509          */
32510         "beforeselectfile" : true,
32511         /**
32512          * @event process
32513          * Fire before process file
32514          * @param {Roo.bootstrap.DocumentManager} this
32515          * @param {Object} file
32516          */
32517         "process" : true,
32518         /**
32519          * @event previewrendered
32520          * Fire when preview rendered
32521          * @param {Roo.bootstrap.DocumentManager} this
32522          * @param {Object} file
32523          */
32524         "previewrendered" : true,
32525         /**
32526          */
32527         "previewResize" : true
32528         
32529     });
32530 };
32531
32532 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32533     
32534     boxes : 0,
32535     inputName : '',
32536     thumbSize : 300,
32537     multiple : true,
32538     files : false,
32539     method : 'POST',
32540     url : '',
32541     paramName : 'imageUpload',
32542     toolTipName : 'filename',
32543     fieldLabel : '',
32544     labelWidth : 4,
32545     labelAlign : 'left',
32546     editable : true,
32547     delegates : false,
32548     xhr : false, 
32549     
32550     labellg : 0,
32551     labelmd : 0,
32552     labelsm : 0,
32553     labelxs : 0,
32554     
32555     getAutoCreate : function()
32556     {   
32557         var managerWidget = {
32558             tag : 'div',
32559             cls : 'roo-document-manager',
32560             cn : [
32561                 {
32562                     tag : 'input',
32563                     cls : 'roo-document-manager-selector',
32564                     type : 'file'
32565                 },
32566                 {
32567                     tag : 'div',
32568                     cls : 'roo-document-manager-uploader',
32569                     cn : [
32570                         {
32571                             tag : 'div',
32572                             cls : 'roo-document-manager-upload-btn',
32573                             html : '<i class="fa fa-plus"></i>'
32574                         }
32575                     ]
32576                     
32577                 }
32578             ]
32579         };
32580         
32581         var content = [
32582             {
32583                 tag : 'div',
32584                 cls : 'column col-md-12',
32585                 cn : managerWidget
32586             }
32587         ];
32588         
32589         if(this.fieldLabel.length){
32590             
32591             content = [
32592                 {
32593                     tag : 'div',
32594                     cls : 'column col-md-12',
32595                     html : this.fieldLabel
32596                 },
32597                 {
32598                     tag : 'div',
32599                     cls : 'column col-md-12',
32600                     cn : managerWidget
32601                 }
32602             ];
32603
32604             if(this.labelAlign == 'left'){
32605                 content = [
32606                     {
32607                         tag : 'div',
32608                         cls : 'column',
32609                         html : this.fieldLabel
32610                     },
32611                     {
32612                         tag : 'div',
32613                         cls : 'column',
32614                         cn : managerWidget
32615                     }
32616                 ];
32617                 
32618                 if(this.labelWidth > 12){
32619                     content[0].style = "width: " + this.labelWidth + 'px';
32620                 }
32621
32622                 if(this.labelWidth < 13 && this.labelmd == 0){
32623                     this.labelmd = this.labelWidth;
32624                 }
32625
32626                 if(this.labellg > 0){
32627                     content[0].cls += ' col-lg-' + this.labellg;
32628                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32629                 }
32630
32631                 if(this.labelmd > 0){
32632                     content[0].cls += ' col-md-' + this.labelmd;
32633                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32634                 }
32635
32636                 if(this.labelsm > 0){
32637                     content[0].cls += ' col-sm-' + this.labelsm;
32638                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32639                 }
32640
32641                 if(this.labelxs > 0){
32642                     content[0].cls += ' col-xs-' + this.labelxs;
32643                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32644                 }
32645                 
32646             }
32647         }
32648         
32649         var cfg = {
32650             tag : 'div',
32651             cls : 'row clearfix',
32652             cn : content
32653         };
32654         
32655         return cfg;
32656         
32657     },
32658     
32659     initEvents : function()
32660     {
32661         this.managerEl = this.el.select('.roo-document-manager', true).first();
32662         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32663         
32664         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32665         this.selectorEl.hide();
32666         
32667         if(this.multiple){
32668             this.selectorEl.attr('multiple', 'multiple');
32669         }
32670         
32671         this.selectorEl.on('change', this.onFileSelected, this);
32672         
32673         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32674         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32675         
32676         this.uploader.on('click', this.onUploaderClick, this);
32677         
32678         this.renderProgressDialog();
32679         
32680         var _this = this;
32681         
32682         window.addEventListener("resize", function() { _this.refresh(); } );
32683         
32684         this.fireEvent('initial', this);
32685     },
32686     
32687     renderProgressDialog : function()
32688     {
32689         var _this = this;
32690         
32691         this.progressDialog = new Roo.bootstrap.Modal({
32692             cls : 'roo-document-manager-progress-dialog',
32693             allow_close : false,
32694             animate : false,
32695             title : '',
32696             buttons : [
32697                 {
32698                     name  :'cancel',
32699                     weight : 'danger',
32700                     html : 'Cancel'
32701                 }
32702             ], 
32703             listeners : { 
32704                 btnclick : function() {
32705                     _this.uploadCancel();
32706                     this.hide();
32707                 }
32708             }
32709         });
32710          
32711         this.progressDialog.render(Roo.get(document.body));
32712          
32713         this.progress = new Roo.bootstrap.Progress({
32714             cls : 'roo-document-manager-progress',
32715             active : true,
32716             striped : true
32717         });
32718         
32719         this.progress.render(this.progressDialog.getChildContainer());
32720         
32721         this.progressBar = new Roo.bootstrap.ProgressBar({
32722             cls : 'roo-document-manager-progress-bar',
32723             aria_valuenow : 0,
32724             aria_valuemin : 0,
32725             aria_valuemax : 12,
32726             panel : 'success'
32727         });
32728         
32729         this.progressBar.render(this.progress.getChildContainer());
32730     },
32731     
32732     onUploaderClick : function(e)
32733     {
32734         e.preventDefault();
32735      
32736         if(this.fireEvent('beforeselectfile', this) != false){
32737             this.selectorEl.dom.click();
32738         }
32739         
32740     },
32741     
32742     onFileSelected : function(e)
32743     {
32744         e.preventDefault();
32745         
32746         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32747             return;
32748         }
32749         
32750         Roo.each(this.selectorEl.dom.files, function(file){
32751             if(this.fireEvent('inspect', this, file) != false){
32752                 this.files.push(file);
32753             }
32754         }, this);
32755         
32756         this.queue();
32757         
32758     },
32759     
32760     queue : function()
32761     {
32762         this.selectorEl.dom.value = '';
32763         
32764         if(!this.files || !this.files.length){
32765             return;
32766         }
32767         
32768         if(this.boxes > 0 && this.files.length > this.boxes){
32769             this.files = this.files.slice(0, this.boxes);
32770         }
32771         
32772         this.uploader.show();
32773         
32774         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32775             this.uploader.hide();
32776         }
32777         
32778         var _this = this;
32779         
32780         var files = [];
32781         
32782         var docs = [];
32783         
32784         Roo.each(this.files, function(file){
32785             
32786             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32787                 var f = this.renderPreview(file);
32788                 files.push(f);
32789                 return;
32790             }
32791             
32792             if(file.type.indexOf('image') != -1){
32793                 this.delegates.push(
32794                     (function(){
32795                         _this.process(file);
32796                     }).createDelegate(this)
32797                 );
32798         
32799                 return;
32800             }
32801             
32802             docs.push(
32803                 (function(){
32804                     _this.process(file);
32805                 }).createDelegate(this)
32806             );
32807             
32808         }, this);
32809         
32810         this.files = files;
32811         
32812         this.delegates = this.delegates.concat(docs);
32813         
32814         if(!this.delegates.length){
32815             this.refresh();
32816             return;
32817         }
32818         
32819         this.progressBar.aria_valuemax = this.delegates.length;
32820         
32821         this.arrange();
32822         
32823         return;
32824     },
32825     
32826     arrange : function()
32827     {
32828         if(!this.delegates.length){
32829             this.progressDialog.hide();
32830             this.refresh();
32831             return;
32832         }
32833         
32834         var delegate = this.delegates.shift();
32835         
32836         this.progressDialog.show();
32837         
32838         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32839         
32840         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32841         
32842         delegate();
32843     },
32844     
32845     refresh : function()
32846     {
32847         this.uploader.show();
32848         
32849         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32850             this.uploader.hide();
32851         }
32852         
32853         Roo.isTouch ? this.closable(false) : this.closable(true);
32854         
32855         this.fireEvent('refresh', this);
32856     },
32857     
32858     onRemove : function(e, el, o)
32859     {
32860         e.preventDefault();
32861         
32862         this.fireEvent('remove', this, o);
32863         
32864     },
32865     
32866     remove : function(o)
32867     {
32868         var files = [];
32869         
32870         Roo.each(this.files, function(file){
32871             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32872                 files.push(file);
32873                 return;
32874             }
32875
32876             o.target.remove();
32877
32878         }, this);
32879         
32880         this.files = files;
32881         
32882         this.refresh();
32883     },
32884     
32885     clear : function()
32886     {
32887         Roo.each(this.files, function(file){
32888             if(!file.target){
32889                 return;
32890             }
32891             
32892             file.target.remove();
32893
32894         }, this);
32895         
32896         this.files = [];
32897         
32898         this.refresh();
32899     },
32900     
32901     onClick : function(e, el, o)
32902     {
32903         e.preventDefault();
32904         
32905         this.fireEvent('click', this, o);
32906         
32907     },
32908     
32909     closable : function(closable)
32910     {
32911         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32912             
32913             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32914             
32915             if(closable){
32916                 el.show();
32917                 return;
32918             }
32919             
32920             el.hide();
32921             
32922         }, this);
32923     },
32924     
32925     xhrOnLoad : function(xhr)
32926     {
32927         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32928             el.remove();
32929         }, this);
32930         
32931         if (xhr.readyState !== 4) {
32932             this.arrange();
32933             this.fireEvent('exception', this, xhr);
32934             return;
32935         }
32936
32937         var response = Roo.decode(xhr.responseText);
32938         
32939         if(!response.success){
32940             this.arrange();
32941             this.fireEvent('exception', this, xhr);
32942             return;
32943         }
32944         
32945         var file = this.renderPreview(response.data);
32946         
32947         this.files.push(file);
32948         
32949         this.arrange();
32950         
32951         this.fireEvent('afterupload', this, xhr);
32952         
32953     },
32954     
32955     xhrOnError : function(xhr)
32956     {
32957         Roo.log('xhr on error');
32958         
32959         var response = Roo.decode(xhr.responseText);
32960           
32961         Roo.log(response);
32962         
32963         this.arrange();
32964     },
32965     
32966     process : function(file)
32967     {
32968         if(this.fireEvent('process', this, file) !== false){
32969             if(this.editable && file.type.indexOf('image') != -1){
32970                 this.fireEvent('edit', this, file);
32971                 return;
32972             }
32973
32974             this.uploadStart(file, false);
32975
32976             return;
32977         }
32978         
32979     },
32980     
32981     uploadStart : function(file, crop)
32982     {
32983         this.xhr = new XMLHttpRequest();
32984         
32985         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32986             this.arrange();
32987             return;
32988         }
32989         
32990         file.xhr = this.xhr;
32991             
32992         this.managerEl.createChild({
32993             tag : 'div',
32994             cls : 'roo-document-manager-loading',
32995             cn : [
32996                 {
32997                     tag : 'div',
32998                     tooltip : file.name,
32999                     cls : 'roo-document-manager-thumb',
33000                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33001                 }
33002             ]
33003
33004         });
33005
33006         this.xhr.open(this.method, this.url, true);
33007         
33008         var headers = {
33009             "Accept": "application/json",
33010             "Cache-Control": "no-cache",
33011             "X-Requested-With": "XMLHttpRequest"
33012         };
33013         
33014         for (var headerName in headers) {
33015             var headerValue = headers[headerName];
33016             if (headerValue) {
33017                 this.xhr.setRequestHeader(headerName, headerValue);
33018             }
33019         }
33020         
33021         var _this = this;
33022         
33023         this.xhr.onload = function()
33024         {
33025             _this.xhrOnLoad(_this.xhr);
33026         }
33027         
33028         this.xhr.onerror = function()
33029         {
33030             _this.xhrOnError(_this.xhr);
33031         }
33032         
33033         var formData = new FormData();
33034
33035         formData.append('returnHTML', 'NO');
33036         
33037         if(crop){
33038             formData.append('crop', crop);
33039         }
33040         
33041         formData.append(this.paramName, file, file.name);
33042         
33043         var options = {
33044             file : file, 
33045             manually : false
33046         };
33047         
33048         if(this.fireEvent('prepare', this, formData, options) != false){
33049             
33050             if(options.manually){
33051                 return;
33052             }
33053             
33054             this.xhr.send(formData);
33055             return;
33056         };
33057         
33058         this.uploadCancel();
33059     },
33060     
33061     uploadCancel : function()
33062     {
33063         if (this.xhr) {
33064             this.xhr.abort();
33065         }
33066         
33067         this.delegates = [];
33068         
33069         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33070             el.remove();
33071         }, this);
33072         
33073         this.arrange();
33074     },
33075     
33076     renderPreview : function(file)
33077     {
33078         if(typeof(file.target) != 'undefined' && file.target){
33079             return file;
33080         }
33081         
33082         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33083         
33084         var previewEl = this.managerEl.createChild({
33085             tag : 'div',
33086             cls : 'roo-document-manager-preview',
33087             cn : [
33088                 {
33089                     tag : 'div',
33090                     tooltip : file[this.toolTipName],
33091                     cls : 'roo-document-manager-thumb',
33092                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33093                 },
33094                 {
33095                     tag : 'button',
33096                     cls : 'close',
33097                     html : '<i class="fa fa-times-circle"></i>'
33098                 }
33099             ]
33100         });
33101
33102         var close = previewEl.select('button.close', true).first();
33103
33104         close.on('click', this.onRemove, this, file);
33105
33106         file.target = previewEl;
33107
33108         var image = previewEl.select('img', true).first();
33109         
33110         var _this = this;
33111         
33112         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33113         
33114         image.on('click', this.onClick, this, file);
33115         
33116         this.fireEvent('previewrendered', this, file);
33117         
33118         return file;
33119         
33120     },
33121     
33122     onPreviewLoad : function(file, image)
33123     {
33124         if(typeof(file.target) == 'undefined' || !file.target){
33125             return;
33126         }
33127         
33128         var width = image.dom.naturalWidth || image.dom.width;
33129         var height = image.dom.naturalHeight || image.dom.height;
33130         
33131         if(!this.previewResize) {
33132             return;
33133         }
33134         
33135         if(width > height){
33136             file.target.addClass('wide');
33137             return;
33138         }
33139         
33140         file.target.addClass('tall');
33141         return;
33142         
33143     },
33144     
33145     uploadFromSource : function(file, crop)
33146     {
33147         this.xhr = new XMLHttpRequest();
33148         
33149         this.managerEl.createChild({
33150             tag : 'div',
33151             cls : 'roo-document-manager-loading',
33152             cn : [
33153                 {
33154                     tag : 'div',
33155                     tooltip : file.name,
33156                     cls : 'roo-document-manager-thumb',
33157                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33158                 }
33159             ]
33160
33161         });
33162
33163         this.xhr.open(this.method, this.url, true);
33164         
33165         var headers = {
33166             "Accept": "application/json",
33167             "Cache-Control": "no-cache",
33168             "X-Requested-With": "XMLHttpRequest"
33169         };
33170         
33171         for (var headerName in headers) {
33172             var headerValue = headers[headerName];
33173             if (headerValue) {
33174                 this.xhr.setRequestHeader(headerName, headerValue);
33175             }
33176         }
33177         
33178         var _this = this;
33179         
33180         this.xhr.onload = function()
33181         {
33182             _this.xhrOnLoad(_this.xhr);
33183         }
33184         
33185         this.xhr.onerror = function()
33186         {
33187             _this.xhrOnError(_this.xhr);
33188         }
33189         
33190         var formData = new FormData();
33191
33192         formData.append('returnHTML', 'NO');
33193         
33194         formData.append('crop', crop);
33195         
33196         if(typeof(file.filename) != 'undefined'){
33197             formData.append('filename', file.filename);
33198         }
33199         
33200         if(typeof(file.mimetype) != 'undefined'){
33201             formData.append('mimetype', file.mimetype);
33202         }
33203         
33204         Roo.log(formData);
33205         
33206         if(this.fireEvent('prepare', this, formData) != false){
33207             this.xhr.send(formData);
33208         };
33209     }
33210 });
33211
33212 /*
33213 * Licence: LGPL
33214 */
33215
33216 /**
33217  * @class Roo.bootstrap.DocumentViewer
33218  * @extends Roo.bootstrap.Component
33219  * Bootstrap DocumentViewer class
33220  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33221  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33222  * 
33223  * @constructor
33224  * Create a new DocumentViewer
33225  * @param {Object} config The config object
33226  */
33227
33228 Roo.bootstrap.DocumentViewer = function(config){
33229     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33230     
33231     this.addEvents({
33232         /**
33233          * @event initial
33234          * Fire after initEvent
33235          * @param {Roo.bootstrap.DocumentViewer} this
33236          */
33237         "initial" : true,
33238         /**
33239          * @event click
33240          * Fire after click
33241          * @param {Roo.bootstrap.DocumentViewer} this
33242          */
33243         "click" : true,
33244         /**
33245          * @event download
33246          * Fire after download button
33247          * @param {Roo.bootstrap.DocumentViewer} this
33248          */
33249         "download" : true,
33250         /**
33251          * @event trash
33252          * Fire after trash button
33253          * @param {Roo.bootstrap.DocumentViewer} this
33254          */
33255         "trash" : true
33256         
33257     });
33258 };
33259
33260 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33261     
33262     showDownload : true,
33263     
33264     showTrash : true,
33265     
33266     getAutoCreate : function()
33267     {
33268         var cfg = {
33269             tag : 'div',
33270             cls : 'roo-document-viewer',
33271             cn : [
33272                 {
33273                     tag : 'div',
33274                     cls : 'roo-document-viewer-body',
33275                     cn : [
33276                         {
33277                             tag : 'div',
33278                             cls : 'roo-document-viewer-thumb',
33279                             cn : [
33280                                 {
33281                                     tag : 'img',
33282                                     cls : 'roo-document-viewer-image'
33283                                 }
33284                             ]
33285                         }
33286                     ]
33287                 },
33288                 {
33289                     tag : 'div',
33290                     cls : 'roo-document-viewer-footer',
33291                     cn : {
33292                         tag : 'div',
33293                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33294                         cn : [
33295                             {
33296                                 tag : 'div',
33297                                 cls : 'btn-group roo-document-viewer-download',
33298                                 cn : [
33299                                     {
33300                                         tag : 'button',
33301                                         cls : 'btn btn-default',
33302                                         html : '<i class="fa fa-download"></i>'
33303                                     }
33304                                 ]
33305                             },
33306                             {
33307                                 tag : 'div',
33308                                 cls : 'btn-group roo-document-viewer-trash',
33309                                 cn : [
33310                                     {
33311                                         tag : 'button',
33312                                         cls : 'btn btn-default',
33313                                         html : '<i class="fa fa-trash"></i>'
33314                                     }
33315                                 ]
33316                             }
33317                         ]
33318                     }
33319                 }
33320             ]
33321         };
33322         
33323         return cfg;
33324     },
33325     
33326     initEvents : function()
33327     {
33328         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33329         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33330         
33331         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33332         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33333         
33334         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33335         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33336         
33337         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33338         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33339         
33340         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33341         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33342         
33343         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33344         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33345         
33346         this.bodyEl.on('click', this.onClick, this);
33347         this.downloadBtn.on('click', this.onDownload, this);
33348         this.trashBtn.on('click', this.onTrash, this);
33349         
33350         this.downloadBtn.hide();
33351         this.trashBtn.hide();
33352         
33353         if(this.showDownload){
33354             this.downloadBtn.show();
33355         }
33356         
33357         if(this.showTrash){
33358             this.trashBtn.show();
33359         }
33360         
33361         if(!this.showDownload && !this.showTrash) {
33362             this.footerEl.hide();
33363         }
33364         
33365     },
33366     
33367     initial : function()
33368     {
33369         this.fireEvent('initial', this);
33370         
33371     },
33372     
33373     onClick : function(e)
33374     {
33375         e.preventDefault();
33376         
33377         this.fireEvent('click', this);
33378     },
33379     
33380     onDownload : function(e)
33381     {
33382         e.preventDefault();
33383         
33384         this.fireEvent('download', this);
33385     },
33386     
33387     onTrash : function(e)
33388     {
33389         e.preventDefault();
33390         
33391         this.fireEvent('trash', this);
33392     }
33393     
33394 });
33395 /*
33396  * - LGPL
33397  *
33398  * FieldLabel
33399  * 
33400  */
33401
33402 /**
33403  * @class Roo.bootstrap.form.FieldLabel
33404  * @extends Roo.bootstrap.Component
33405  * Bootstrap FieldLabel class
33406  * @cfg {String} html contents of the element
33407  * @cfg {String} tag tag of the element default label
33408  * @cfg {String} cls class of the element
33409  * @cfg {String} target label target 
33410  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33411  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33412  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33413  * @cfg {String} iconTooltip default "This field is required"
33414  * @cfg {String} indicatorpos (left|right) default left
33415  * 
33416  * @constructor
33417  * Create a new FieldLabel
33418  * @param {Object} config The config object
33419  */
33420
33421 Roo.bootstrap.form.FieldLabel = function(config){
33422     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33423     
33424     this.addEvents({
33425             /**
33426              * @event invalid
33427              * Fires after the field has been marked as invalid.
33428              * @param {Roo.form.FieldLabel} this
33429              * @param {String} msg The validation message
33430              */
33431             invalid : true,
33432             /**
33433              * @event valid
33434              * Fires after the field has been validated with no errors.
33435              * @param {Roo.form.FieldLabel} this
33436              */
33437             valid : true
33438         });
33439 };
33440
33441 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33442     
33443     tag: 'label',
33444     cls: '',
33445     html: '',
33446     target: '',
33447     allowBlank : true,
33448     invalidClass : 'has-warning',
33449     validClass : 'has-success',
33450     iconTooltip : 'This field is required',
33451     indicatorpos : 'left',
33452     
33453     getAutoCreate : function(){
33454         
33455         var cls = "";
33456         if (!this.allowBlank) {
33457             cls  = "visible";
33458         }
33459         
33460         var cfg = {
33461             tag : this.tag,
33462             cls : 'roo-bootstrap-field-label ' + this.cls,
33463             for : this.target,
33464             cn : [
33465                 {
33466                     tag : 'i',
33467                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33468                     tooltip : this.iconTooltip
33469                 },
33470                 {
33471                     tag : 'span',
33472                     html : this.html
33473                 }
33474             ] 
33475         };
33476         
33477         if(this.indicatorpos == 'right'){
33478             var cfg = {
33479                 tag : this.tag,
33480                 cls : 'roo-bootstrap-field-label ' + this.cls,
33481                 for : this.target,
33482                 cn : [
33483                     {
33484                         tag : 'span',
33485                         html : this.html
33486                     },
33487                     {
33488                         tag : 'i',
33489                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33490                         tooltip : this.iconTooltip
33491                     }
33492                 ] 
33493             };
33494         }
33495         
33496         return cfg;
33497     },
33498     
33499     initEvents: function() 
33500     {
33501         Roo.bootstrap.Element.superclass.initEvents.call(this);
33502         
33503         this.indicator = this.indicatorEl();
33504         
33505         if(this.indicator){
33506             this.indicator.removeClass('visible');
33507             this.indicator.addClass('invisible');
33508         }
33509         
33510         Roo.bootstrap.form.FieldLabel.register(this);
33511     },
33512     
33513     indicatorEl : function()
33514     {
33515         var indicator = this.el.select('i.roo-required-indicator',true).first();
33516         
33517         if(!indicator){
33518             return false;
33519         }
33520         
33521         return indicator;
33522         
33523     },
33524     
33525     /**
33526      * Mark this field as valid
33527      */
33528     markValid : function()
33529     {
33530         if(this.indicator){
33531             this.indicator.removeClass('visible');
33532             this.indicator.addClass('invisible');
33533         }
33534         if (Roo.bootstrap.version == 3) {
33535             this.el.removeClass(this.invalidClass);
33536             this.el.addClass(this.validClass);
33537         } else {
33538             this.el.removeClass('is-invalid');
33539             this.el.addClass('is-valid');
33540         }
33541         
33542         
33543         this.fireEvent('valid', this);
33544     },
33545     
33546     /**
33547      * Mark this field as invalid
33548      * @param {String} msg The validation message
33549      */
33550     markInvalid : function(msg)
33551     {
33552         if(this.indicator){
33553             this.indicator.removeClass('invisible');
33554             this.indicator.addClass('visible');
33555         }
33556           if (Roo.bootstrap.version == 3) {
33557             this.el.removeClass(this.validClass);
33558             this.el.addClass(this.invalidClass);
33559         } else {
33560             this.el.removeClass('is-valid');
33561             this.el.addClass('is-invalid');
33562         }
33563         
33564         
33565         this.fireEvent('invalid', this, msg);
33566     }
33567     
33568    
33569 });
33570
33571 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33572     
33573     groups: {},
33574     
33575      /**
33576     * register a FieldLabel Group
33577     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33578     */
33579     register : function(label)
33580     {
33581         if(this.groups.hasOwnProperty(label.target)){
33582             return;
33583         }
33584      
33585         this.groups[label.target] = label;
33586         
33587     },
33588     /**
33589     * fetch a FieldLabel Group based on the target
33590     * @param {string} target
33591     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33592     */
33593     get: function(target) {
33594         if (typeof(this.groups[target]) == 'undefined') {
33595             return false;
33596         }
33597         
33598         return this.groups[target] ;
33599     }
33600 });
33601
33602  
33603
33604  /*
33605  * - LGPL
33606  *
33607  * page DateSplitField.
33608  * 
33609  */
33610
33611
33612 /**
33613  * @class Roo.bootstrap.form.DateSplitField
33614  * @extends Roo.bootstrap.Component
33615  * Bootstrap DateSplitField class
33616  * @cfg {string} fieldLabel - the label associated
33617  * @cfg {Number} labelWidth set the width of label (0-12)
33618  * @cfg {String} labelAlign (top|left)
33619  * @cfg {Boolean} dayAllowBlank (true|false) default false
33620  * @cfg {Boolean} monthAllowBlank (true|false) default false
33621  * @cfg {Boolean} yearAllowBlank (true|false) default false
33622  * @cfg {string} dayPlaceholder 
33623  * @cfg {string} monthPlaceholder
33624  * @cfg {string} yearPlaceholder
33625  * @cfg {string} dayFormat default 'd'
33626  * @cfg {string} monthFormat default 'm'
33627  * @cfg {string} yearFormat default 'Y'
33628  * @cfg {Number} labellg set the width of label (1-12)
33629  * @cfg {Number} labelmd set the width of label (1-12)
33630  * @cfg {Number} labelsm set the width of label (1-12)
33631  * @cfg {Number} labelxs set the width of label (1-12)
33632
33633  *     
33634  * @constructor
33635  * Create a new DateSplitField
33636  * @param {Object} config The config object
33637  */
33638
33639 Roo.bootstrap.form.DateSplitField = function(config){
33640     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33641     
33642     this.addEvents({
33643         // raw events
33644          /**
33645          * @event years
33646          * getting the data of years
33647          * @param {Roo.bootstrap.form.DateSplitField} this
33648          * @param {Object} years
33649          */
33650         "years" : true,
33651         /**
33652          * @event days
33653          * getting the data of days
33654          * @param {Roo.bootstrap.form.DateSplitField} this
33655          * @param {Object} days
33656          */
33657         "days" : true,
33658         /**
33659          * @event invalid
33660          * Fires after the field has been marked as invalid.
33661          * @param {Roo.form.Field} this
33662          * @param {String} msg The validation message
33663          */
33664         invalid : true,
33665        /**
33666          * @event valid
33667          * Fires after the field has been validated with no errors.
33668          * @param {Roo.form.Field} this
33669          */
33670         valid : true
33671     });
33672 };
33673
33674 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33675     
33676     fieldLabel : '',
33677     labelAlign : 'top',
33678     labelWidth : 3,
33679     dayAllowBlank : false,
33680     monthAllowBlank : false,
33681     yearAllowBlank : false,
33682     dayPlaceholder : '',
33683     monthPlaceholder : '',
33684     yearPlaceholder : '',
33685     dayFormat : 'd',
33686     monthFormat : 'm',
33687     yearFormat : 'Y',
33688     isFormField : true,
33689     labellg : 0,
33690     labelmd : 0,
33691     labelsm : 0,
33692     labelxs : 0,
33693     
33694     getAutoCreate : function()
33695     {
33696         var cfg = {
33697             tag : 'div',
33698             cls : 'row roo-date-split-field-group',
33699             cn : [
33700                 {
33701                     tag : 'input',
33702                     type : 'hidden',
33703                     cls : 'form-hidden-field roo-date-split-field-group-value',
33704                     name : this.name
33705                 }
33706             ]
33707         };
33708         
33709         var labelCls = 'col-md-12';
33710         var contentCls = 'col-md-4';
33711         
33712         if(this.fieldLabel){
33713             
33714             var label = {
33715                 tag : 'div',
33716                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33717                 cn : [
33718                     {
33719                         tag : 'label',
33720                         html : this.fieldLabel
33721                     }
33722                 ]
33723             };
33724             
33725             if(this.labelAlign == 'left'){
33726             
33727                 if(this.labelWidth > 12){
33728                     label.style = "width: " + this.labelWidth + 'px';
33729                 }
33730
33731                 if(this.labelWidth < 13 && this.labelmd == 0){
33732                     this.labelmd = this.labelWidth;
33733                 }
33734
33735                 if(this.labellg > 0){
33736                     labelCls = ' col-lg-' + this.labellg;
33737                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33738                 }
33739
33740                 if(this.labelmd > 0){
33741                     labelCls = ' col-md-' + this.labelmd;
33742                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33743                 }
33744
33745                 if(this.labelsm > 0){
33746                     labelCls = ' col-sm-' + this.labelsm;
33747                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33748                 }
33749
33750                 if(this.labelxs > 0){
33751                     labelCls = ' col-xs-' + this.labelxs;
33752                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33753                 }
33754             }
33755             
33756             label.cls += ' ' + labelCls;
33757             
33758             cfg.cn.push(label);
33759         }
33760         
33761         Roo.each(['day', 'month', 'year'], function(t){
33762             cfg.cn.push({
33763                 tag : 'div',
33764                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33765             });
33766         }, this);
33767         
33768         return cfg;
33769     },
33770     
33771     inputEl: function ()
33772     {
33773         return this.el.select('.roo-date-split-field-group-value', true).first();
33774     },
33775     
33776     onRender : function(ct, position) 
33777     {
33778         var _this = this;
33779         
33780         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33781         
33782         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33783         
33784         this.dayField = new Roo.bootstrap.form.ComboBox({
33785             allowBlank : this.dayAllowBlank,
33786             alwaysQuery : true,
33787             displayField : 'value',
33788             editable : false,
33789             fieldLabel : '',
33790             forceSelection : true,
33791             mode : 'local',
33792             placeholder : this.dayPlaceholder,
33793             selectOnFocus : true,
33794             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33795             triggerAction : 'all',
33796             typeAhead : true,
33797             valueField : 'value',
33798             store : new Roo.data.SimpleStore({
33799                 data : (function() {    
33800                     var days = [];
33801                     _this.fireEvent('days', _this, days);
33802                     return days;
33803                 })(),
33804                 fields : [ 'value' ]
33805             }),
33806             listeners : {
33807                 select : function (_self, record, index)
33808                 {
33809                     _this.setValue(_this.getValue());
33810                 }
33811             }
33812         });
33813
33814         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33815         
33816         this.monthField = new Roo.bootstrap.form.MonthField({
33817             after : '<i class=\"fa fa-calendar\"></i>',
33818             allowBlank : this.monthAllowBlank,
33819             placeholder : this.monthPlaceholder,
33820             readOnly : true,
33821             listeners : {
33822                 render : function (_self)
33823                 {
33824                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33825                         e.preventDefault();
33826                         _self.focus();
33827                     });
33828                 },
33829                 select : function (_self, oldvalue, newvalue)
33830                 {
33831                     _this.setValue(_this.getValue());
33832                 }
33833             }
33834         });
33835         
33836         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33837         
33838         this.yearField = new Roo.bootstrap.form.ComboBox({
33839             allowBlank : this.yearAllowBlank,
33840             alwaysQuery : true,
33841             displayField : 'value',
33842             editable : false,
33843             fieldLabel : '',
33844             forceSelection : true,
33845             mode : 'local',
33846             placeholder : this.yearPlaceholder,
33847             selectOnFocus : true,
33848             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33849             triggerAction : 'all',
33850             typeAhead : true,
33851             valueField : 'value',
33852             store : new Roo.data.SimpleStore({
33853                 data : (function() {
33854                     var years = [];
33855                     _this.fireEvent('years', _this, years);
33856                     return years;
33857                 })(),
33858                 fields : [ 'value' ]
33859             }),
33860             listeners : {
33861                 select : function (_self, record, index)
33862                 {
33863                     _this.setValue(_this.getValue());
33864                 }
33865             }
33866         });
33867
33868         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33869     },
33870     
33871     setValue : function(v, format)
33872     {
33873         this.inputEl.dom.value = v;
33874         
33875         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33876         
33877         var d = Date.parseDate(v, f);
33878         
33879         if(!d){
33880             this.validate();
33881             return;
33882         }
33883         
33884         this.setDay(d.format(this.dayFormat));
33885         this.setMonth(d.format(this.monthFormat));
33886         this.setYear(d.format(this.yearFormat));
33887         
33888         this.validate();
33889         
33890         return;
33891     },
33892     
33893     setDay : function(v)
33894     {
33895         this.dayField.setValue(v);
33896         this.inputEl.dom.value = this.getValue();
33897         this.validate();
33898         return;
33899     },
33900     
33901     setMonth : function(v)
33902     {
33903         this.monthField.setValue(v, true);
33904         this.inputEl.dom.value = this.getValue();
33905         this.validate();
33906         return;
33907     },
33908     
33909     setYear : function(v)
33910     {
33911         this.yearField.setValue(v);
33912         this.inputEl.dom.value = this.getValue();
33913         this.validate();
33914         return;
33915     },
33916     
33917     getDay : function()
33918     {
33919         return this.dayField.getValue();
33920     },
33921     
33922     getMonth : function()
33923     {
33924         return this.monthField.getValue();
33925     },
33926     
33927     getYear : function()
33928     {
33929         return this.yearField.getValue();
33930     },
33931     
33932     getValue : function()
33933     {
33934         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33935         
33936         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33937         
33938         return date;
33939     },
33940     
33941     reset : function()
33942     {
33943         this.setDay('');
33944         this.setMonth('');
33945         this.setYear('');
33946         this.inputEl.dom.value = '';
33947         this.validate();
33948         return;
33949     },
33950     
33951     validate : function()
33952     {
33953         var d = this.dayField.validate();
33954         var m = this.monthField.validate();
33955         var y = this.yearField.validate();
33956         
33957         var valid = true;
33958         
33959         if(
33960                 (!this.dayAllowBlank && !d) ||
33961                 (!this.monthAllowBlank && !m) ||
33962                 (!this.yearAllowBlank && !y)
33963         ){
33964             valid = false;
33965         }
33966         
33967         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33968             return valid;
33969         }
33970         
33971         if(valid){
33972             this.markValid();
33973             return valid;
33974         }
33975         
33976         this.markInvalid();
33977         
33978         return valid;
33979     },
33980     
33981     markValid : function()
33982     {
33983         
33984         var label = this.el.select('label', true).first();
33985         var icon = this.el.select('i.fa-star', true).first();
33986
33987         if(label && icon){
33988             icon.remove();
33989         }
33990         
33991         this.fireEvent('valid', this);
33992     },
33993     
33994      /**
33995      * Mark this field as invalid
33996      * @param {String} msg The validation message
33997      */
33998     markInvalid : function(msg)
33999     {
34000         
34001         var label = this.el.select('label', true).first();
34002         var icon = this.el.select('i.fa-star', true).first();
34003
34004         if(label && !icon){
34005             this.el.select('.roo-date-split-field-label', true).createChild({
34006                 tag : 'i',
34007                 cls : 'text-danger fa fa-lg fa-star',
34008                 tooltip : 'This field is required',
34009                 style : 'margin-right:5px;'
34010             }, label, true);
34011         }
34012         
34013         this.fireEvent('invalid', this, msg);
34014     },
34015     
34016     clearInvalid : function()
34017     {
34018         var label = this.el.select('label', true).first();
34019         var icon = this.el.select('i.fa-star', true).first();
34020
34021         if(label && icon){
34022             icon.remove();
34023         }
34024         
34025         this.fireEvent('valid', this);
34026     },
34027     
34028     getName: function()
34029     {
34030         return this.name;
34031     }
34032     
34033 });
34034
34035  
34036
34037 /**
34038  * @class Roo.bootstrap.LayoutMasonry
34039  * @extends Roo.bootstrap.Component
34040  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34041  * Bootstrap Layout Masonry class
34042  *
34043  * This is based on 
34044  * http://masonry.desandro.com
34045  *
34046  * The idea is to render all the bricks based on vertical width...
34047  *
34048  * The original code extends 'outlayer' - we might need to use that....
34049
34050  * @constructor
34051  * Create a new Element
34052  * @param {Object} config The config object
34053  */
34054
34055 Roo.bootstrap.LayoutMasonry = function(config){
34056     
34057     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34058     
34059     this.bricks = [];
34060     
34061     Roo.bootstrap.LayoutMasonry.register(this);
34062     
34063     this.addEvents({
34064         // raw events
34065         /**
34066          * @event layout
34067          * Fire after layout the items
34068          * @param {Roo.bootstrap.LayoutMasonry} this
34069          * @param {Roo.EventObject} e
34070          */
34071         "layout" : true
34072     });
34073     
34074 };
34075
34076 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34077     
34078     /**
34079      * @cfg {Boolean} isLayoutInstant = no animation?
34080      */   
34081     isLayoutInstant : false, // needed?
34082    
34083     /**
34084      * @cfg {Number} boxWidth  width of the columns
34085      */   
34086     boxWidth : 450,
34087     
34088       /**
34089      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34090      */   
34091     boxHeight : 0,
34092     
34093     /**
34094      * @cfg {Number} padWidth padding below box..
34095      */   
34096     padWidth : 10, 
34097     
34098     /**
34099      * @cfg {Number} gutter gutter width..
34100      */   
34101     gutter : 10,
34102     
34103      /**
34104      * @cfg {Number} maxCols maximum number of columns
34105      */   
34106     
34107     maxCols: 0,
34108     
34109     /**
34110      * @cfg {Boolean} isAutoInitial defalut true
34111      */   
34112     isAutoInitial : true, 
34113     
34114     containerWidth: 0,
34115     
34116     /**
34117      * @cfg {Boolean} isHorizontal defalut false
34118      */   
34119     isHorizontal : false, 
34120
34121     currentSize : null,
34122     
34123     tag: 'div',
34124     
34125     cls: '',
34126     
34127     bricks: null, //CompositeElement
34128     
34129     cols : 1,
34130     
34131     _isLayoutInited : false,
34132     
34133 //    isAlternative : false, // only use for vertical layout...
34134     
34135     /**
34136      * @cfg {Number} alternativePadWidth padding below box..
34137      */   
34138     alternativePadWidth : 50,
34139     
34140     selectedBrick : [],
34141     
34142     getAutoCreate : function(){
34143         
34144         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34145         
34146         var cfg = {
34147             tag: this.tag,
34148             cls: 'blog-masonary-wrapper ' + this.cls,
34149             cn : {
34150                 cls : 'mas-boxes masonary'
34151             }
34152         };
34153         
34154         return cfg;
34155     },
34156     
34157     getChildContainer: function( )
34158     {
34159         if (this.boxesEl) {
34160             return this.boxesEl;
34161         }
34162         
34163         this.boxesEl = this.el.select('.mas-boxes').first();
34164         
34165         return this.boxesEl;
34166     },
34167     
34168     
34169     initEvents : function()
34170     {
34171         var _this = this;
34172         
34173         if(this.isAutoInitial){
34174             Roo.log('hook children rendered');
34175             this.on('childrenrendered', function() {
34176                 Roo.log('children rendered');
34177                 _this.initial();
34178             } ,this);
34179         }
34180     },
34181     
34182     initial : function()
34183     {
34184         this.selectedBrick = [];
34185         
34186         this.currentSize = this.el.getBox(true);
34187         
34188         Roo.EventManager.onWindowResize(this.resize, this); 
34189
34190         if(!this.isAutoInitial){
34191             this.layout();
34192             return;
34193         }
34194         
34195         this.layout();
34196         
34197         return;
34198         //this.layout.defer(500,this);
34199         
34200     },
34201     
34202     resize : function()
34203     {
34204         var cs = this.el.getBox(true);
34205         
34206         if (
34207                 this.currentSize.width == cs.width && 
34208                 this.currentSize.x == cs.x && 
34209                 this.currentSize.height == cs.height && 
34210                 this.currentSize.y == cs.y 
34211         ) {
34212             Roo.log("no change in with or X or Y");
34213             return;
34214         }
34215         
34216         this.currentSize = cs;
34217         
34218         this.layout();
34219         
34220     },
34221     
34222     layout : function()
34223     {   
34224         this._resetLayout();
34225         
34226         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34227         
34228         this.layoutItems( isInstant );
34229       
34230         this._isLayoutInited = true;
34231         
34232         this.fireEvent('layout', this);
34233         
34234     },
34235     
34236     _resetLayout : function()
34237     {
34238         if(this.isHorizontal){
34239             this.horizontalMeasureColumns();
34240             return;
34241         }
34242         
34243         this.verticalMeasureColumns();
34244         
34245     },
34246     
34247     verticalMeasureColumns : function()
34248     {
34249         this.getContainerWidth();
34250         
34251 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34252 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34253 //            return;
34254 //        }
34255         
34256         var boxWidth = this.boxWidth + this.padWidth;
34257         
34258         if(this.containerWidth < this.boxWidth){
34259             boxWidth = this.containerWidth
34260         }
34261         
34262         var containerWidth = this.containerWidth;
34263         
34264         var cols = Math.floor(containerWidth / boxWidth);
34265         
34266         this.cols = Math.max( cols, 1 );
34267         
34268         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34269         
34270         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34271         
34272         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34273         
34274         this.colWidth = boxWidth + avail - this.padWidth;
34275         
34276         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34277         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34278     },
34279     
34280     horizontalMeasureColumns : function()
34281     {
34282         this.getContainerWidth();
34283         
34284         var boxWidth = this.boxWidth;
34285         
34286         if(this.containerWidth < boxWidth){
34287             boxWidth = this.containerWidth;
34288         }
34289         
34290         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34291         
34292         this.el.setHeight(boxWidth);
34293         
34294     },
34295     
34296     getContainerWidth : function()
34297     {
34298         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34299     },
34300     
34301     layoutItems : function( isInstant )
34302     {
34303         Roo.log(this.bricks);
34304         
34305         var items = Roo.apply([], this.bricks);
34306         
34307         if(this.isHorizontal){
34308             this._horizontalLayoutItems( items , isInstant );
34309             return;
34310         }
34311         
34312 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34313 //            this._verticalAlternativeLayoutItems( items , isInstant );
34314 //            return;
34315 //        }
34316         
34317         this._verticalLayoutItems( items , isInstant );
34318         
34319     },
34320     
34321     _verticalLayoutItems : function ( items , isInstant)
34322     {
34323         if ( !items || !items.length ) {
34324             return;
34325         }
34326         
34327         var standard = [
34328             ['xs', 'xs', 'xs', 'tall'],
34329             ['xs', 'xs', 'tall'],
34330             ['xs', 'xs', 'sm'],
34331             ['xs', 'xs', 'xs'],
34332             ['xs', 'tall'],
34333             ['xs', 'sm'],
34334             ['xs', 'xs'],
34335             ['xs'],
34336             
34337             ['sm', 'xs', 'xs'],
34338             ['sm', 'xs'],
34339             ['sm'],
34340             
34341             ['tall', 'xs', 'xs', 'xs'],
34342             ['tall', 'xs', 'xs'],
34343             ['tall', 'xs'],
34344             ['tall']
34345             
34346         ];
34347         
34348         var queue = [];
34349         
34350         var boxes = [];
34351         
34352         var box = [];
34353         
34354         Roo.each(items, function(item, k){
34355             
34356             switch (item.size) {
34357                 // these layouts take up a full box,
34358                 case 'md' :
34359                 case 'md-left' :
34360                 case 'md-right' :
34361                 case 'wide' :
34362                     
34363                     if(box.length){
34364                         boxes.push(box);
34365                         box = [];
34366                     }
34367                     
34368                     boxes.push([item]);
34369                     
34370                     break;
34371                     
34372                 case 'xs' :
34373                 case 'sm' :
34374                 case 'tall' :
34375                     
34376                     box.push(item);
34377                     
34378                     break;
34379                 default :
34380                     break;
34381                     
34382             }
34383             
34384         }, this);
34385         
34386         if(box.length){
34387             boxes.push(box);
34388             box = [];
34389         }
34390         
34391         var filterPattern = function(box, length)
34392         {
34393             if(!box.length){
34394                 return;
34395             }
34396             
34397             var match = false;
34398             
34399             var pattern = box.slice(0, length);
34400             
34401             var format = [];
34402             
34403             Roo.each(pattern, function(i){
34404                 format.push(i.size);
34405             }, this);
34406             
34407             Roo.each(standard, function(s){
34408                 
34409                 if(String(s) != String(format)){
34410                     return;
34411                 }
34412                 
34413                 match = true;
34414                 return false;
34415                 
34416             }, this);
34417             
34418             if(!match && length == 1){
34419                 return;
34420             }
34421             
34422             if(!match){
34423                 filterPattern(box, length - 1);
34424                 return;
34425             }
34426                 
34427             queue.push(pattern);
34428
34429             box = box.slice(length, box.length);
34430
34431             filterPattern(box, 4);
34432
34433             return;
34434             
34435         }
34436         
34437         Roo.each(boxes, function(box, k){
34438             
34439             if(!box.length){
34440                 return;
34441             }
34442             
34443             if(box.length == 1){
34444                 queue.push(box);
34445                 return;
34446             }
34447             
34448             filterPattern(box, 4);
34449             
34450         }, this);
34451         
34452         this._processVerticalLayoutQueue( queue, isInstant );
34453         
34454     },
34455     
34456 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34457 //    {
34458 //        if ( !items || !items.length ) {
34459 //            return;
34460 //        }
34461 //
34462 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34463 //        
34464 //    },
34465     
34466     _horizontalLayoutItems : function ( items , isInstant)
34467     {
34468         if ( !items || !items.length || items.length < 3) {
34469             return;
34470         }
34471         
34472         items.reverse();
34473         
34474         var eItems = items.slice(0, 3);
34475         
34476         items = items.slice(3, items.length);
34477         
34478         var standard = [
34479             ['xs', 'xs', 'xs', 'wide'],
34480             ['xs', 'xs', 'wide'],
34481             ['xs', 'xs', 'sm'],
34482             ['xs', 'xs', 'xs'],
34483             ['xs', 'wide'],
34484             ['xs', 'sm'],
34485             ['xs', 'xs'],
34486             ['xs'],
34487             
34488             ['sm', 'xs', 'xs'],
34489             ['sm', 'xs'],
34490             ['sm'],
34491             
34492             ['wide', 'xs', 'xs', 'xs'],
34493             ['wide', 'xs', 'xs'],
34494             ['wide', 'xs'],
34495             ['wide'],
34496             
34497             ['wide-thin']
34498         ];
34499         
34500         var queue = [];
34501         
34502         var boxes = [];
34503         
34504         var box = [];
34505         
34506         Roo.each(items, function(item, k){
34507             
34508             switch (item.size) {
34509                 case 'md' :
34510                 case 'md-left' :
34511                 case 'md-right' :
34512                 case 'tall' :
34513                     
34514                     if(box.length){
34515                         boxes.push(box);
34516                         box = [];
34517                     }
34518                     
34519                     boxes.push([item]);
34520                     
34521                     break;
34522                     
34523                 case 'xs' :
34524                 case 'sm' :
34525                 case 'wide' :
34526                 case 'wide-thin' :
34527                     
34528                     box.push(item);
34529                     
34530                     break;
34531                 default :
34532                     break;
34533                     
34534             }
34535             
34536         }, this);
34537         
34538         if(box.length){
34539             boxes.push(box);
34540             box = [];
34541         }
34542         
34543         var filterPattern = function(box, length)
34544         {
34545             if(!box.length){
34546                 return;
34547             }
34548             
34549             var match = false;
34550             
34551             var pattern = box.slice(0, length);
34552             
34553             var format = [];
34554             
34555             Roo.each(pattern, function(i){
34556                 format.push(i.size);
34557             }, this);
34558             
34559             Roo.each(standard, function(s){
34560                 
34561                 if(String(s) != String(format)){
34562                     return;
34563                 }
34564                 
34565                 match = true;
34566                 return false;
34567                 
34568             }, this);
34569             
34570             if(!match && length == 1){
34571                 return;
34572             }
34573             
34574             if(!match){
34575                 filterPattern(box, length - 1);
34576                 return;
34577             }
34578                 
34579             queue.push(pattern);
34580
34581             box = box.slice(length, box.length);
34582
34583             filterPattern(box, 4);
34584
34585             return;
34586             
34587         }
34588         
34589         Roo.each(boxes, function(box, k){
34590             
34591             if(!box.length){
34592                 return;
34593             }
34594             
34595             if(box.length == 1){
34596                 queue.push(box);
34597                 return;
34598             }
34599             
34600             filterPattern(box, 4);
34601             
34602         }, this);
34603         
34604         
34605         var prune = [];
34606         
34607         var pos = this.el.getBox(true);
34608         
34609         var minX = pos.x;
34610         
34611         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34612         
34613         var hit_end = false;
34614         
34615         Roo.each(queue, function(box){
34616             
34617             if(hit_end){
34618                 
34619                 Roo.each(box, function(b){
34620                 
34621                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34622                     b.el.hide();
34623
34624                 }, this);
34625
34626                 return;
34627             }
34628             
34629             var mx = 0;
34630             
34631             Roo.each(box, function(b){
34632                 
34633                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34634                 b.el.show();
34635
34636                 mx = Math.max(mx, b.x);
34637                 
34638             }, this);
34639             
34640             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34641             
34642             if(maxX < minX){
34643                 
34644                 Roo.each(box, function(b){
34645                 
34646                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34647                     b.el.hide();
34648                     
34649                 }, this);
34650                 
34651                 hit_end = true;
34652                 
34653                 return;
34654             }
34655             
34656             prune.push(box);
34657             
34658         }, this);
34659         
34660         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34661     },
34662     
34663     /** Sets position of item in DOM
34664     * @param {Element} item
34665     * @param {Number} x - horizontal position
34666     * @param {Number} y - vertical position
34667     * @param {Boolean} isInstant - disables transitions
34668     */
34669     _processVerticalLayoutQueue : function( queue, isInstant )
34670     {
34671         var pos = this.el.getBox(true);
34672         var x = pos.x;
34673         var y = pos.y;
34674         var maxY = [];
34675         
34676         for (var i = 0; i < this.cols; i++){
34677             maxY[i] = pos.y;
34678         }
34679         
34680         Roo.each(queue, function(box, k){
34681             
34682             var col = k % this.cols;
34683             
34684             Roo.each(box, function(b,kk){
34685                 
34686                 b.el.position('absolute');
34687                 
34688                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34689                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34690                 
34691                 if(b.size == 'md-left' || b.size == 'md-right'){
34692                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34693                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34694                 }
34695                 
34696                 b.el.setWidth(width);
34697                 b.el.setHeight(height);
34698                 // iframe?
34699                 b.el.select('iframe',true).setSize(width,height);
34700                 
34701             }, this);
34702             
34703             for (var i = 0; i < this.cols; i++){
34704                 
34705                 if(maxY[i] < maxY[col]){
34706                     col = i;
34707                     continue;
34708                 }
34709                 
34710                 col = Math.min(col, i);
34711                 
34712             }
34713             
34714             x = pos.x + col * (this.colWidth + this.padWidth);
34715             
34716             y = maxY[col];
34717             
34718             var positions = [];
34719             
34720             switch (box.length){
34721                 case 1 :
34722                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34723                     break;
34724                 case 2 :
34725                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34726                     break;
34727                 case 3 :
34728                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34729                     break;
34730                 case 4 :
34731                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34732                     break;
34733                 default :
34734                     break;
34735             }
34736             
34737             Roo.each(box, function(b,kk){
34738                 
34739                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34740                 
34741                 var sz = b.el.getSize();
34742                 
34743                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34744                 
34745             }, this);
34746             
34747         }, this);
34748         
34749         var mY = 0;
34750         
34751         for (var i = 0; i < this.cols; i++){
34752             mY = Math.max(mY, maxY[i]);
34753         }
34754         
34755         this.el.setHeight(mY - pos.y);
34756         
34757     },
34758     
34759 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34760 //    {
34761 //        var pos = this.el.getBox(true);
34762 //        var x = pos.x;
34763 //        var y = pos.y;
34764 //        var maxX = pos.right;
34765 //        
34766 //        var maxHeight = 0;
34767 //        
34768 //        Roo.each(items, function(item, k){
34769 //            
34770 //            var c = k % 2;
34771 //            
34772 //            item.el.position('absolute');
34773 //                
34774 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34775 //
34776 //            item.el.setWidth(width);
34777 //
34778 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34779 //
34780 //            item.el.setHeight(height);
34781 //            
34782 //            if(c == 0){
34783 //                item.el.setXY([x, y], isInstant ? false : true);
34784 //            } else {
34785 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34786 //            }
34787 //            
34788 //            y = y + height + this.alternativePadWidth;
34789 //            
34790 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34791 //            
34792 //        }, this);
34793 //        
34794 //        this.el.setHeight(maxHeight);
34795 //        
34796 //    },
34797     
34798     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34799     {
34800         var pos = this.el.getBox(true);
34801         
34802         var minX = pos.x;
34803         var minY = pos.y;
34804         
34805         var maxX = pos.right;
34806         
34807         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34808         
34809         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34810         
34811         Roo.each(queue, function(box, k){
34812             
34813             Roo.each(box, function(b, kk){
34814                 
34815                 b.el.position('absolute');
34816                 
34817                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34818                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34819                 
34820                 if(b.size == 'md-left' || b.size == 'md-right'){
34821                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34822                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34823                 }
34824                 
34825                 b.el.setWidth(width);
34826                 b.el.setHeight(height);
34827                 
34828             }, this);
34829             
34830             if(!box.length){
34831                 return;
34832             }
34833             
34834             var positions = [];
34835             
34836             switch (box.length){
34837                 case 1 :
34838                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34839                     break;
34840                 case 2 :
34841                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34842                     break;
34843                 case 3 :
34844                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34845                     break;
34846                 case 4 :
34847                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34848                     break;
34849                 default :
34850                     break;
34851             }
34852             
34853             Roo.each(box, function(b,kk){
34854                 
34855                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34856                 
34857                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34858                 
34859             }, this);
34860             
34861         }, this);
34862         
34863     },
34864     
34865     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34866     {
34867         Roo.each(eItems, function(b,k){
34868             
34869             b.size = (k == 0) ? 'sm' : 'xs';
34870             b.x = (k == 0) ? 2 : 1;
34871             b.y = (k == 0) ? 2 : 1;
34872             
34873             b.el.position('absolute');
34874             
34875             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34876                 
34877             b.el.setWidth(width);
34878             
34879             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34880             
34881             b.el.setHeight(height);
34882             
34883         }, this);
34884
34885         var positions = [];
34886         
34887         positions.push({
34888             x : maxX - this.unitWidth * 2 - this.gutter,
34889             y : minY
34890         });
34891         
34892         positions.push({
34893             x : maxX - this.unitWidth,
34894             y : minY + (this.unitWidth + this.gutter) * 2
34895         });
34896         
34897         positions.push({
34898             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34899             y : minY
34900         });
34901         
34902         Roo.each(eItems, function(b,k){
34903             
34904             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34905
34906         }, this);
34907         
34908     },
34909     
34910     getVerticalOneBoxColPositions : function(x, y, box)
34911     {
34912         var pos = [];
34913         
34914         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34915         
34916         if(box[0].size == 'md-left'){
34917             rand = 0;
34918         }
34919         
34920         if(box[0].size == 'md-right'){
34921             rand = 1;
34922         }
34923         
34924         pos.push({
34925             x : x + (this.unitWidth + this.gutter) * rand,
34926             y : y
34927         });
34928         
34929         return pos;
34930     },
34931     
34932     getVerticalTwoBoxColPositions : function(x, y, box)
34933     {
34934         var pos = [];
34935         
34936         if(box[0].size == 'xs'){
34937             
34938             pos.push({
34939                 x : x,
34940                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34941             });
34942
34943             pos.push({
34944                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34945                 y : y
34946             });
34947             
34948             return pos;
34949             
34950         }
34951         
34952         pos.push({
34953             x : x,
34954             y : y
34955         });
34956
34957         pos.push({
34958             x : x + (this.unitWidth + this.gutter) * 2,
34959             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34960         });
34961         
34962         return pos;
34963         
34964     },
34965     
34966     getVerticalThreeBoxColPositions : function(x, y, box)
34967     {
34968         var pos = [];
34969         
34970         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34971             
34972             pos.push({
34973                 x : x,
34974                 y : y
34975             });
34976
34977             pos.push({
34978                 x : x + (this.unitWidth + this.gutter) * 1,
34979                 y : y
34980             });
34981             
34982             pos.push({
34983                 x : x + (this.unitWidth + this.gutter) * 2,
34984                 y : y
34985             });
34986             
34987             return pos;
34988             
34989         }
34990         
34991         if(box[0].size == 'xs' && box[1].size == 'xs'){
34992             
34993             pos.push({
34994                 x : x,
34995                 y : y
34996             });
34997
34998             pos.push({
34999                 x : x,
35000                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35001             });
35002             
35003             pos.push({
35004                 x : x + (this.unitWidth + this.gutter) * 1,
35005                 y : y
35006             });
35007             
35008             return pos;
35009             
35010         }
35011         
35012         pos.push({
35013             x : x,
35014             y : y
35015         });
35016
35017         pos.push({
35018             x : x + (this.unitWidth + this.gutter) * 2,
35019             y : y
35020         });
35021
35022         pos.push({
35023             x : x + (this.unitWidth + this.gutter) * 2,
35024             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35025         });
35026             
35027         return pos;
35028         
35029     },
35030     
35031     getVerticalFourBoxColPositions : function(x, y, box)
35032     {
35033         var pos = [];
35034         
35035         if(box[0].size == 'xs'){
35036             
35037             pos.push({
35038                 x : x,
35039                 y : y
35040             });
35041
35042             pos.push({
35043                 x : x,
35044                 y : y + (this.unitHeight + this.gutter) * 1
35045             });
35046             
35047             pos.push({
35048                 x : x,
35049                 y : y + (this.unitHeight + this.gutter) * 2
35050             });
35051             
35052             pos.push({
35053                 x : x + (this.unitWidth + this.gutter) * 1,
35054                 y : y
35055             });
35056             
35057             return pos;
35058             
35059         }
35060         
35061         pos.push({
35062             x : x,
35063             y : y
35064         });
35065
35066         pos.push({
35067             x : x + (this.unitWidth + this.gutter) * 2,
35068             y : y
35069         });
35070
35071         pos.push({
35072             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35073             y : y + (this.unitHeight + this.gutter) * 1
35074         });
35075
35076         pos.push({
35077             x : x + (this.unitWidth + this.gutter) * 2,
35078             y : y + (this.unitWidth + this.gutter) * 2
35079         });
35080
35081         return pos;
35082         
35083     },
35084     
35085     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35086     {
35087         var pos = [];
35088         
35089         if(box[0].size == 'md-left'){
35090             pos.push({
35091                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35092                 y : minY
35093             });
35094             
35095             return pos;
35096         }
35097         
35098         if(box[0].size == 'md-right'){
35099             pos.push({
35100                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35101                 y : minY + (this.unitWidth + this.gutter) * 1
35102             });
35103             
35104             return pos;
35105         }
35106         
35107         var rand = Math.floor(Math.random() * (4 - box[0].y));
35108         
35109         pos.push({
35110             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35111             y : minY + (this.unitWidth + this.gutter) * rand
35112         });
35113         
35114         return pos;
35115         
35116     },
35117     
35118     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35119     {
35120         var pos = [];
35121         
35122         if(box[0].size == 'xs'){
35123             
35124             pos.push({
35125                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35126                 y : minY
35127             });
35128
35129             pos.push({
35130                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35131                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35132             });
35133             
35134             return pos;
35135             
35136         }
35137         
35138         pos.push({
35139             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35140             y : minY
35141         });
35142
35143         pos.push({
35144             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35145             y : minY + (this.unitWidth + this.gutter) * 2
35146         });
35147         
35148         return pos;
35149         
35150     },
35151     
35152     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35153     {
35154         var pos = [];
35155         
35156         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35157             
35158             pos.push({
35159                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35160                 y : minY
35161             });
35162
35163             pos.push({
35164                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35165                 y : minY + (this.unitWidth + this.gutter) * 1
35166             });
35167             
35168             pos.push({
35169                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35170                 y : minY + (this.unitWidth + this.gutter) * 2
35171             });
35172             
35173             return pos;
35174             
35175         }
35176         
35177         if(box[0].size == 'xs' && box[1].size == 'xs'){
35178             
35179             pos.push({
35180                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35181                 y : minY
35182             });
35183
35184             pos.push({
35185                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35186                 y : minY
35187             });
35188             
35189             pos.push({
35190                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35191                 y : minY + (this.unitWidth + this.gutter) * 1
35192             });
35193             
35194             return pos;
35195             
35196         }
35197         
35198         pos.push({
35199             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35200             y : minY
35201         });
35202
35203         pos.push({
35204             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35205             y : minY + (this.unitWidth + this.gutter) * 2
35206         });
35207
35208         pos.push({
35209             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35210             y : minY + (this.unitWidth + this.gutter) * 2
35211         });
35212             
35213         return pos;
35214         
35215     },
35216     
35217     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35218     {
35219         var pos = [];
35220         
35221         if(box[0].size == 'xs'){
35222             
35223             pos.push({
35224                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35225                 y : minY
35226             });
35227
35228             pos.push({
35229                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35230                 y : minY
35231             });
35232             
35233             pos.push({
35234                 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),
35235                 y : minY
35236             });
35237             
35238             pos.push({
35239                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35240                 y : minY + (this.unitWidth + this.gutter) * 1
35241             });
35242             
35243             return pos;
35244             
35245         }
35246         
35247         pos.push({
35248             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35249             y : minY
35250         });
35251         
35252         pos.push({
35253             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35254             y : minY + (this.unitWidth + this.gutter) * 2
35255         });
35256         
35257         pos.push({
35258             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35259             y : minY + (this.unitWidth + this.gutter) * 2
35260         });
35261         
35262         pos.push({
35263             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),
35264             y : minY + (this.unitWidth + this.gutter) * 2
35265         });
35266
35267         return pos;
35268         
35269     },
35270     
35271     /**
35272     * remove a Masonry Brick
35273     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35274     */
35275     removeBrick : function(brick_id)
35276     {
35277         if (!brick_id) {
35278             return;
35279         }
35280         
35281         for (var i = 0; i<this.bricks.length; i++) {
35282             if (this.bricks[i].id == brick_id) {
35283                 this.bricks.splice(i,1);
35284                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35285                 this.initial();
35286             }
35287         }
35288     },
35289     
35290     /**
35291     * adds a Masonry Brick
35292     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35293     */
35294     addBrick : function(cfg)
35295     {
35296         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35297         //this.register(cn);
35298         cn.parentId = this.id;
35299         cn.render(this.el);
35300         return cn;
35301     },
35302     
35303     /**
35304     * register a Masonry Brick
35305     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35306     */
35307     
35308     register : function(brick)
35309     {
35310         this.bricks.push(brick);
35311         brick.masonryId = this.id;
35312     },
35313     
35314     /**
35315     * clear all the Masonry Brick
35316     */
35317     clearAll : function()
35318     {
35319         this.bricks = [];
35320         //this.getChildContainer().dom.innerHTML = "";
35321         this.el.dom.innerHTML = '';
35322     },
35323     
35324     getSelected : function()
35325     {
35326         if (!this.selectedBrick) {
35327             return false;
35328         }
35329         
35330         return this.selectedBrick;
35331     }
35332 });
35333
35334 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35335     
35336     groups: {},
35337      /**
35338     * register a Masonry Layout
35339     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35340     */
35341     
35342     register : function(layout)
35343     {
35344         this.groups[layout.id] = layout;
35345     },
35346     /**
35347     * fetch a  Masonry Layout based on the masonry layout ID
35348     * @param {string} the masonry layout to add
35349     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35350     */
35351     
35352     get: function(layout_id) {
35353         if (typeof(this.groups[layout_id]) == 'undefined') {
35354             return false;
35355         }
35356         return this.groups[layout_id] ;
35357     }
35358     
35359     
35360     
35361 });
35362
35363  
35364
35365  /**
35366  *
35367  * This is based on 
35368  * http://masonry.desandro.com
35369  *
35370  * The idea is to render all the bricks based on vertical width...
35371  *
35372  * The original code extends 'outlayer' - we might need to use that....
35373  * 
35374  */
35375
35376
35377 /**
35378  * @class Roo.bootstrap.LayoutMasonryAuto
35379  * @extends Roo.bootstrap.Component
35380  * Bootstrap Layout Masonry class
35381  * 
35382  * @constructor
35383  * Create a new Element
35384  * @param {Object} config The config object
35385  */
35386
35387 Roo.bootstrap.LayoutMasonryAuto = function(config){
35388     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35389 };
35390
35391 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35392     
35393       /**
35394      * @cfg {Boolean} isFitWidth  - resize the width..
35395      */   
35396     isFitWidth : false,  // options..
35397     /**
35398      * @cfg {Boolean} isOriginLeft = left align?
35399      */   
35400     isOriginLeft : true,
35401     /**
35402      * @cfg {Boolean} isOriginTop = top align?
35403      */   
35404     isOriginTop : false,
35405     /**
35406      * @cfg {Boolean} isLayoutInstant = no animation?
35407      */   
35408     isLayoutInstant : false, // needed?
35409     /**
35410      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35411      */   
35412     isResizingContainer : true,
35413     /**
35414      * @cfg {Number} columnWidth  width of the columns 
35415      */   
35416     
35417     columnWidth : 0,
35418     
35419     /**
35420      * @cfg {Number} maxCols maximum number of columns
35421      */   
35422     
35423     maxCols: 0,
35424     /**
35425      * @cfg {Number} padHeight padding below box..
35426      */   
35427     
35428     padHeight : 10, 
35429     
35430     /**
35431      * @cfg {Boolean} isAutoInitial defalut true
35432      */   
35433     
35434     isAutoInitial : true, 
35435     
35436     // private?
35437     gutter : 0,
35438     
35439     containerWidth: 0,
35440     initialColumnWidth : 0,
35441     currentSize : null,
35442     
35443     colYs : null, // array.
35444     maxY : 0,
35445     padWidth: 10,
35446     
35447     
35448     tag: 'div',
35449     cls: '',
35450     bricks: null, //CompositeElement
35451     cols : 0, // array?
35452     // element : null, // wrapped now this.el
35453     _isLayoutInited : null, 
35454     
35455     
35456     getAutoCreate : function(){
35457         
35458         var cfg = {
35459             tag: this.tag,
35460             cls: 'blog-masonary-wrapper ' + this.cls,
35461             cn : {
35462                 cls : 'mas-boxes masonary'
35463             }
35464         };
35465         
35466         return cfg;
35467     },
35468     
35469     getChildContainer: function( )
35470     {
35471         if (this.boxesEl) {
35472             return this.boxesEl;
35473         }
35474         
35475         this.boxesEl = this.el.select('.mas-boxes').first();
35476         
35477         return this.boxesEl;
35478     },
35479     
35480     
35481     initEvents : function()
35482     {
35483         var _this = this;
35484         
35485         if(this.isAutoInitial){
35486             Roo.log('hook children rendered');
35487             this.on('childrenrendered', function() {
35488                 Roo.log('children rendered');
35489                 _this.initial();
35490             } ,this);
35491         }
35492         
35493     },
35494     
35495     initial : function()
35496     {
35497         this.reloadItems();
35498
35499         this.currentSize = this.el.getBox(true);
35500
35501         /// was window resize... - let's see if this works..
35502         Roo.EventManager.onWindowResize(this.resize, this); 
35503
35504         if(!this.isAutoInitial){
35505             this.layout();
35506             return;
35507         }
35508         
35509         this.layout.defer(500,this);
35510     },
35511     
35512     reloadItems: function()
35513     {
35514         this.bricks = this.el.select('.masonry-brick', true);
35515         
35516         this.bricks.each(function(b) {
35517             //Roo.log(b.getSize());
35518             if (!b.attr('originalwidth')) {
35519                 b.attr('originalwidth',  b.getSize().width);
35520             }
35521             
35522         });
35523         
35524         Roo.log(this.bricks.elements.length);
35525     },
35526     
35527     resize : function()
35528     {
35529         Roo.log('resize');
35530         var cs = this.el.getBox(true);
35531         
35532         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35533             Roo.log("no change in with or X");
35534             return;
35535         }
35536         this.currentSize = cs;
35537         this.layout();
35538     },
35539     
35540     layout : function()
35541     {
35542          Roo.log('layout');
35543         this._resetLayout();
35544         //this._manageStamps();
35545       
35546         // don't animate first layout
35547         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35548         this.layoutItems( isInstant );
35549       
35550         // flag for initalized
35551         this._isLayoutInited = true;
35552     },
35553     
35554     layoutItems : function( isInstant )
35555     {
35556         //var items = this._getItemsForLayout( this.items );
35557         // original code supports filtering layout items.. we just ignore it..
35558         
35559         this._layoutItems( this.bricks , isInstant );
35560       
35561         this._postLayout();
35562     },
35563     _layoutItems : function ( items , isInstant)
35564     {
35565        //this.fireEvent( 'layout', this, items );
35566     
35567
35568         if ( !items || !items.elements.length ) {
35569           // no items, emit event with empty array
35570             return;
35571         }
35572
35573         var queue = [];
35574         items.each(function(item) {
35575             Roo.log("layout item");
35576             Roo.log(item);
35577             // get x/y object from method
35578             var position = this._getItemLayoutPosition( item );
35579             // enqueue
35580             position.item = item;
35581             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35582             queue.push( position );
35583         }, this);
35584       
35585         this._processLayoutQueue( queue );
35586     },
35587     /** Sets position of item in DOM
35588     * @param {Element} item
35589     * @param {Number} x - horizontal position
35590     * @param {Number} y - vertical position
35591     * @param {Boolean} isInstant - disables transitions
35592     */
35593     _processLayoutQueue : function( queue )
35594     {
35595         for ( var i=0, len = queue.length; i < len; i++ ) {
35596             var obj = queue[i];
35597             obj.item.position('absolute');
35598             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35599         }
35600     },
35601       
35602     
35603     /**
35604     * Any logic you want to do after each layout,
35605     * i.e. size the container
35606     */
35607     _postLayout : function()
35608     {
35609         this.resizeContainer();
35610     },
35611     
35612     resizeContainer : function()
35613     {
35614         if ( !this.isResizingContainer ) {
35615             return;
35616         }
35617         var size = this._getContainerSize();
35618         if ( size ) {
35619             this.el.setSize(size.width,size.height);
35620             this.boxesEl.setSize(size.width,size.height);
35621         }
35622     },
35623     
35624     
35625     
35626     _resetLayout : function()
35627     {
35628         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35629         this.colWidth = this.el.getWidth();
35630         //this.gutter = this.el.getWidth(); 
35631         
35632         this.measureColumns();
35633
35634         // reset column Y
35635         var i = this.cols;
35636         this.colYs = [];
35637         while (i--) {
35638             this.colYs.push( 0 );
35639         }
35640     
35641         this.maxY = 0;
35642     },
35643
35644     measureColumns : function()
35645     {
35646         this.getContainerWidth();
35647       // if columnWidth is 0, default to outerWidth of first item
35648         if ( !this.columnWidth ) {
35649             var firstItem = this.bricks.first();
35650             Roo.log(firstItem);
35651             this.columnWidth  = this.containerWidth;
35652             if (firstItem && firstItem.attr('originalwidth') ) {
35653                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35654             }
35655             // columnWidth fall back to item of first element
35656             Roo.log("set column width?");
35657                         this.initialColumnWidth = this.columnWidth  ;
35658
35659             // if first elem has no width, default to size of container
35660             
35661         }
35662         
35663         
35664         if (this.initialColumnWidth) {
35665             this.columnWidth = this.initialColumnWidth;
35666         }
35667         
35668         
35669             
35670         // column width is fixed at the top - however if container width get's smaller we should
35671         // reduce it...
35672         
35673         // this bit calcs how man columns..
35674             
35675         var columnWidth = this.columnWidth += this.gutter;
35676       
35677         // calculate columns
35678         var containerWidth = this.containerWidth + this.gutter;
35679         
35680         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35681         // fix rounding errors, typically with gutters
35682         var excess = columnWidth - containerWidth % columnWidth;
35683         
35684         
35685         // if overshoot is less than a pixel, round up, otherwise floor it
35686         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35687         cols = Math[ mathMethod ]( cols );
35688         this.cols = Math.max( cols, 1 );
35689         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35690         
35691          // padding positioning..
35692         var totalColWidth = this.cols * this.columnWidth;
35693         var padavail = this.containerWidth - totalColWidth;
35694         // so for 2 columns - we need 3 'pads'
35695         
35696         var padNeeded = (1+this.cols) * this.padWidth;
35697         
35698         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35699         
35700         this.columnWidth += padExtra
35701         //this.padWidth = Math.floor(padavail /  ( this.cols));
35702         
35703         // adjust colum width so that padding is fixed??
35704         
35705         // we have 3 columns ... total = width * 3
35706         // we have X left over... that should be used by 
35707         
35708         //if (this.expandC) {
35709             
35710         //}
35711         
35712         
35713         
35714     },
35715     
35716     getContainerWidth : function()
35717     {
35718        /* // container is parent if fit width
35719         var container = this.isFitWidth ? this.element.parentNode : this.element;
35720         // check that this.size and size are there
35721         // IE8 triggers resize on body size change, so they might not be
35722         
35723         var size = getSize( container );  //FIXME
35724         this.containerWidth = size && size.innerWidth; //FIXME
35725         */
35726          
35727         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35728         
35729     },
35730     
35731     _getItemLayoutPosition : function( item )  // what is item?
35732     {
35733         // we resize the item to our columnWidth..
35734       
35735         item.setWidth(this.columnWidth);
35736         item.autoBoxAdjust  = false;
35737         
35738         var sz = item.getSize();
35739  
35740         // how many columns does this brick span
35741         var remainder = this.containerWidth % this.columnWidth;
35742         
35743         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35744         // round if off by 1 pixel, otherwise use ceil
35745         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35746         colSpan = Math.min( colSpan, this.cols );
35747         
35748         // normally this should be '1' as we dont' currently allow multi width columns..
35749         
35750         var colGroup = this._getColGroup( colSpan );
35751         // get the minimum Y value from the columns
35752         var minimumY = Math.min.apply( Math, colGroup );
35753         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35754         
35755         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35756          
35757         // position the brick
35758         var position = {
35759             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35760             y: this.currentSize.y + minimumY + this.padHeight
35761         };
35762         
35763         Roo.log(position);
35764         // apply setHeight to necessary columns
35765         var setHeight = minimumY + sz.height + this.padHeight;
35766         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35767         
35768         var setSpan = this.cols + 1 - colGroup.length;
35769         for ( var i = 0; i < setSpan; i++ ) {
35770           this.colYs[ shortColIndex + i ] = setHeight ;
35771         }
35772       
35773         return position;
35774     },
35775     
35776     /**
35777      * @param {Number} colSpan - number of columns the element spans
35778      * @returns {Array} colGroup
35779      */
35780     _getColGroup : function( colSpan )
35781     {
35782         if ( colSpan < 2 ) {
35783           // if brick spans only one column, use all the column Ys
35784           return this.colYs;
35785         }
35786       
35787         var colGroup = [];
35788         // how many different places could this brick fit horizontally
35789         var groupCount = this.cols + 1 - colSpan;
35790         // for each group potential horizontal position
35791         for ( var i = 0; i < groupCount; i++ ) {
35792           // make an array of colY values for that one group
35793           var groupColYs = this.colYs.slice( i, i + colSpan );
35794           // and get the max value of the array
35795           colGroup[i] = Math.max.apply( Math, groupColYs );
35796         }
35797         return colGroup;
35798     },
35799     /*
35800     _manageStamp : function( stamp )
35801     {
35802         var stampSize =  stamp.getSize();
35803         var offset = stamp.getBox();
35804         // get the columns that this stamp affects
35805         var firstX = this.isOriginLeft ? offset.x : offset.right;
35806         var lastX = firstX + stampSize.width;
35807         var firstCol = Math.floor( firstX / this.columnWidth );
35808         firstCol = Math.max( 0, firstCol );
35809         
35810         var lastCol = Math.floor( lastX / this.columnWidth );
35811         // lastCol should not go over if multiple of columnWidth #425
35812         lastCol -= lastX % this.columnWidth ? 0 : 1;
35813         lastCol = Math.min( this.cols - 1, lastCol );
35814         
35815         // set colYs to bottom of the stamp
35816         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35817             stampSize.height;
35818             
35819         for ( var i = firstCol; i <= lastCol; i++ ) {
35820           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35821         }
35822     },
35823     */
35824     
35825     _getContainerSize : function()
35826     {
35827         this.maxY = Math.max.apply( Math, this.colYs );
35828         var size = {
35829             height: this.maxY
35830         };
35831       
35832         if ( this.isFitWidth ) {
35833             size.width = this._getContainerFitWidth();
35834         }
35835       
35836         return size;
35837     },
35838     
35839     _getContainerFitWidth : function()
35840     {
35841         var unusedCols = 0;
35842         // count unused columns
35843         var i = this.cols;
35844         while ( --i ) {
35845           if ( this.colYs[i] !== 0 ) {
35846             break;
35847           }
35848           unusedCols++;
35849         }
35850         // fit container to columns that have been used
35851         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35852     },
35853     
35854     needsResizeLayout : function()
35855     {
35856         var previousWidth = this.containerWidth;
35857         this.getContainerWidth();
35858         return previousWidth !== this.containerWidth;
35859     }
35860  
35861 });
35862
35863  
35864
35865  /*
35866  * - LGPL
35867  *
35868  * element
35869  * 
35870  */
35871
35872 /**
35873  * @class Roo.bootstrap.MasonryBrick
35874  * @extends Roo.bootstrap.Component
35875  * Bootstrap MasonryBrick class
35876  * 
35877  * @constructor
35878  * Create a new MasonryBrick
35879  * @param {Object} config The config object
35880  */
35881
35882 Roo.bootstrap.MasonryBrick = function(config){
35883     
35884     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35885     
35886     Roo.bootstrap.MasonryBrick.register(this);
35887     
35888     this.addEvents({
35889         // raw events
35890         /**
35891          * @event click
35892          * When a MasonryBrick is clcik
35893          * @param {Roo.bootstrap.MasonryBrick} this
35894          * @param {Roo.EventObject} e
35895          */
35896         "click" : true
35897     });
35898 };
35899
35900 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35901     
35902     /**
35903      * @cfg {String} title
35904      */   
35905     title : '',
35906     /**
35907      * @cfg {String} html
35908      */   
35909     html : '',
35910     /**
35911      * @cfg {String} bgimage
35912      */   
35913     bgimage : '',
35914     /**
35915      * @cfg {String} videourl
35916      */   
35917     videourl : '',
35918     /**
35919      * @cfg {String} cls
35920      */   
35921     cls : '',
35922     /**
35923      * @cfg {String} href
35924      */   
35925     href : '',
35926     /**
35927      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35928      */   
35929     size : 'xs',
35930     
35931     /**
35932      * @cfg {String} placetitle (center|bottom)
35933      */   
35934     placetitle : '',
35935     
35936     /**
35937      * @cfg {Boolean} isFitContainer defalut true
35938      */   
35939     isFitContainer : true, 
35940     
35941     /**
35942      * @cfg {Boolean} preventDefault defalut false
35943      */   
35944     preventDefault : false, 
35945     
35946     /**
35947      * @cfg {Boolean} inverse defalut false
35948      */   
35949     maskInverse : false, 
35950     
35951     getAutoCreate : function()
35952     {
35953         if(!this.isFitContainer){
35954             return this.getSplitAutoCreate();
35955         }
35956         
35957         var cls = 'masonry-brick masonry-brick-full';
35958         
35959         if(this.href.length){
35960             cls += ' masonry-brick-link';
35961         }
35962         
35963         if(this.bgimage.length){
35964             cls += ' masonry-brick-image';
35965         }
35966         
35967         if(this.maskInverse){
35968             cls += ' mask-inverse';
35969         }
35970         
35971         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35972             cls += ' enable-mask';
35973         }
35974         
35975         if(this.size){
35976             cls += ' masonry-' + this.size + '-brick';
35977         }
35978         
35979         if(this.placetitle.length){
35980             
35981             switch (this.placetitle) {
35982                 case 'center' :
35983                     cls += ' masonry-center-title';
35984                     break;
35985                 case 'bottom' :
35986                     cls += ' masonry-bottom-title';
35987                     break;
35988                 default:
35989                     break;
35990             }
35991             
35992         } else {
35993             if(!this.html.length && !this.bgimage.length){
35994                 cls += ' masonry-center-title';
35995             }
35996
35997             if(!this.html.length && this.bgimage.length){
35998                 cls += ' masonry-bottom-title';
35999             }
36000         }
36001         
36002         if(this.cls){
36003             cls += ' ' + this.cls;
36004         }
36005         
36006         var cfg = {
36007             tag: (this.href.length) ? 'a' : 'div',
36008             cls: cls,
36009             cn: [
36010                 {
36011                     tag: 'div',
36012                     cls: 'masonry-brick-mask'
36013                 },
36014                 {
36015                     tag: 'div',
36016                     cls: 'masonry-brick-paragraph',
36017                     cn: []
36018                 }
36019             ]
36020         };
36021         
36022         if(this.href.length){
36023             cfg.href = this.href;
36024         }
36025         
36026         var cn = cfg.cn[1].cn;
36027         
36028         if(this.title.length){
36029             cn.push({
36030                 tag: 'h4',
36031                 cls: 'masonry-brick-title',
36032                 html: this.title
36033             });
36034         }
36035         
36036         if(this.html.length){
36037             cn.push({
36038                 tag: 'p',
36039                 cls: 'masonry-brick-text',
36040                 html: this.html
36041             });
36042         }
36043         
36044         if (!this.title.length && !this.html.length) {
36045             cfg.cn[1].cls += ' hide';
36046         }
36047         
36048         if(this.bgimage.length){
36049             cfg.cn.push({
36050                 tag: 'img',
36051                 cls: 'masonry-brick-image-view',
36052                 src: this.bgimage
36053             });
36054         }
36055         
36056         if(this.videourl.length){
36057             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36058             // youtube support only?
36059             cfg.cn.push({
36060                 tag: 'iframe',
36061                 cls: 'masonry-brick-image-view',
36062                 src: vurl,
36063                 frameborder : 0,
36064                 allowfullscreen : true
36065             });
36066         }
36067         
36068         return cfg;
36069         
36070     },
36071     
36072     getSplitAutoCreate : function()
36073     {
36074         var cls = 'masonry-brick masonry-brick-split';
36075         
36076         if(this.href.length){
36077             cls += ' masonry-brick-link';
36078         }
36079         
36080         if(this.bgimage.length){
36081             cls += ' masonry-brick-image';
36082         }
36083         
36084         if(this.size){
36085             cls += ' masonry-' + this.size + '-brick';
36086         }
36087         
36088         switch (this.placetitle) {
36089             case 'center' :
36090                 cls += ' masonry-center-title';
36091                 break;
36092             case 'bottom' :
36093                 cls += ' masonry-bottom-title';
36094                 break;
36095             default:
36096                 if(!this.bgimage.length){
36097                     cls += ' masonry-center-title';
36098                 }
36099
36100                 if(this.bgimage.length){
36101                     cls += ' masonry-bottom-title';
36102                 }
36103                 break;
36104         }
36105         
36106         if(this.cls){
36107             cls += ' ' + this.cls;
36108         }
36109         
36110         var cfg = {
36111             tag: (this.href.length) ? 'a' : 'div',
36112             cls: cls,
36113             cn: [
36114                 {
36115                     tag: 'div',
36116                     cls: 'masonry-brick-split-head',
36117                     cn: [
36118                         {
36119                             tag: 'div',
36120                             cls: 'masonry-brick-paragraph',
36121                             cn: []
36122                         }
36123                     ]
36124                 },
36125                 {
36126                     tag: 'div',
36127                     cls: 'masonry-brick-split-body',
36128                     cn: []
36129                 }
36130             ]
36131         };
36132         
36133         if(this.href.length){
36134             cfg.href = this.href;
36135         }
36136         
36137         if(this.title.length){
36138             cfg.cn[0].cn[0].cn.push({
36139                 tag: 'h4',
36140                 cls: 'masonry-brick-title',
36141                 html: this.title
36142             });
36143         }
36144         
36145         if(this.html.length){
36146             cfg.cn[1].cn.push({
36147                 tag: 'p',
36148                 cls: 'masonry-brick-text',
36149                 html: this.html
36150             });
36151         }
36152
36153         if(this.bgimage.length){
36154             cfg.cn[0].cn.push({
36155                 tag: 'img',
36156                 cls: 'masonry-brick-image-view',
36157                 src: this.bgimage
36158             });
36159         }
36160         
36161         if(this.videourl.length){
36162             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36163             // youtube support only?
36164             cfg.cn[0].cn.cn.push({
36165                 tag: 'iframe',
36166                 cls: 'masonry-brick-image-view',
36167                 src: vurl,
36168                 frameborder : 0,
36169                 allowfullscreen : true
36170             });
36171         }
36172         
36173         return cfg;
36174     },
36175     
36176     initEvents: function() 
36177     {
36178         switch (this.size) {
36179             case 'xs' :
36180                 this.x = 1;
36181                 this.y = 1;
36182                 break;
36183             case 'sm' :
36184                 this.x = 2;
36185                 this.y = 2;
36186                 break;
36187             case 'md' :
36188             case 'md-left' :
36189             case 'md-right' :
36190                 this.x = 3;
36191                 this.y = 3;
36192                 break;
36193             case 'tall' :
36194                 this.x = 2;
36195                 this.y = 3;
36196                 break;
36197             case 'wide' :
36198                 this.x = 3;
36199                 this.y = 2;
36200                 break;
36201             case 'wide-thin' :
36202                 this.x = 3;
36203                 this.y = 1;
36204                 break;
36205                         
36206             default :
36207                 break;
36208         }
36209         
36210         if(Roo.isTouch){
36211             this.el.on('touchstart', this.onTouchStart, this);
36212             this.el.on('touchmove', this.onTouchMove, this);
36213             this.el.on('touchend', this.onTouchEnd, this);
36214             this.el.on('contextmenu', this.onContextMenu, this);
36215         } else {
36216             this.el.on('mouseenter'  ,this.enter, this);
36217             this.el.on('mouseleave', this.leave, this);
36218             this.el.on('click', this.onClick, this);
36219         }
36220         
36221         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36222             this.parent().bricks.push(this);   
36223         }
36224         
36225     },
36226     
36227     onClick: function(e, el)
36228     {
36229         var time = this.endTimer - this.startTimer;
36230         // Roo.log(e.preventDefault());
36231         if(Roo.isTouch){
36232             if(time > 1000){
36233                 e.preventDefault();
36234                 return;
36235             }
36236         }
36237         
36238         if(!this.preventDefault){
36239             return;
36240         }
36241         
36242         e.preventDefault();
36243         
36244         if (this.activeClass != '') {
36245             this.selectBrick();
36246         }
36247         
36248         this.fireEvent('click', this, e);
36249     },
36250     
36251     enter: function(e, el)
36252     {
36253         e.preventDefault();
36254         
36255         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36256             return;
36257         }
36258         
36259         if(this.bgimage.length && this.html.length){
36260             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36261         }
36262     },
36263     
36264     leave: function(e, el)
36265     {
36266         e.preventDefault();
36267         
36268         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36269             return;
36270         }
36271         
36272         if(this.bgimage.length && this.html.length){
36273             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36274         }
36275     },
36276     
36277     onTouchStart: function(e, el)
36278     {
36279 //        e.preventDefault();
36280         
36281         this.touchmoved = false;
36282         
36283         if(!this.isFitContainer){
36284             return;
36285         }
36286         
36287         if(!this.bgimage.length || !this.html.length){
36288             return;
36289         }
36290         
36291         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36292         
36293         this.timer = new Date().getTime();
36294         
36295     },
36296     
36297     onTouchMove: function(e, el)
36298     {
36299         this.touchmoved = true;
36300     },
36301     
36302     onContextMenu : function(e,el)
36303     {
36304         e.preventDefault();
36305         e.stopPropagation();
36306         return false;
36307     },
36308     
36309     onTouchEnd: function(e, el)
36310     {
36311 //        e.preventDefault();
36312         
36313         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36314         
36315             this.leave(e,el);
36316             
36317             return;
36318         }
36319         
36320         if(!this.bgimage.length || !this.html.length){
36321             
36322             if(this.href.length){
36323                 window.location.href = this.href;
36324             }
36325             
36326             return;
36327         }
36328         
36329         if(!this.isFitContainer){
36330             return;
36331         }
36332         
36333         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36334         
36335         window.location.href = this.href;
36336     },
36337     
36338     //selection on single brick only
36339     selectBrick : function() {
36340         
36341         if (!this.parentId) {
36342             return;
36343         }
36344         
36345         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36346         var index = m.selectedBrick.indexOf(this.id);
36347         
36348         if ( index > -1) {
36349             m.selectedBrick.splice(index,1);
36350             this.el.removeClass(this.activeClass);
36351             return;
36352         }
36353         
36354         for(var i = 0; i < m.selectedBrick.length; i++) {
36355             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36356             b.el.removeClass(b.activeClass);
36357         }
36358         
36359         m.selectedBrick = [];
36360         
36361         m.selectedBrick.push(this.id);
36362         this.el.addClass(this.activeClass);
36363         return;
36364     },
36365     
36366     isSelected : function(){
36367         return this.el.hasClass(this.activeClass);
36368         
36369     }
36370 });
36371
36372 Roo.apply(Roo.bootstrap.MasonryBrick, {
36373     
36374     //groups: {},
36375     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36376      /**
36377     * register a Masonry Brick
36378     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36379     */
36380     
36381     register : function(brick)
36382     {
36383         //this.groups[brick.id] = brick;
36384         this.groups.add(brick.id, brick);
36385     },
36386     /**
36387     * fetch a  masonry brick based on the masonry brick ID
36388     * @param {string} the masonry brick to add
36389     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36390     */
36391     
36392     get: function(brick_id) 
36393     {
36394         // if (typeof(this.groups[brick_id]) == 'undefined') {
36395         //     return false;
36396         // }
36397         // return this.groups[brick_id] ;
36398         
36399         if(this.groups.key(brick_id)) {
36400             return this.groups.key(brick_id);
36401         }
36402         
36403         return false;
36404     }
36405     
36406     
36407     
36408 });
36409
36410  /*
36411  * - LGPL
36412  *
36413  * element
36414  * 
36415  */
36416
36417 /**
36418  * @class Roo.bootstrap.Brick
36419  * @extends Roo.bootstrap.Component
36420  * Bootstrap Brick class
36421  * 
36422  * @constructor
36423  * Create a new Brick
36424  * @param {Object} config The config object
36425  */
36426
36427 Roo.bootstrap.Brick = function(config){
36428     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36429     
36430     this.addEvents({
36431         // raw events
36432         /**
36433          * @event click
36434          * When a Brick is click
36435          * @param {Roo.bootstrap.Brick} this
36436          * @param {Roo.EventObject} e
36437          */
36438         "click" : true
36439     });
36440 };
36441
36442 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36443     
36444     /**
36445      * @cfg {String} title
36446      */   
36447     title : '',
36448     /**
36449      * @cfg {String} html
36450      */   
36451     html : '',
36452     /**
36453      * @cfg {String} bgimage
36454      */   
36455     bgimage : '',
36456     /**
36457      * @cfg {String} cls
36458      */   
36459     cls : '',
36460     /**
36461      * @cfg {String} href
36462      */   
36463     href : '',
36464     /**
36465      * @cfg {String} video
36466      */   
36467     video : '',
36468     /**
36469      * @cfg {Boolean} square
36470      */   
36471     square : true,
36472     
36473     getAutoCreate : function()
36474     {
36475         var cls = 'roo-brick';
36476         
36477         if(this.href.length){
36478             cls += ' roo-brick-link';
36479         }
36480         
36481         if(this.bgimage.length){
36482             cls += ' roo-brick-image';
36483         }
36484         
36485         if(!this.html.length && !this.bgimage.length){
36486             cls += ' roo-brick-center-title';
36487         }
36488         
36489         if(!this.html.length && this.bgimage.length){
36490             cls += ' roo-brick-bottom-title';
36491         }
36492         
36493         if(this.cls){
36494             cls += ' ' + this.cls;
36495         }
36496         
36497         var cfg = {
36498             tag: (this.href.length) ? 'a' : 'div',
36499             cls: cls,
36500             cn: [
36501                 {
36502                     tag: 'div',
36503                     cls: 'roo-brick-paragraph',
36504                     cn: []
36505                 }
36506             ]
36507         };
36508         
36509         if(this.href.length){
36510             cfg.href = this.href;
36511         }
36512         
36513         var cn = cfg.cn[0].cn;
36514         
36515         if(this.title.length){
36516             cn.push({
36517                 tag: 'h4',
36518                 cls: 'roo-brick-title',
36519                 html: this.title
36520             });
36521         }
36522         
36523         if(this.html.length){
36524             cn.push({
36525                 tag: 'p',
36526                 cls: 'roo-brick-text',
36527                 html: this.html
36528             });
36529         } else {
36530             cn.cls += ' hide';
36531         }
36532         
36533         if(this.bgimage.length){
36534             cfg.cn.push({
36535                 tag: 'img',
36536                 cls: 'roo-brick-image-view',
36537                 src: this.bgimage
36538             });
36539         }
36540         
36541         return cfg;
36542     },
36543     
36544     initEvents: function() 
36545     {
36546         if(this.title.length || this.html.length){
36547             this.el.on('mouseenter'  ,this.enter, this);
36548             this.el.on('mouseleave', this.leave, this);
36549         }
36550         
36551         Roo.EventManager.onWindowResize(this.resize, this); 
36552         
36553         if(this.bgimage.length){
36554             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36555             this.imageEl.on('load', this.onImageLoad, this);
36556             return;
36557         }
36558         
36559         this.resize();
36560     },
36561     
36562     onImageLoad : function()
36563     {
36564         this.resize();
36565     },
36566     
36567     resize : function()
36568     {
36569         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36570         
36571         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36572         
36573         if(this.bgimage.length){
36574             var image = this.el.select('.roo-brick-image-view', true).first();
36575             
36576             image.setWidth(paragraph.getWidth());
36577             
36578             if(this.square){
36579                 image.setHeight(paragraph.getWidth());
36580             }
36581             
36582             this.el.setHeight(image.getHeight());
36583             paragraph.setHeight(image.getHeight());
36584             
36585         }
36586         
36587     },
36588     
36589     enter: function(e, el)
36590     {
36591         e.preventDefault();
36592         
36593         if(this.bgimage.length){
36594             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36595             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36596         }
36597     },
36598     
36599     leave: function(e, el)
36600     {
36601         e.preventDefault();
36602         
36603         if(this.bgimage.length){
36604             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36605             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36606         }
36607     }
36608     
36609 });
36610
36611  
36612
36613  /*
36614  * - LGPL
36615  *
36616  * Number field 
36617  */
36618
36619 /**
36620  * @class Roo.bootstrap.form.NumberField
36621  * @extends Roo.bootstrap.form.Input
36622  * Bootstrap NumberField class
36623  * 
36624  * 
36625  * 
36626  * 
36627  * @constructor
36628  * Create a new NumberField
36629  * @param {Object} config The config object
36630  */
36631
36632 Roo.bootstrap.form.NumberField = function(config){
36633     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36634 };
36635
36636 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36637     
36638     /**
36639      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36640      */
36641     allowDecimals : true,
36642     /**
36643      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36644      */
36645     decimalSeparator : ".",
36646     /**
36647      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36648      */
36649     decimalPrecision : 2,
36650     /**
36651      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36652      */
36653     allowNegative : true,
36654     
36655     /**
36656      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36657      */
36658     allowZero: true,
36659     /**
36660      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36661      */
36662     minValue : Number.NEGATIVE_INFINITY,
36663     /**
36664      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36665      */
36666     maxValue : Number.MAX_VALUE,
36667     /**
36668      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36669      */
36670     minText : "The minimum value for this field is {0}",
36671     /**
36672      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36673      */
36674     maxText : "The maximum value for this field is {0}",
36675     /**
36676      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36677      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36678      */
36679     nanText : "{0} is not a valid number",
36680     /**
36681      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36682      */
36683     thousandsDelimiter : false,
36684     /**
36685      * @cfg {String} valueAlign alignment of value
36686      */
36687     valueAlign : "left",
36688
36689     getAutoCreate : function()
36690     {
36691         var hiddenInput = {
36692             tag: 'input',
36693             type: 'hidden',
36694             id: Roo.id(),
36695             cls: 'hidden-number-input'
36696         };
36697         
36698         if (this.name) {
36699             hiddenInput.name = this.name;
36700         }
36701         
36702         this.name = '';
36703         
36704         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36705         
36706         this.name = hiddenInput.name;
36707         
36708         if(cfg.cn.length > 0) {
36709             cfg.cn.push(hiddenInput);
36710         }
36711         
36712         return cfg;
36713     },
36714
36715     // private
36716     initEvents : function()
36717     {   
36718         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36719         
36720         var allowed = "0123456789";
36721         
36722         if(this.allowDecimals){
36723             allowed += this.decimalSeparator;
36724         }
36725         
36726         if(this.allowNegative){
36727             allowed += "-";
36728         }
36729         
36730         if(this.thousandsDelimiter) {
36731             allowed += ",";
36732         }
36733         
36734         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36735         
36736         var keyPress = function(e){
36737             
36738             var k = e.getKey();
36739             
36740             var c = e.getCharCode();
36741             
36742             if(
36743                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36744                     allowed.indexOf(String.fromCharCode(c)) === -1
36745             ){
36746                 e.stopEvent();
36747                 return;
36748             }
36749             
36750             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36751                 return;
36752             }
36753             
36754             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36755                 e.stopEvent();
36756             }
36757         };
36758         
36759         this.el.on("keypress", keyPress, this);
36760     },
36761     
36762     validateValue : function(value)
36763     {
36764         
36765         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36766             return false;
36767         }
36768         
36769         var num = this.parseValue(value);
36770         
36771         if(isNaN(num)){
36772             this.markInvalid(String.format(this.nanText, value));
36773             return false;
36774         }
36775         
36776         if(num < this.minValue){
36777             this.markInvalid(String.format(this.minText, this.minValue));
36778             return false;
36779         }
36780         
36781         if(num > this.maxValue){
36782             this.markInvalid(String.format(this.maxText, this.maxValue));
36783             return false;
36784         }
36785         
36786         return true;
36787     },
36788
36789     getValue : function()
36790     {
36791         var v = this.hiddenEl().getValue();
36792         
36793         return this.fixPrecision(this.parseValue(v));
36794     },
36795
36796     parseValue : function(value)
36797     {
36798         if(this.thousandsDelimiter) {
36799             value += "";
36800             r = new RegExp(",", "g");
36801             value = value.replace(r, "");
36802         }
36803         
36804         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36805         return isNaN(value) ? '' : value;
36806     },
36807
36808     fixPrecision : function(value)
36809     {
36810         if(this.thousandsDelimiter) {
36811             value += "";
36812             r = new RegExp(",", "g");
36813             value = value.replace(r, "");
36814         }
36815         
36816         var nan = isNaN(value);
36817         
36818         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36819             return nan ? '' : value;
36820         }
36821         return parseFloat(value).toFixed(this.decimalPrecision);
36822     },
36823
36824     setValue : function(v)
36825     {
36826         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36827         
36828         this.value = v;
36829         
36830         if(this.rendered){
36831             
36832             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36833             
36834             this.inputEl().dom.value = (v == '') ? '' :
36835                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36836             
36837             if(!this.allowZero && v === '0') {
36838                 this.hiddenEl().dom.value = '';
36839                 this.inputEl().dom.value = '';
36840             }
36841             
36842             this.validate();
36843         }
36844     },
36845
36846     decimalPrecisionFcn : function(v)
36847     {
36848         return Math.floor(v);
36849     },
36850
36851     beforeBlur : function()
36852     {
36853         var v = this.parseValue(this.getRawValue());
36854         
36855         if(v || v === 0 || v === ''){
36856             this.setValue(v);
36857         }
36858     },
36859     
36860     hiddenEl : function()
36861     {
36862         return this.el.select('input.hidden-number-input',true).first();
36863     }
36864     
36865 });
36866
36867  
36868
36869 /*
36870 * Licence: LGPL
36871 */
36872
36873 /**
36874  * @class Roo.bootstrap.DocumentSlider
36875  * @extends Roo.bootstrap.Component
36876  * Bootstrap DocumentSlider class
36877  * 
36878  * @constructor
36879  * Create a new DocumentViewer
36880  * @param {Object} config The config object
36881  */
36882
36883 Roo.bootstrap.DocumentSlider = function(config){
36884     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36885     
36886     this.files = [];
36887     
36888     this.addEvents({
36889         /**
36890          * @event initial
36891          * Fire after initEvent
36892          * @param {Roo.bootstrap.DocumentSlider} this
36893          */
36894         "initial" : true,
36895         /**
36896          * @event update
36897          * Fire after update
36898          * @param {Roo.bootstrap.DocumentSlider} this
36899          */
36900         "update" : true,
36901         /**
36902          * @event click
36903          * Fire after click
36904          * @param {Roo.bootstrap.DocumentSlider} this
36905          */
36906         "click" : true
36907     });
36908 };
36909
36910 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36911     
36912     files : false,
36913     
36914     indicator : 0,
36915     
36916     getAutoCreate : function()
36917     {
36918         var cfg = {
36919             tag : 'div',
36920             cls : 'roo-document-slider',
36921             cn : [
36922                 {
36923                     tag : 'div',
36924                     cls : 'roo-document-slider-header',
36925                     cn : [
36926                         {
36927                             tag : 'div',
36928                             cls : 'roo-document-slider-header-title'
36929                         }
36930                     ]
36931                 },
36932                 {
36933                     tag : 'div',
36934                     cls : 'roo-document-slider-body',
36935                     cn : [
36936                         {
36937                             tag : 'div',
36938                             cls : 'roo-document-slider-prev',
36939                             cn : [
36940                                 {
36941                                     tag : 'i',
36942                                     cls : 'fa fa-chevron-left'
36943                                 }
36944                             ]
36945                         },
36946                         {
36947                             tag : 'div',
36948                             cls : 'roo-document-slider-thumb',
36949                             cn : [
36950                                 {
36951                                     tag : 'img',
36952                                     cls : 'roo-document-slider-image'
36953                                 }
36954                             ]
36955                         },
36956                         {
36957                             tag : 'div',
36958                             cls : 'roo-document-slider-next',
36959                             cn : [
36960                                 {
36961                                     tag : 'i',
36962                                     cls : 'fa fa-chevron-right'
36963                                 }
36964                             ]
36965                         }
36966                     ]
36967                 }
36968             ]
36969         };
36970         
36971         return cfg;
36972     },
36973     
36974     initEvents : function()
36975     {
36976         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36977         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36978         
36979         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36980         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36981         
36982         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36983         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36984         
36985         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36986         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36987         
36988         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36989         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36990         
36991         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36992         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36993         
36994         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36995         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36996         
36997         this.thumbEl.on('click', this.onClick, this);
36998         
36999         this.prevIndicator.on('click', this.prev, this);
37000         
37001         this.nextIndicator.on('click', this.next, this);
37002         
37003     },
37004     
37005     initial : function()
37006     {
37007         if(this.files.length){
37008             this.indicator = 1;
37009             this.update()
37010         }
37011         
37012         this.fireEvent('initial', this);
37013     },
37014     
37015     update : function()
37016     {
37017         this.imageEl.attr('src', this.files[this.indicator - 1]);
37018         
37019         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37020         
37021         this.prevIndicator.show();
37022         
37023         if(this.indicator == 1){
37024             this.prevIndicator.hide();
37025         }
37026         
37027         this.nextIndicator.show();
37028         
37029         if(this.indicator == this.files.length){
37030             this.nextIndicator.hide();
37031         }
37032         
37033         this.thumbEl.scrollTo('top');
37034         
37035         this.fireEvent('update', this);
37036     },
37037     
37038     onClick : function(e)
37039     {
37040         e.preventDefault();
37041         
37042         this.fireEvent('click', this);
37043     },
37044     
37045     prev : function(e)
37046     {
37047         e.preventDefault();
37048         
37049         this.indicator = Math.max(1, this.indicator - 1);
37050         
37051         this.update();
37052     },
37053     
37054     next : function(e)
37055     {
37056         e.preventDefault();
37057         
37058         this.indicator = Math.min(this.files.length, this.indicator + 1);
37059         
37060         this.update();
37061     }
37062 });
37063 /*
37064  * - LGPL
37065  *
37066  * RadioSet
37067  *
37068  *
37069  */
37070
37071 /**
37072  * @class Roo.bootstrap.form.RadioSet
37073  * @extends Roo.bootstrap.form.Input
37074  * @children Roo.bootstrap.form.Radio
37075  * Bootstrap RadioSet class
37076  * @cfg {String} indicatorpos (left|right) default left
37077  * @cfg {Boolean} inline (true|false) inline the element (default true)
37078  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37079  * @constructor
37080  * Create a new RadioSet
37081  * @param {Object} config The config object
37082  */
37083
37084 Roo.bootstrap.form.RadioSet = function(config){
37085     
37086     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37087     
37088     this.radioes = [];
37089     
37090     Roo.bootstrap.form.RadioSet.register(this);
37091     
37092     this.addEvents({
37093         /**
37094         * @event check
37095         * Fires when the element is checked or unchecked.
37096         * @param {Roo.bootstrap.form.RadioSet} this This radio
37097         * @param {Roo.bootstrap.form.Radio} item The checked item
37098         */
37099        check : true,
37100        /**
37101         * @event click
37102         * Fires when the element is click.
37103         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37104         * @param {Roo.bootstrap.form.Radio} item The checked item
37105         * @param {Roo.EventObject} e The event object
37106         */
37107        click : true
37108     });
37109     
37110 };
37111
37112 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37113
37114     radioes : false,
37115     
37116     inline : true,
37117     
37118     weight : '',
37119     
37120     indicatorpos : 'left',
37121     
37122     getAutoCreate : function()
37123     {
37124         var label = {
37125             tag : 'label',
37126             cls : 'roo-radio-set-label',
37127             cn : [
37128                 {
37129                     tag : 'span',
37130                     html : this.fieldLabel
37131                 }
37132             ]
37133         };
37134         if (Roo.bootstrap.version == 3) {
37135             
37136             
37137             if(this.indicatorpos == 'left'){
37138                 label.cn.unshift({
37139                     tag : 'i',
37140                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37141                     tooltip : 'This field is required'
37142                 });
37143             } else {
37144                 label.cn.push({
37145                     tag : 'i',
37146                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37147                     tooltip : 'This field is required'
37148                 });
37149             }
37150         }
37151         var items = {
37152             tag : 'div',
37153             cls : 'roo-radio-set-items'
37154         };
37155         
37156         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37157         
37158         if (align === 'left' && this.fieldLabel.length) {
37159             
37160             items = {
37161                 cls : "roo-radio-set-right", 
37162                 cn: [
37163                     items
37164                 ]
37165             };
37166             
37167             if(this.labelWidth > 12){
37168                 label.style = "width: " + this.labelWidth + 'px';
37169             }
37170             
37171             if(this.labelWidth < 13 && this.labelmd == 0){
37172                 this.labelmd = this.labelWidth;
37173             }
37174             
37175             if(this.labellg > 0){
37176                 label.cls += ' col-lg-' + this.labellg;
37177                 items.cls += ' col-lg-' + (12 - this.labellg);
37178             }
37179             
37180             if(this.labelmd > 0){
37181                 label.cls += ' col-md-' + this.labelmd;
37182                 items.cls += ' col-md-' + (12 - this.labelmd);
37183             }
37184             
37185             if(this.labelsm > 0){
37186                 label.cls += ' col-sm-' + this.labelsm;
37187                 items.cls += ' col-sm-' + (12 - this.labelsm);
37188             }
37189             
37190             if(this.labelxs > 0){
37191                 label.cls += ' col-xs-' + this.labelxs;
37192                 items.cls += ' col-xs-' + (12 - this.labelxs);
37193             }
37194         }
37195         
37196         var cfg = {
37197             tag : 'div',
37198             cls : 'roo-radio-set',
37199             cn : [
37200                 {
37201                     tag : 'input',
37202                     cls : 'roo-radio-set-input',
37203                     type : 'hidden',
37204                     name : this.name,
37205                     value : this.value ? this.value :  ''
37206                 },
37207                 label,
37208                 items
37209             ]
37210         };
37211         
37212         if(this.weight.length){
37213             cfg.cls += ' roo-radio-' + this.weight;
37214         }
37215         
37216         if(this.inline) {
37217             cfg.cls += ' roo-radio-set-inline';
37218         }
37219         
37220         var settings=this;
37221         ['xs','sm','md','lg'].map(function(size){
37222             if (settings[size]) {
37223                 cfg.cls += ' col-' + size + '-' + settings[size];
37224             }
37225         });
37226         
37227         return cfg;
37228         
37229     },
37230
37231     initEvents : function()
37232     {
37233         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37234         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37235         
37236         if(!this.fieldLabel.length){
37237             this.labelEl.hide();
37238         }
37239         
37240         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37241         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37242         
37243         this.indicator = this.indicatorEl();
37244         
37245         if(this.indicator){
37246             this.indicator.addClass('invisible');
37247         }
37248         
37249         this.originalValue = this.getValue();
37250         
37251     },
37252     
37253     inputEl: function ()
37254     {
37255         return this.el.select('.roo-radio-set-input', true).first();
37256     },
37257     
37258     getChildContainer : function()
37259     {
37260         return this.itemsEl;
37261     },
37262     
37263     register : function(item)
37264     {
37265         this.radioes.push(item);
37266         
37267     },
37268     
37269     validate : function()
37270     {   
37271         if(this.getVisibilityEl().hasClass('hidden')){
37272             return true;
37273         }
37274         
37275         var valid = false;
37276         
37277         Roo.each(this.radioes, function(i){
37278             if(!i.checked){
37279                 return;
37280             }
37281             
37282             valid = true;
37283             return false;
37284         });
37285         
37286         if(this.allowBlank) {
37287             return true;
37288         }
37289         
37290         if(this.disabled || valid){
37291             this.markValid();
37292             return true;
37293         }
37294         
37295         this.markInvalid();
37296         return false;
37297         
37298     },
37299     
37300     markValid : function()
37301     {
37302         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37303             this.indicatorEl().removeClass('visible');
37304             this.indicatorEl().addClass('invisible');
37305         }
37306         
37307         
37308         if (Roo.bootstrap.version == 3) {
37309             this.el.removeClass([this.invalidClass, this.validClass]);
37310             this.el.addClass(this.validClass);
37311         } else {
37312             this.el.removeClass(['is-invalid','is-valid']);
37313             this.el.addClass(['is-valid']);
37314         }
37315         this.fireEvent('valid', this);
37316     },
37317     
37318     markInvalid : function(msg)
37319     {
37320         if(this.allowBlank || this.disabled){
37321             return;
37322         }
37323         
37324         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37325             this.indicatorEl().removeClass('invisible');
37326             this.indicatorEl().addClass('visible');
37327         }
37328         if (Roo.bootstrap.version == 3) {
37329             this.el.removeClass([this.invalidClass, this.validClass]);
37330             this.el.addClass(this.invalidClass);
37331         } else {
37332             this.el.removeClass(['is-invalid','is-valid']);
37333             this.el.addClass(['is-invalid']);
37334         }
37335         
37336         this.fireEvent('invalid', this, msg);
37337         
37338     },
37339     
37340     setValue : function(v, suppressEvent)
37341     {   
37342         if(this.value === v){
37343             return;
37344         }
37345         
37346         this.value = v;
37347         
37348         if(this.rendered){
37349             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37350         }
37351         
37352         Roo.each(this.radioes, function(i){
37353             i.checked = false;
37354             i.el.removeClass('checked');
37355         });
37356         
37357         Roo.each(this.radioes, function(i){
37358             
37359             if(i.value === v || i.value.toString() === v.toString()){
37360                 i.checked = true;
37361                 i.el.addClass('checked');
37362                 
37363                 if(suppressEvent !== true){
37364                     this.fireEvent('check', this, i);
37365                 }
37366                 
37367                 return false;
37368             }
37369             
37370         }, this);
37371         
37372         this.validate();
37373     },
37374     
37375     clearInvalid : function(){
37376         
37377         if(!this.el || this.preventMark){
37378             return;
37379         }
37380         
37381         this.el.removeClass([this.invalidClass]);
37382         
37383         this.fireEvent('valid', this);
37384     }
37385     
37386 });
37387
37388 Roo.apply(Roo.bootstrap.form.RadioSet, {
37389     
37390     groups: {},
37391     
37392     register : function(set)
37393     {
37394         this.groups[set.name] = set;
37395     },
37396     
37397     get: function(name) 
37398     {
37399         if (typeof(this.groups[name]) == 'undefined') {
37400             return false;
37401         }
37402         
37403         return this.groups[name] ;
37404     }
37405     
37406 });
37407 /*
37408  * Based on:
37409  * Ext JS Library 1.1.1
37410  * Copyright(c) 2006-2007, Ext JS, LLC.
37411  *
37412  * Originally Released Under LGPL - original licence link has changed is not relivant.
37413  *
37414  * Fork - LGPL
37415  * <script type="text/javascript">
37416  */
37417
37418
37419 /**
37420  * @class Roo.bootstrap.SplitBar
37421  * @extends Roo.util.Observable
37422  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37423  * <br><br>
37424  * Usage:
37425  * <pre><code>
37426 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37427                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37428 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37429 split.minSize = 100;
37430 split.maxSize = 600;
37431 split.animate = true;
37432 split.on('moved', splitterMoved);
37433 </code></pre>
37434  * @constructor
37435  * Create a new SplitBar
37436  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37437  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37438  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37439  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37440                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37441                         position of the SplitBar).
37442  */
37443 Roo.bootstrap.SplitBar = function(cfg){
37444     
37445     /** @private */
37446     
37447     //{
37448     //  dragElement : elm
37449     //  resizingElement: el,
37450         // optional..
37451     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37452     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37453         // existingProxy ???
37454     //}
37455     
37456     this.el = Roo.get(cfg.dragElement, true);
37457     this.el.dom.unselectable = "on";
37458     /** @private */
37459     this.resizingEl = Roo.get(cfg.resizingElement, true);
37460
37461     /**
37462      * @private
37463      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37464      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37465      * @type Number
37466      */
37467     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37468     
37469     /**
37470      * The minimum size of the resizing element. (Defaults to 0)
37471      * @type Number
37472      */
37473     this.minSize = 0;
37474     
37475     /**
37476      * The maximum size of the resizing element. (Defaults to 2000)
37477      * @type Number
37478      */
37479     this.maxSize = 2000;
37480     
37481     /**
37482      * Whether to animate the transition to the new size
37483      * @type Boolean
37484      */
37485     this.animate = false;
37486     
37487     /**
37488      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37489      * @type Boolean
37490      */
37491     this.useShim = false;
37492     
37493     /** @private */
37494     this.shim = null;
37495     
37496     if(!cfg.existingProxy){
37497         /** @private */
37498         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37499     }else{
37500         this.proxy = Roo.get(cfg.existingProxy).dom;
37501     }
37502     /** @private */
37503     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37504     
37505     /** @private */
37506     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37507     
37508     /** @private */
37509     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37510     
37511     /** @private */
37512     this.dragSpecs = {};
37513     
37514     /**
37515      * @private The adapter to use to positon and resize elements
37516      */
37517     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37518     this.adapter.init(this);
37519     
37520     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37521         /** @private */
37522         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37523         this.el.addClass("roo-splitbar-h");
37524     }else{
37525         /** @private */
37526         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37527         this.el.addClass("roo-splitbar-v");
37528     }
37529     
37530     this.addEvents({
37531         /**
37532          * @event resize
37533          * Fires when the splitter is moved (alias for {@link #event-moved})
37534          * @param {Roo.bootstrap.SplitBar} this
37535          * @param {Number} newSize the new width or height
37536          */
37537         "resize" : true,
37538         /**
37539          * @event moved
37540          * Fires when the splitter is moved
37541          * @param {Roo.bootstrap.SplitBar} this
37542          * @param {Number} newSize the new width or height
37543          */
37544         "moved" : true,
37545         /**
37546          * @event beforeresize
37547          * Fires before the splitter is dragged
37548          * @param {Roo.bootstrap.SplitBar} this
37549          */
37550         "beforeresize" : true,
37551
37552         "beforeapply" : true
37553     });
37554
37555     Roo.util.Observable.call(this);
37556 };
37557
37558 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37559     onStartProxyDrag : function(x, y){
37560         this.fireEvent("beforeresize", this);
37561         if(!this.overlay){
37562             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37563             o.unselectable();
37564             o.enableDisplayMode("block");
37565             // all splitbars share the same overlay
37566             Roo.bootstrap.SplitBar.prototype.overlay = o;
37567         }
37568         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37569         this.overlay.show();
37570         Roo.get(this.proxy).setDisplayed("block");
37571         var size = this.adapter.getElementSize(this);
37572         this.activeMinSize = this.getMinimumSize();;
37573         this.activeMaxSize = this.getMaximumSize();;
37574         var c1 = size - this.activeMinSize;
37575         var c2 = Math.max(this.activeMaxSize - size, 0);
37576         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37577             this.dd.resetConstraints();
37578             this.dd.setXConstraint(
37579                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37580                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37581             );
37582             this.dd.setYConstraint(0, 0);
37583         }else{
37584             this.dd.resetConstraints();
37585             this.dd.setXConstraint(0, 0);
37586             this.dd.setYConstraint(
37587                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37588                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37589             );
37590          }
37591         this.dragSpecs.startSize = size;
37592         this.dragSpecs.startPoint = [x, y];
37593         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37594     },
37595     
37596     /** 
37597      * @private Called after the drag operation by the DDProxy
37598      */
37599     onEndProxyDrag : function(e){
37600         Roo.get(this.proxy).setDisplayed(false);
37601         var endPoint = Roo.lib.Event.getXY(e);
37602         if(this.overlay){
37603             this.overlay.hide();
37604         }
37605         var newSize;
37606         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37607             newSize = this.dragSpecs.startSize + 
37608                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37609                     endPoint[0] - this.dragSpecs.startPoint[0] :
37610                     this.dragSpecs.startPoint[0] - endPoint[0]
37611                 );
37612         }else{
37613             newSize = this.dragSpecs.startSize + 
37614                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37615                     endPoint[1] - this.dragSpecs.startPoint[1] :
37616                     this.dragSpecs.startPoint[1] - endPoint[1]
37617                 );
37618         }
37619         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37620         if(newSize != this.dragSpecs.startSize){
37621             if(this.fireEvent('beforeapply', this, newSize) !== false){
37622                 this.adapter.setElementSize(this, newSize);
37623                 this.fireEvent("moved", this, newSize);
37624                 this.fireEvent("resize", this, newSize);
37625             }
37626         }
37627     },
37628     
37629     /**
37630      * Get the adapter this SplitBar uses
37631      * @return The adapter object
37632      */
37633     getAdapter : function(){
37634         return this.adapter;
37635     },
37636     
37637     /**
37638      * Set the adapter this SplitBar uses
37639      * @param {Object} adapter A SplitBar adapter object
37640      */
37641     setAdapter : function(adapter){
37642         this.adapter = adapter;
37643         this.adapter.init(this);
37644     },
37645     
37646     /**
37647      * Gets the minimum size for the resizing element
37648      * @return {Number} The minimum size
37649      */
37650     getMinimumSize : function(){
37651         return this.minSize;
37652     },
37653     
37654     /**
37655      * Sets the minimum size for the resizing element
37656      * @param {Number} minSize The minimum size
37657      */
37658     setMinimumSize : function(minSize){
37659         this.minSize = minSize;
37660     },
37661     
37662     /**
37663      * Gets the maximum size for the resizing element
37664      * @return {Number} The maximum size
37665      */
37666     getMaximumSize : function(){
37667         return this.maxSize;
37668     },
37669     
37670     /**
37671      * Sets the maximum size for the resizing element
37672      * @param {Number} maxSize The maximum size
37673      */
37674     setMaximumSize : function(maxSize){
37675         this.maxSize = maxSize;
37676     },
37677     
37678     /**
37679      * Sets the initialize size for the resizing element
37680      * @param {Number} size The initial size
37681      */
37682     setCurrentSize : function(size){
37683         var oldAnimate = this.animate;
37684         this.animate = false;
37685         this.adapter.setElementSize(this, size);
37686         this.animate = oldAnimate;
37687     },
37688     
37689     /**
37690      * Destroy this splitbar. 
37691      * @param {Boolean} removeEl True to remove the element
37692      */
37693     destroy : function(removeEl){
37694         if(this.shim){
37695             this.shim.remove();
37696         }
37697         this.dd.unreg();
37698         this.proxy.parentNode.removeChild(this.proxy);
37699         if(removeEl){
37700             this.el.remove();
37701         }
37702     }
37703 });
37704
37705 /**
37706  * @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.
37707  */
37708 Roo.bootstrap.SplitBar.createProxy = function(dir){
37709     var proxy = new Roo.Element(document.createElement("div"));
37710     proxy.unselectable();
37711     var cls = 'roo-splitbar-proxy';
37712     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37713     document.body.appendChild(proxy.dom);
37714     return proxy.dom;
37715 };
37716
37717 /** 
37718  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37719  * Default Adapter. It assumes the splitter and resizing element are not positioned
37720  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37721  */
37722 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37723 };
37724
37725 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37726     // do nothing for now
37727     init : function(s){
37728     
37729     },
37730     /**
37731      * Called before drag operations to get the current size of the resizing element. 
37732      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37733      */
37734      getElementSize : function(s){
37735         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37736             return s.resizingEl.getWidth();
37737         }else{
37738             return s.resizingEl.getHeight();
37739         }
37740     },
37741     
37742     /**
37743      * Called after drag operations to set the size of the resizing element.
37744      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37745      * @param {Number} newSize The new size to set
37746      * @param {Function} onComplete A function to be invoked when resizing is complete
37747      */
37748     setElementSize : function(s, newSize, onComplete){
37749         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37750             if(!s.animate){
37751                 s.resizingEl.setWidth(newSize);
37752                 if(onComplete){
37753                     onComplete(s, newSize);
37754                 }
37755             }else{
37756                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37757             }
37758         }else{
37759             
37760             if(!s.animate){
37761                 s.resizingEl.setHeight(newSize);
37762                 if(onComplete){
37763                     onComplete(s, newSize);
37764                 }
37765             }else{
37766                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37767             }
37768         }
37769     }
37770 };
37771
37772 /** 
37773  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37774  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37775  * Adapter that  moves the splitter element to align with the resized sizing element. 
37776  * Used with an absolute positioned SplitBar.
37777  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37778  * document.body, make sure you assign an id to the body element.
37779  */
37780 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37781     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37782     this.container = Roo.get(container);
37783 };
37784
37785 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37786     init : function(s){
37787         this.basic.init(s);
37788     },
37789     
37790     getElementSize : function(s){
37791         return this.basic.getElementSize(s);
37792     },
37793     
37794     setElementSize : function(s, newSize, onComplete){
37795         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37796     },
37797     
37798     moveSplitter : function(s){
37799         var yes = Roo.bootstrap.SplitBar;
37800         switch(s.placement){
37801             case yes.LEFT:
37802                 s.el.setX(s.resizingEl.getRight());
37803                 break;
37804             case yes.RIGHT:
37805                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37806                 break;
37807             case yes.TOP:
37808                 s.el.setY(s.resizingEl.getBottom());
37809                 break;
37810             case yes.BOTTOM:
37811                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37812                 break;
37813         }
37814     }
37815 };
37816
37817 /**
37818  * Orientation constant - Create a vertical SplitBar
37819  * @static
37820  * @type Number
37821  */
37822 Roo.bootstrap.SplitBar.VERTICAL = 1;
37823
37824 /**
37825  * Orientation constant - Create a horizontal SplitBar
37826  * @static
37827  * @type Number
37828  */
37829 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37830
37831 /**
37832  * Placement constant - The resizing element is to the left of the splitter element
37833  * @static
37834  * @type Number
37835  */
37836 Roo.bootstrap.SplitBar.LEFT = 1;
37837
37838 /**
37839  * Placement constant - The resizing element is to the right of the splitter element
37840  * @static
37841  * @type Number
37842  */
37843 Roo.bootstrap.SplitBar.RIGHT = 2;
37844
37845 /**
37846  * Placement constant - The resizing element is positioned above the splitter element
37847  * @static
37848  * @type Number
37849  */
37850 Roo.bootstrap.SplitBar.TOP = 3;
37851
37852 /**
37853  * Placement constant - The resizing element is positioned under splitter element
37854  * @static
37855  * @type Number
37856  */
37857 Roo.bootstrap.SplitBar.BOTTOM = 4;
37858 /*
37859  * Based on:
37860  * Ext JS Library 1.1.1
37861  * Copyright(c) 2006-2007, Ext JS, LLC.
37862  *
37863  * Originally Released Under LGPL - original licence link has changed is not relivant.
37864  *
37865  * Fork - LGPL
37866  * <script type="text/javascript">
37867  */
37868
37869 /**
37870  * @class Roo.bootstrap.layout.Manager
37871  * @extends Roo.bootstrap.Component
37872  * @abstract
37873  * Base class for layout managers.
37874  */
37875 Roo.bootstrap.layout.Manager = function(config)
37876 {
37877     this.monitorWindowResize = true; // do this before we apply configuration.
37878     
37879     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37880
37881
37882
37883
37884
37885     /** false to disable window resize monitoring @type Boolean */
37886     
37887     this.regions = {};
37888     this.addEvents({
37889         /**
37890          * @event layout
37891          * Fires when a layout is performed.
37892          * @param {Roo.LayoutManager} this
37893          */
37894         "layout" : true,
37895         /**
37896          * @event regionresized
37897          * Fires when the user resizes a region.
37898          * @param {Roo.LayoutRegion} region The resized region
37899          * @param {Number} newSize The new size (width for east/west, height for north/south)
37900          */
37901         "regionresized" : true,
37902         /**
37903          * @event regioncollapsed
37904          * Fires when a region is collapsed.
37905          * @param {Roo.LayoutRegion} region The collapsed region
37906          */
37907         "regioncollapsed" : true,
37908         /**
37909          * @event regionexpanded
37910          * Fires when a region is expanded.
37911          * @param {Roo.LayoutRegion} region The expanded region
37912          */
37913         "regionexpanded" : true
37914     });
37915     this.updating = false;
37916
37917     if (config.el) {
37918         this.el = Roo.get(config.el);
37919         this.initEvents();
37920     }
37921
37922 };
37923
37924 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37925
37926
37927     regions : null,
37928
37929     monitorWindowResize : true,
37930
37931
37932     updating : false,
37933
37934
37935     onRender : function(ct, position)
37936     {
37937         if(!this.el){
37938             this.el = Roo.get(ct);
37939             this.initEvents();
37940         }
37941         //this.fireEvent('render',this);
37942     },
37943
37944
37945     initEvents: function()
37946     {
37947
37948
37949         // ie scrollbar fix
37950         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37951             document.body.scroll = "no";
37952         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37953             this.el.position('relative');
37954         }
37955         this.id = this.el.id;
37956         this.el.addClass("roo-layout-container");
37957         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37958         if(this.el.dom != document.body ) {
37959             this.el.on('resize', this.layout,this);
37960             this.el.on('show', this.layout,this);
37961         }
37962
37963     },
37964
37965     /**
37966      * Returns true if this layout is currently being updated
37967      * @return {Boolean}
37968      */
37969     isUpdating : function(){
37970         return this.updating;
37971     },
37972
37973     /**
37974      * Suspend the LayoutManager from doing auto-layouts while
37975      * making multiple add or remove calls
37976      */
37977     beginUpdate : function(){
37978         this.updating = true;
37979     },
37980
37981     /**
37982      * Restore auto-layouts and optionally disable the manager from performing a layout
37983      * @param {Boolean} noLayout true to disable a layout update
37984      */
37985     endUpdate : function(noLayout){
37986         this.updating = false;
37987         if(!noLayout){
37988             this.layout();
37989         }
37990     },
37991
37992     layout: function(){
37993         // abstract...
37994     },
37995
37996     onRegionResized : function(region, newSize){
37997         this.fireEvent("regionresized", region, newSize);
37998         this.layout();
37999     },
38000
38001     onRegionCollapsed : function(region){
38002         this.fireEvent("regioncollapsed", region);
38003     },
38004
38005     onRegionExpanded : function(region){
38006         this.fireEvent("regionexpanded", region);
38007     },
38008
38009     /**
38010      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38011      * performs box-model adjustments.
38012      * @return {Object} The size as an object {width: (the width), height: (the height)}
38013      */
38014     getViewSize : function()
38015     {
38016         var size;
38017         if(this.el.dom != document.body){
38018             size = this.el.getSize();
38019         }else{
38020             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38021         }
38022         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38023         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38024         return size;
38025     },
38026
38027     /**
38028      * Returns the Element this layout is bound to.
38029      * @return {Roo.Element}
38030      */
38031     getEl : function(){
38032         return this.el;
38033     },
38034
38035     /**
38036      * Returns the specified region.
38037      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38038      * @return {Roo.LayoutRegion}
38039      */
38040     getRegion : function(target){
38041         return this.regions[target.toLowerCase()];
38042     },
38043
38044     onWindowResize : function(){
38045         if(this.monitorWindowResize){
38046             this.layout();
38047         }
38048     }
38049 });
38050 /*
38051  * Based on:
38052  * Ext JS Library 1.1.1
38053  * Copyright(c) 2006-2007, Ext JS, LLC.
38054  *
38055  * Originally Released Under LGPL - original licence link has changed is not relivant.
38056  *
38057  * Fork - LGPL
38058  * <script type="text/javascript">
38059  */
38060 /**
38061  * @class Roo.bootstrap.layout.Border
38062  * @extends Roo.bootstrap.layout.Manager
38063  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38064  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38065  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38066  * please see: examples/bootstrap/nested.html<br><br>
38067  
38068 <b>The container the layout is rendered into can be either the body element or any other element.
38069 If it is not the body element, the container needs to either be an absolute positioned element,
38070 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38071 the container size if it is not the body element.</b>
38072
38073 * @constructor
38074 * Create a new Border
38075 * @param {Object} config Configuration options
38076  */
38077 Roo.bootstrap.layout.Border = function(config){
38078     config = config || {};
38079     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38080     
38081     
38082     
38083     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38084         if(config[region]){
38085             config[region].region = region;
38086             this.addRegion(config[region]);
38087         }
38088     },this);
38089     
38090 };
38091
38092 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38093
38094 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38095     
38096         /**
38097          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38098          */
38099         /**
38100          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38101          */
38102         /**
38103          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38104          */
38105         /**
38106          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38107          */
38108         /**
38109          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38110          */
38111         
38112         
38113         
38114         
38115     parent : false, // this might point to a 'nest' or a ???
38116     
38117     /**
38118      * Creates and adds a new region if it doesn't already exist.
38119      * @param {String} target The target region key (north, south, east, west or center).
38120      * @param {Object} config The regions config object
38121      * @return {BorderLayoutRegion} The new region
38122      */
38123     addRegion : function(config)
38124     {
38125         if(!this.regions[config.region]){
38126             var r = this.factory(config);
38127             this.bindRegion(r);
38128         }
38129         return this.regions[config.region];
38130     },
38131
38132     // private (kinda)
38133     bindRegion : function(r){
38134         this.regions[r.config.region] = r;
38135         
38136         r.on("visibilitychange",    this.layout, this);
38137         r.on("paneladded",          this.layout, this);
38138         r.on("panelremoved",        this.layout, this);
38139         r.on("invalidated",         this.layout, this);
38140         r.on("resized",             this.onRegionResized, this);
38141         r.on("collapsed",           this.onRegionCollapsed, this);
38142         r.on("expanded",            this.onRegionExpanded, this);
38143     },
38144
38145     /**
38146      * Performs a layout update.
38147      */
38148     layout : function()
38149     {
38150         if(this.updating) {
38151             return;
38152         }
38153         
38154         // render all the rebions if they have not been done alreayd?
38155         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38156             if(this.regions[region] && !this.regions[region].bodyEl){
38157                 this.regions[region].onRender(this.el)
38158             }
38159         },this);
38160         
38161         var size = this.getViewSize();
38162         var w = size.width;
38163         var h = size.height;
38164         var centerW = w;
38165         var centerH = h;
38166         var centerY = 0;
38167         var centerX = 0;
38168         //var x = 0, y = 0;
38169
38170         var rs = this.regions;
38171         var north = rs["north"];
38172         var south = rs["south"]; 
38173         var west = rs["west"];
38174         var east = rs["east"];
38175         var center = rs["center"];
38176         //if(this.hideOnLayout){ // not supported anymore
38177             //c.el.setStyle("display", "none");
38178         //}
38179         if(north && north.isVisible()){
38180             var b = north.getBox();
38181             var m = north.getMargins();
38182             b.width = w - (m.left+m.right);
38183             b.x = m.left;
38184             b.y = m.top;
38185             centerY = b.height + b.y + m.bottom;
38186             centerH -= centerY;
38187             north.updateBox(this.safeBox(b));
38188         }
38189         if(south && south.isVisible()){
38190             var b = south.getBox();
38191             var m = south.getMargins();
38192             b.width = w - (m.left+m.right);
38193             b.x = m.left;
38194             var totalHeight = (b.height + m.top + m.bottom);
38195             b.y = h - totalHeight + m.top;
38196             centerH -= totalHeight;
38197             south.updateBox(this.safeBox(b));
38198         }
38199         if(west && west.isVisible()){
38200             var b = west.getBox();
38201             var m = west.getMargins();
38202             b.height = centerH - (m.top+m.bottom);
38203             b.x = m.left;
38204             b.y = centerY + m.top;
38205             var totalWidth = (b.width + m.left + m.right);
38206             centerX += totalWidth;
38207             centerW -= totalWidth;
38208             west.updateBox(this.safeBox(b));
38209         }
38210         if(east && east.isVisible()){
38211             var b = east.getBox();
38212             var m = east.getMargins();
38213             b.height = centerH - (m.top+m.bottom);
38214             var totalWidth = (b.width + m.left + m.right);
38215             b.x = w - totalWidth + m.left;
38216             b.y = centerY + m.top;
38217             centerW -= totalWidth;
38218             east.updateBox(this.safeBox(b));
38219         }
38220         if(center){
38221             var m = center.getMargins();
38222             var centerBox = {
38223                 x: centerX + m.left,
38224                 y: centerY + m.top,
38225                 width: centerW - (m.left+m.right),
38226                 height: centerH - (m.top+m.bottom)
38227             };
38228             //if(this.hideOnLayout){
38229                 //center.el.setStyle("display", "block");
38230             //}
38231             center.updateBox(this.safeBox(centerBox));
38232         }
38233         this.el.repaint();
38234         this.fireEvent("layout", this);
38235     },
38236
38237     // private
38238     safeBox : function(box){
38239         box.width = Math.max(0, box.width);
38240         box.height = Math.max(0, box.height);
38241         return box;
38242     },
38243
38244     /**
38245      * Adds a ContentPanel (or subclass) to this layout.
38246      * @param {String} target The target region key (north, south, east, west or center).
38247      * @param {Roo.ContentPanel} panel The panel to add
38248      * @return {Roo.ContentPanel} The added panel
38249      */
38250     add : function(target, panel){
38251          
38252         target = target.toLowerCase();
38253         return this.regions[target].add(panel);
38254     },
38255
38256     /**
38257      * Remove a ContentPanel (or subclass) to this layout.
38258      * @param {String} target The target region key (north, south, east, west or center).
38259      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38260      * @return {Roo.ContentPanel} The removed panel
38261      */
38262     remove : function(target, panel){
38263         target = target.toLowerCase();
38264         return this.regions[target].remove(panel);
38265     },
38266
38267     /**
38268      * Searches all regions for a panel with the specified id
38269      * @param {String} panelId
38270      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38271      */
38272     findPanel : function(panelId){
38273         var rs = this.regions;
38274         for(var target in rs){
38275             if(typeof rs[target] != "function"){
38276                 var p = rs[target].getPanel(panelId);
38277                 if(p){
38278                     return p;
38279                 }
38280             }
38281         }
38282         return null;
38283     },
38284
38285     /**
38286      * Searches all regions for a panel with the specified id and activates (shows) it.
38287      * @param {String/ContentPanel} panelId The panels id or the panel itself
38288      * @return {Roo.ContentPanel} The shown panel or null
38289      */
38290     showPanel : function(panelId) {
38291       var rs = this.regions;
38292       for(var target in rs){
38293          var r = rs[target];
38294          if(typeof r != "function"){
38295             if(r.hasPanel(panelId)){
38296                return r.showPanel(panelId);
38297             }
38298          }
38299       }
38300       return null;
38301    },
38302
38303    /**
38304      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38305      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38306      */
38307    /*
38308     restoreState : function(provider){
38309         if(!provider){
38310             provider = Roo.state.Manager;
38311         }
38312         var sm = new Roo.LayoutStateManager();
38313         sm.init(this, provider);
38314     },
38315 */
38316  
38317  
38318     /**
38319      * Adds a xtype elements to the layout.
38320      * <pre><code>
38321
38322 layout.addxtype({
38323        xtype : 'ContentPanel',
38324        region: 'west',
38325        items: [ .... ]
38326    }
38327 );
38328
38329 layout.addxtype({
38330         xtype : 'NestedLayoutPanel',
38331         region: 'west',
38332         layout: {
38333            center: { },
38334            west: { }   
38335         },
38336         items : [ ... list of content panels or nested layout panels.. ]
38337    }
38338 );
38339 </code></pre>
38340      * @param {Object} cfg Xtype definition of item to add.
38341      */
38342     addxtype : function(cfg)
38343     {
38344         // basically accepts a pannel...
38345         // can accept a layout region..!?!?
38346         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38347         
38348         
38349         // theory?  children can only be panels??
38350         
38351         //if (!cfg.xtype.match(/Panel$/)) {
38352         //    return false;
38353         //}
38354         var ret = false;
38355         
38356         if (typeof(cfg.region) == 'undefined') {
38357             Roo.log("Failed to add Panel, region was not set");
38358             Roo.log(cfg);
38359             return false;
38360         }
38361         var region = cfg.region;
38362         delete cfg.region;
38363         
38364           
38365         var xitems = [];
38366         if (cfg.items) {
38367             xitems = cfg.items;
38368             delete cfg.items;
38369         }
38370         var nb = false;
38371         
38372         if ( region == 'center') {
38373             Roo.log("Center: " + cfg.title);
38374         }
38375         
38376         
38377         switch(cfg.xtype) 
38378         {
38379             case 'Content':  // ContentPanel (el, cfg)
38380             case 'Scroll':  // ContentPanel (el, cfg)
38381             case 'View': 
38382                 cfg.autoCreate = cfg.autoCreate || true;
38383                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38384                 //} else {
38385                 //    var el = this.el.createChild();
38386                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38387                 //}
38388                 
38389                 this.add(region, ret);
38390                 break;
38391             
38392             /*
38393             case 'TreePanel': // our new panel!
38394                 cfg.el = this.el.createChild();
38395                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38396                 this.add(region, ret);
38397                 break;
38398             */
38399             
38400             case 'Nest': 
38401                 // create a new Layout (which is  a Border Layout...
38402                 
38403                 var clayout = cfg.layout;
38404                 clayout.el  = this.el.createChild();
38405                 clayout.items   = clayout.items  || [];
38406                 
38407                 delete cfg.layout;
38408                 
38409                 // replace this exitems with the clayout ones..
38410                 xitems = clayout.items;
38411                  
38412                 // force background off if it's in center...
38413                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38414                     cfg.background = false;
38415                 }
38416                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38417                 
38418                 
38419                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38420                 //console.log('adding nested layout panel '  + cfg.toSource());
38421                 this.add(region, ret);
38422                 nb = {}; /// find first...
38423                 break;
38424             
38425             case 'Grid':
38426                 
38427                 // needs grid and region
38428                 
38429                 //var el = this.getRegion(region).el.createChild();
38430                 /*
38431                  *var el = this.el.createChild();
38432                 // create the grid first...
38433                 cfg.grid.container = el;
38434                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38435                 */
38436                 
38437                 if (region == 'center' && this.active ) {
38438                     cfg.background = false;
38439                 }
38440                 
38441                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38442                 
38443                 this.add(region, ret);
38444                 /*
38445                 if (cfg.background) {
38446                     // render grid on panel activation (if panel background)
38447                     ret.on('activate', function(gp) {
38448                         if (!gp.grid.rendered) {
38449                     //        gp.grid.render(el);
38450                         }
38451                     });
38452                 } else {
38453                   //  cfg.grid.render(el);
38454                 }
38455                 */
38456                 break;
38457            
38458            
38459             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38460                 // it was the old xcomponent building that caused this before.
38461                 // espeically if border is the top element in the tree.
38462                 ret = this;
38463                 break; 
38464                 
38465                     
38466                 
38467                 
38468                 
38469             default:
38470                 /*
38471                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38472                     
38473                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38474                     this.add(region, ret);
38475                 } else {
38476                 */
38477                     Roo.log(cfg);
38478                     throw "Can not add '" + cfg.xtype + "' to Border";
38479                     return null;
38480              
38481                                 
38482              
38483         }
38484         this.beginUpdate();
38485         // add children..
38486         var region = '';
38487         var abn = {};
38488         Roo.each(xitems, function(i)  {
38489             region = nb && i.region ? i.region : false;
38490             
38491             var add = ret.addxtype(i);
38492            
38493             if (region) {
38494                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38495                 if (!i.background) {
38496                     abn[region] = nb[region] ;
38497                 }
38498             }
38499             
38500         });
38501         this.endUpdate();
38502
38503         // make the last non-background panel active..
38504         //if (nb) { Roo.log(abn); }
38505         if (nb) {
38506             
38507             for(var r in abn) {
38508                 region = this.getRegion(r);
38509                 if (region) {
38510                     // tried using nb[r], but it does not work..
38511                      
38512                     region.showPanel(abn[r]);
38513                    
38514                 }
38515             }
38516         }
38517         return ret;
38518         
38519     },
38520     
38521     
38522 // private
38523     factory : function(cfg)
38524     {
38525         
38526         var validRegions = Roo.bootstrap.layout.Border.regions;
38527
38528         var target = cfg.region;
38529         cfg.mgr = this;
38530         
38531         var r = Roo.bootstrap.layout;
38532         Roo.log(target);
38533         switch(target){
38534             case "north":
38535                 return new r.North(cfg);
38536             case "south":
38537                 return new r.South(cfg);
38538             case "east":
38539                 return new r.East(cfg);
38540             case "west":
38541                 return new r.West(cfg);
38542             case "center":
38543                 return new r.Center(cfg);
38544         }
38545         throw 'Layout region "'+target+'" not supported.';
38546     }
38547     
38548     
38549 });
38550  /*
38551  * Based on:
38552  * Ext JS Library 1.1.1
38553  * Copyright(c) 2006-2007, Ext JS, LLC.
38554  *
38555  * Originally Released Under LGPL - original licence link has changed is not relivant.
38556  *
38557  * Fork - LGPL
38558  * <script type="text/javascript">
38559  */
38560  
38561 /**
38562  * @class Roo.bootstrap.layout.Basic
38563  * @extends Roo.util.Observable
38564  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38565  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38566  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38567  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38568  * @cfg {string}   region  the region that it inhabits..
38569  * @cfg {bool}   skipConfig skip config?
38570  * 
38571
38572  */
38573 Roo.bootstrap.layout.Basic = function(config){
38574     
38575     this.mgr = config.mgr;
38576     
38577     this.position = config.region;
38578     
38579     var skipConfig = config.skipConfig;
38580     
38581     this.events = {
38582         /**
38583          * @scope Roo.BasicLayoutRegion
38584          */
38585         
38586         /**
38587          * @event beforeremove
38588          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38589          * @param {Roo.LayoutRegion} this
38590          * @param {Roo.ContentPanel} panel The panel
38591          * @param {Object} e The cancel event object
38592          */
38593         "beforeremove" : true,
38594         /**
38595          * @event invalidated
38596          * Fires when the layout for this region is changed.
38597          * @param {Roo.LayoutRegion} this
38598          */
38599         "invalidated" : true,
38600         /**
38601          * @event visibilitychange
38602          * Fires when this region is shown or hidden 
38603          * @param {Roo.LayoutRegion} this
38604          * @param {Boolean} visibility true or false
38605          */
38606         "visibilitychange" : true,
38607         /**
38608          * @event paneladded
38609          * Fires when a panel is added. 
38610          * @param {Roo.LayoutRegion} this
38611          * @param {Roo.ContentPanel} panel The panel
38612          */
38613         "paneladded" : true,
38614         /**
38615          * @event panelremoved
38616          * Fires when a panel is removed. 
38617          * @param {Roo.LayoutRegion} this
38618          * @param {Roo.ContentPanel} panel The panel
38619          */
38620         "panelremoved" : true,
38621         /**
38622          * @event beforecollapse
38623          * Fires when this region before collapse.
38624          * @param {Roo.LayoutRegion} this
38625          */
38626         "beforecollapse" : true,
38627         /**
38628          * @event collapsed
38629          * Fires when this region is collapsed.
38630          * @param {Roo.LayoutRegion} this
38631          */
38632         "collapsed" : true,
38633         /**
38634          * @event expanded
38635          * Fires when this region is expanded.
38636          * @param {Roo.LayoutRegion} this
38637          */
38638         "expanded" : true,
38639         /**
38640          * @event slideshow
38641          * Fires when this region is slid into view.
38642          * @param {Roo.LayoutRegion} this
38643          */
38644         "slideshow" : true,
38645         /**
38646          * @event slidehide
38647          * Fires when this region slides out of view. 
38648          * @param {Roo.LayoutRegion} this
38649          */
38650         "slidehide" : true,
38651         /**
38652          * @event panelactivated
38653          * Fires when a panel is activated. 
38654          * @param {Roo.LayoutRegion} this
38655          * @param {Roo.ContentPanel} panel The activated panel
38656          */
38657         "panelactivated" : true,
38658         /**
38659          * @event resized
38660          * Fires when the user resizes this region. 
38661          * @param {Roo.LayoutRegion} this
38662          * @param {Number} newSize The new size (width for east/west, height for north/south)
38663          */
38664         "resized" : true
38665     };
38666     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38667     this.panels = new Roo.util.MixedCollection();
38668     this.panels.getKey = this.getPanelId.createDelegate(this);
38669     this.box = null;
38670     this.activePanel = null;
38671     // ensure listeners are added...
38672     
38673     if (config.listeners || config.events) {
38674         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38675             listeners : config.listeners || {},
38676             events : config.events || {}
38677         });
38678     }
38679     
38680     if(skipConfig !== true){
38681         this.applyConfig(config);
38682     }
38683 };
38684
38685 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38686 {
38687     getPanelId : function(p){
38688         return p.getId();
38689     },
38690     
38691     applyConfig : function(config){
38692         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38693         this.config = config;
38694         
38695     },
38696     
38697     /**
38698      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38699      * the width, for horizontal (north, south) the height.
38700      * @param {Number} newSize The new width or height
38701      */
38702     resizeTo : function(newSize){
38703         var el = this.el ? this.el :
38704                  (this.activePanel ? this.activePanel.getEl() : null);
38705         if(el){
38706             switch(this.position){
38707                 case "east":
38708                 case "west":
38709                     el.setWidth(newSize);
38710                     this.fireEvent("resized", this, newSize);
38711                 break;
38712                 case "north":
38713                 case "south":
38714                     el.setHeight(newSize);
38715                     this.fireEvent("resized", this, newSize);
38716                 break;                
38717             }
38718         }
38719     },
38720     
38721     getBox : function(){
38722         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38723     },
38724     
38725     getMargins : function(){
38726         return this.margins;
38727     },
38728     
38729     updateBox : function(box){
38730         this.box = box;
38731         var el = this.activePanel.getEl();
38732         el.dom.style.left = box.x + "px";
38733         el.dom.style.top = box.y + "px";
38734         this.activePanel.setSize(box.width, box.height);
38735     },
38736     
38737     /**
38738      * Returns the container element for this region.
38739      * @return {Roo.Element}
38740      */
38741     getEl : function(){
38742         return this.activePanel;
38743     },
38744     
38745     /**
38746      * Returns true if this region is currently visible.
38747      * @return {Boolean}
38748      */
38749     isVisible : function(){
38750         return this.activePanel ? true : false;
38751     },
38752     
38753     setActivePanel : function(panel){
38754         panel = this.getPanel(panel);
38755         if(this.activePanel && this.activePanel != panel){
38756             this.activePanel.setActiveState(false);
38757             this.activePanel.getEl().setLeftTop(-10000,-10000);
38758         }
38759         this.activePanel = panel;
38760         panel.setActiveState(true);
38761         if(this.box){
38762             panel.setSize(this.box.width, this.box.height);
38763         }
38764         this.fireEvent("panelactivated", this, panel);
38765         this.fireEvent("invalidated");
38766     },
38767     
38768     /**
38769      * Show the specified panel.
38770      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38771      * @return {Roo.ContentPanel} The shown panel or null
38772      */
38773     showPanel : function(panel){
38774         panel = this.getPanel(panel);
38775         if(panel){
38776             this.setActivePanel(panel);
38777         }
38778         return panel;
38779     },
38780     
38781     /**
38782      * Get the active panel for this region.
38783      * @return {Roo.ContentPanel} The active panel or null
38784      */
38785     getActivePanel : function(){
38786         return this.activePanel;
38787     },
38788     
38789     /**
38790      * Add the passed ContentPanel(s)
38791      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38792      * @return {Roo.ContentPanel} The panel added (if only one was added)
38793      */
38794     add : function(panel){
38795         if(arguments.length > 1){
38796             for(var i = 0, len = arguments.length; i < len; i++) {
38797                 this.add(arguments[i]);
38798             }
38799             return null;
38800         }
38801         if(this.hasPanel(panel)){
38802             this.showPanel(panel);
38803             return panel;
38804         }
38805         var el = panel.getEl();
38806         if(el.dom.parentNode != this.mgr.el.dom){
38807             this.mgr.el.dom.appendChild(el.dom);
38808         }
38809         if(panel.setRegion){
38810             panel.setRegion(this);
38811         }
38812         this.panels.add(panel);
38813         el.setStyle("position", "absolute");
38814         if(!panel.background){
38815             this.setActivePanel(panel);
38816             if(this.config.initialSize && this.panels.getCount()==1){
38817                 this.resizeTo(this.config.initialSize);
38818             }
38819         }
38820         this.fireEvent("paneladded", this, panel);
38821         return panel;
38822     },
38823     
38824     /**
38825      * Returns true if the panel is in this region.
38826      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38827      * @return {Boolean}
38828      */
38829     hasPanel : function(panel){
38830         if(typeof panel == "object"){ // must be panel obj
38831             panel = panel.getId();
38832         }
38833         return this.getPanel(panel) ? true : false;
38834     },
38835     
38836     /**
38837      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38838      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38839      * @param {Boolean} preservePanel Overrides the config preservePanel option
38840      * @return {Roo.ContentPanel} The panel that was removed
38841      */
38842     remove : function(panel, preservePanel){
38843         panel = this.getPanel(panel);
38844         if(!panel){
38845             return null;
38846         }
38847         var e = {};
38848         this.fireEvent("beforeremove", this, panel, e);
38849         if(e.cancel === true){
38850             return null;
38851         }
38852         var panelId = panel.getId();
38853         this.panels.removeKey(panelId);
38854         return panel;
38855     },
38856     
38857     /**
38858      * Returns the panel specified or null if it's not in this region.
38859      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38860      * @return {Roo.ContentPanel}
38861      */
38862     getPanel : function(id){
38863         if(typeof id == "object"){ // must be panel obj
38864             return id;
38865         }
38866         return this.panels.get(id);
38867     },
38868     
38869     /**
38870      * Returns this regions position (north/south/east/west/center).
38871      * @return {String} 
38872      */
38873     getPosition: function(){
38874         return this.position;    
38875     }
38876 });/*
38877  * Based on:
38878  * Ext JS Library 1.1.1
38879  * Copyright(c) 2006-2007, Ext JS, LLC.
38880  *
38881  * Originally Released Under LGPL - original licence link has changed is not relivant.
38882  *
38883  * Fork - LGPL
38884  * <script type="text/javascript">
38885  */
38886  
38887 /**
38888  * @class Roo.bootstrap.layout.Region
38889  * @extends Roo.bootstrap.layout.Basic
38890  * This class represents a region in a layout manager.
38891  
38892  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38893  * @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})
38894  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38895  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38896  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38897  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38898  * @cfg {String}    title           The title for the region (overrides panel titles)
38899  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38900  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38901  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38902  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38903  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38904  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38905  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38906  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38907  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38908  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38909
38910  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38911  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38912  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38913  * @cfg {Number}    width           For East/West panels
38914  * @cfg {Number}    height          For North/South panels
38915  * @cfg {Boolean}   split           To show the splitter
38916  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38917  * 
38918  * @cfg {string}   cls             Extra CSS classes to add to region
38919  * 
38920  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38921  * @cfg {string}   region  the region that it inhabits..
38922  *
38923
38924  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38925  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38926
38927  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38928  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38929  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38930  */
38931 Roo.bootstrap.layout.Region = function(config)
38932 {
38933     this.applyConfig(config);
38934
38935     var mgr = config.mgr;
38936     var pos = config.region;
38937     config.skipConfig = true;
38938     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38939     
38940     if (mgr.el) {
38941         this.onRender(mgr.el);   
38942     }
38943      
38944     this.visible = true;
38945     this.collapsed = false;
38946     this.unrendered_panels = [];
38947 };
38948
38949 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38950
38951     position: '', // set by wrapper (eg. north/south etc..)
38952     unrendered_panels : null,  // unrendered panels.
38953     
38954     tabPosition : false,
38955     
38956     mgr: false, // points to 'Border'
38957     
38958     
38959     createBody : function(){
38960         /** This region's body element 
38961         * @type Roo.Element */
38962         this.bodyEl = this.el.createChild({
38963                 tag: "div",
38964                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38965         });
38966     },
38967
38968     onRender: function(ctr, pos)
38969     {
38970         var dh = Roo.DomHelper;
38971         /** This region's container element 
38972         * @type Roo.Element */
38973         this.el = dh.append(ctr.dom, {
38974                 tag: "div",
38975                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38976             }, true);
38977         /** This region's title element 
38978         * @type Roo.Element */
38979     
38980         this.titleEl = dh.append(this.el.dom,  {
38981                 tag: "div",
38982                 unselectable: "on",
38983                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38984                 children:[
38985                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38986                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38987                 ]
38988             }, true);
38989         
38990         this.titleEl.enableDisplayMode();
38991         /** This region's title text element 
38992         * @type HTMLElement */
38993         this.titleTextEl = this.titleEl.dom.firstChild;
38994         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38995         /*
38996         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38997         this.closeBtn.enableDisplayMode();
38998         this.closeBtn.on("click", this.closeClicked, this);
38999         this.closeBtn.hide();
39000     */
39001         this.createBody(this.config);
39002         if(this.config.hideWhenEmpty){
39003             this.hide();
39004             this.on("paneladded", this.validateVisibility, this);
39005             this.on("panelremoved", this.validateVisibility, this);
39006         }
39007         if(this.autoScroll){
39008             this.bodyEl.setStyle("overflow", "auto");
39009         }else{
39010             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39011         }
39012         //if(c.titlebar !== false){
39013             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39014                 this.titleEl.hide();
39015             }else{
39016                 this.titleEl.show();
39017                 if(this.config.title){
39018                     this.titleTextEl.innerHTML = this.config.title;
39019                 }
39020             }
39021         //}
39022         if(this.config.collapsed){
39023             this.collapse(true);
39024         }
39025         if(this.config.hidden){
39026             this.hide();
39027         }
39028         
39029         if (this.unrendered_panels && this.unrendered_panels.length) {
39030             for (var i =0;i< this.unrendered_panels.length; i++) {
39031                 this.add(this.unrendered_panels[i]);
39032             }
39033             this.unrendered_panels = null;
39034             
39035         }
39036         
39037     },
39038     
39039     applyConfig : function(c)
39040     {
39041         /*
39042          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39043             var dh = Roo.DomHelper;
39044             if(c.titlebar !== false){
39045                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39046                 this.collapseBtn.on("click", this.collapse, this);
39047                 this.collapseBtn.enableDisplayMode();
39048                 /*
39049                 if(c.showPin === true || this.showPin){
39050                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39051                     this.stickBtn.enableDisplayMode();
39052                     this.stickBtn.on("click", this.expand, this);
39053                     this.stickBtn.hide();
39054                 }
39055                 
39056             }
39057             */
39058             /** This region's collapsed element
39059             * @type Roo.Element */
39060             /*
39061              *
39062             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39063                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39064             ]}, true);
39065             
39066             if(c.floatable !== false){
39067                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39068                this.collapsedEl.on("click", this.collapseClick, this);
39069             }
39070
39071             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39072                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39073                    id: "message", unselectable: "on", style:{"float":"left"}});
39074                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39075              }
39076             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39077             this.expandBtn.on("click", this.expand, this);
39078             
39079         }
39080         
39081         if(this.collapseBtn){
39082             this.collapseBtn.setVisible(c.collapsible == true);
39083         }
39084         
39085         this.cmargins = c.cmargins || this.cmargins ||
39086                          (this.position == "west" || this.position == "east" ?
39087                              {top: 0, left: 2, right:2, bottom: 0} :
39088                              {top: 2, left: 0, right:0, bottom: 2});
39089         */
39090         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39091         
39092         
39093         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39094         
39095         this.autoScroll = c.autoScroll || false;
39096         
39097         
39098        
39099         
39100         this.duration = c.duration || .30;
39101         this.slideDuration = c.slideDuration || .45;
39102         this.config = c;
39103        
39104     },
39105     /**
39106      * Returns true if this region is currently visible.
39107      * @return {Boolean}
39108      */
39109     isVisible : function(){
39110         return this.visible;
39111     },
39112
39113     /**
39114      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39115      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39116      */
39117     //setCollapsedTitle : function(title){
39118     //    title = title || "&#160;";
39119      //   if(this.collapsedTitleTextEl){
39120       //      this.collapsedTitleTextEl.innerHTML = title;
39121        // }
39122     //},
39123
39124     getBox : function(){
39125         var b;
39126       //  if(!this.collapsed){
39127             b = this.el.getBox(false, true);
39128        // }else{
39129           //  b = this.collapsedEl.getBox(false, true);
39130         //}
39131         return b;
39132     },
39133
39134     getMargins : function(){
39135         return this.margins;
39136         //return this.collapsed ? this.cmargins : this.margins;
39137     },
39138 /*
39139     highlight : function(){
39140         this.el.addClass("x-layout-panel-dragover");
39141     },
39142
39143     unhighlight : function(){
39144         this.el.removeClass("x-layout-panel-dragover");
39145     },
39146 */
39147     updateBox : function(box)
39148     {
39149         if (!this.bodyEl) {
39150             return; // not rendered yet..
39151         }
39152         
39153         this.box = box;
39154         if(!this.collapsed){
39155             this.el.dom.style.left = box.x + "px";
39156             this.el.dom.style.top = box.y + "px";
39157             this.updateBody(box.width, box.height);
39158         }else{
39159             this.collapsedEl.dom.style.left = box.x + "px";
39160             this.collapsedEl.dom.style.top = box.y + "px";
39161             this.collapsedEl.setSize(box.width, box.height);
39162         }
39163         if(this.tabs){
39164             this.tabs.autoSizeTabs();
39165         }
39166     },
39167
39168     updateBody : function(w, h)
39169     {
39170         if(w !== null){
39171             this.el.setWidth(w);
39172             w -= this.el.getBorderWidth("rl");
39173             if(this.config.adjustments){
39174                 w += this.config.adjustments[0];
39175             }
39176         }
39177         if(h !== null && h > 0){
39178             this.el.setHeight(h);
39179             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39180             h -= this.el.getBorderWidth("tb");
39181             if(this.config.adjustments){
39182                 h += this.config.adjustments[1];
39183             }
39184             this.bodyEl.setHeight(h);
39185             if(this.tabs){
39186                 h = this.tabs.syncHeight(h);
39187             }
39188         }
39189         if(this.panelSize){
39190             w = w !== null ? w : this.panelSize.width;
39191             h = h !== null ? h : this.panelSize.height;
39192         }
39193         if(this.activePanel){
39194             var el = this.activePanel.getEl();
39195             w = w !== null ? w : el.getWidth();
39196             h = h !== null ? h : el.getHeight();
39197             this.panelSize = {width: w, height: h};
39198             this.activePanel.setSize(w, h);
39199         }
39200         if(Roo.isIE && this.tabs){
39201             this.tabs.el.repaint();
39202         }
39203     },
39204
39205     /**
39206      * Returns the container element for this region.
39207      * @return {Roo.Element}
39208      */
39209     getEl : function(){
39210         return this.el;
39211     },
39212
39213     /**
39214      * Hides this region.
39215      */
39216     hide : function(){
39217         //if(!this.collapsed){
39218             this.el.dom.style.left = "-2000px";
39219             this.el.hide();
39220         //}else{
39221          //   this.collapsedEl.dom.style.left = "-2000px";
39222          //   this.collapsedEl.hide();
39223        // }
39224         this.visible = false;
39225         this.fireEvent("visibilitychange", this, false);
39226     },
39227
39228     /**
39229      * Shows this region if it was previously hidden.
39230      */
39231     show : function(){
39232         //if(!this.collapsed){
39233             this.el.show();
39234         //}else{
39235         //    this.collapsedEl.show();
39236        // }
39237         this.visible = true;
39238         this.fireEvent("visibilitychange", this, true);
39239     },
39240 /*
39241     closeClicked : function(){
39242         if(this.activePanel){
39243             this.remove(this.activePanel);
39244         }
39245     },
39246
39247     collapseClick : function(e){
39248         if(this.isSlid){
39249            e.stopPropagation();
39250            this.slideIn();
39251         }else{
39252            e.stopPropagation();
39253            this.slideOut();
39254         }
39255     },
39256 */
39257     /**
39258      * Collapses this region.
39259      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39260      */
39261     /*
39262     collapse : function(skipAnim, skipCheck = false){
39263         if(this.collapsed) {
39264             return;
39265         }
39266         
39267         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39268             
39269             this.collapsed = true;
39270             if(this.split){
39271                 this.split.el.hide();
39272             }
39273             if(this.config.animate && skipAnim !== true){
39274                 this.fireEvent("invalidated", this);
39275                 this.animateCollapse();
39276             }else{
39277                 this.el.setLocation(-20000,-20000);
39278                 this.el.hide();
39279                 this.collapsedEl.show();
39280                 this.fireEvent("collapsed", this);
39281                 this.fireEvent("invalidated", this);
39282             }
39283         }
39284         
39285     },
39286 */
39287     animateCollapse : function(){
39288         // overridden
39289     },
39290
39291     /**
39292      * Expands this region if it was previously collapsed.
39293      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39294      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39295      */
39296     /*
39297     expand : function(e, skipAnim){
39298         if(e) {
39299             e.stopPropagation();
39300         }
39301         if(!this.collapsed || this.el.hasActiveFx()) {
39302             return;
39303         }
39304         if(this.isSlid){
39305             this.afterSlideIn();
39306             skipAnim = true;
39307         }
39308         this.collapsed = false;
39309         if(this.config.animate && skipAnim !== true){
39310             this.animateExpand();
39311         }else{
39312             this.el.show();
39313             if(this.split){
39314                 this.split.el.show();
39315             }
39316             this.collapsedEl.setLocation(-2000,-2000);
39317             this.collapsedEl.hide();
39318             this.fireEvent("invalidated", this);
39319             this.fireEvent("expanded", this);
39320         }
39321     },
39322 */
39323     animateExpand : function(){
39324         // overridden
39325     },
39326
39327     initTabs : function()
39328     {
39329         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39330         
39331         var ts = new Roo.bootstrap.panel.Tabs({
39332             el: this.bodyEl.dom,
39333             region : this,
39334             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39335             disableTooltips: this.config.disableTabTips,
39336             toolbar : this.config.toolbar
39337         });
39338         
39339         if(this.config.hideTabs){
39340             ts.stripWrap.setDisplayed(false);
39341         }
39342         this.tabs = ts;
39343         ts.resizeTabs = this.config.resizeTabs === true;
39344         ts.minTabWidth = this.config.minTabWidth || 40;
39345         ts.maxTabWidth = this.config.maxTabWidth || 250;
39346         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39347         ts.monitorResize = false;
39348         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39349         ts.bodyEl.addClass('roo-layout-tabs-body');
39350         this.panels.each(this.initPanelAsTab, this);
39351     },
39352
39353     initPanelAsTab : function(panel){
39354         var ti = this.tabs.addTab(
39355             panel.getEl().id,
39356             panel.getTitle(),
39357             null,
39358             this.config.closeOnTab && panel.isClosable(),
39359             panel.tpl
39360         );
39361         if(panel.tabTip !== undefined){
39362             ti.setTooltip(panel.tabTip);
39363         }
39364         ti.on("activate", function(){
39365               this.setActivePanel(panel);
39366         }, this);
39367         
39368         if(this.config.closeOnTab){
39369             ti.on("beforeclose", function(t, e){
39370                 e.cancel = true;
39371                 this.remove(panel);
39372             }, this);
39373         }
39374         
39375         panel.tabItem = ti;
39376         
39377         return ti;
39378     },
39379
39380     updatePanelTitle : function(panel, title)
39381     {
39382         if(this.activePanel == panel){
39383             this.updateTitle(title);
39384         }
39385         if(this.tabs){
39386             var ti = this.tabs.getTab(panel.getEl().id);
39387             ti.setText(title);
39388             if(panel.tabTip !== undefined){
39389                 ti.setTooltip(panel.tabTip);
39390             }
39391         }
39392     },
39393
39394     updateTitle : function(title){
39395         if(this.titleTextEl && !this.config.title){
39396             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39397         }
39398     },
39399
39400     setActivePanel : function(panel)
39401     {
39402         panel = this.getPanel(panel);
39403         if(this.activePanel && this.activePanel != panel){
39404             if(this.activePanel.setActiveState(false) === false){
39405                 return;
39406             }
39407         }
39408         this.activePanel = panel;
39409         panel.setActiveState(true);
39410         if(this.panelSize){
39411             panel.setSize(this.panelSize.width, this.panelSize.height);
39412         }
39413         if(this.closeBtn){
39414             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39415         }
39416         this.updateTitle(panel.getTitle());
39417         if(this.tabs){
39418             this.fireEvent("invalidated", this);
39419         }
39420         this.fireEvent("panelactivated", this, panel);
39421     },
39422
39423     /**
39424      * Shows the specified panel.
39425      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39426      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39427      */
39428     showPanel : function(panel)
39429     {
39430         panel = this.getPanel(panel);
39431         if(panel){
39432             if(this.tabs){
39433                 var tab = this.tabs.getTab(panel.getEl().id);
39434                 if(tab.isHidden()){
39435                     this.tabs.unhideTab(tab.id);
39436                 }
39437                 tab.activate();
39438             }else{
39439                 this.setActivePanel(panel);
39440             }
39441         }
39442         return panel;
39443     },
39444
39445     /**
39446      * Get the active panel for this region.
39447      * @return {Roo.ContentPanel} The active panel or null
39448      */
39449     getActivePanel : function(){
39450         return this.activePanel;
39451     },
39452
39453     validateVisibility : function(){
39454         if(this.panels.getCount() < 1){
39455             this.updateTitle("&#160;");
39456             this.closeBtn.hide();
39457             this.hide();
39458         }else{
39459             if(!this.isVisible()){
39460                 this.show();
39461             }
39462         }
39463     },
39464
39465     /**
39466      * Adds the passed ContentPanel(s) to this region.
39467      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39468      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39469      */
39470     add : function(panel)
39471     {
39472         if(arguments.length > 1){
39473             for(var i = 0, len = arguments.length; i < len; i++) {
39474                 this.add(arguments[i]);
39475             }
39476             return null;
39477         }
39478         
39479         // if we have not been rendered yet, then we can not really do much of this..
39480         if (!this.bodyEl) {
39481             this.unrendered_panels.push(panel);
39482             return panel;
39483         }
39484         
39485         
39486         
39487         
39488         if(this.hasPanel(panel)){
39489             this.showPanel(panel);
39490             return panel;
39491         }
39492         panel.setRegion(this);
39493         this.panels.add(panel);
39494        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39495             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39496             // and hide them... ???
39497             this.bodyEl.dom.appendChild(panel.getEl().dom);
39498             if(panel.background !== true){
39499                 this.setActivePanel(panel);
39500             }
39501             this.fireEvent("paneladded", this, panel);
39502             return panel;
39503         }
39504         */
39505         if(!this.tabs){
39506             this.initTabs();
39507         }else{
39508             this.initPanelAsTab(panel);
39509         }
39510         
39511         
39512         if(panel.background !== true){
39513             this.tabs.activate(panel.getEl().id);
39514         }
39515         this.fireEvent("paneladded", this, panel);
39516         return panel;
39517     },
39518
39519     /**
39520      * Hides the tab for the specified panel.
39521      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39522      */
39523     hidePanel : function(panel){
39524         if(this.tabs && (panel = this.getPanel(panel))){
39525             this.tabs.hideTab(panel.getEl().id);
39526         }
39527     },
39528
39529     /**
39530      * Unhides the tab for a previously hidden panel.
39531      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39532      */
39533     unhidePanel : function(panel){
39534         if(this.tabs && (panel = this.getPanel(panel))){
39535             this.tabs.unhideTab(panel.getEl().id);
39536         }
39537     },
39538
39539     clearPanels : function(){
39540         while(this.panels.getCount() > 0){
39541              this.remove(this.panels.first());
39542         }
39543     },
39544
39545     /**
39546      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39547      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39548      * @param {Boolean} preservePanel Overrides the config preservePanel option
39549      * @return {Roo.ContentPanel} The panel that was removed
39550      */
39551     remove : function(panel, preservePanel)
39552     {
39553         panel = this.getPanel(panel);
39554         if(!panel){
39555             return null;
39556         }
39557         var e = {};
39558         this.fireEvent("beforeremove", this, panel, e);
39559         if(e.cancel === true){
39560             return null;
39561         }
39562         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39563         var panelId = panel.getId();
39564         this.panels.removeKey(panelId);
39565         if(preservePanel){
39566             document.body.appendChild(panel.getEl().dom);
39567         }
39568         if(this.tabs){
39569             this.tabs.removeTab(panel.getEl().id);
39570         }else if (!preservePanel){
39571             this.bodyEl.dom.removeChild(panel.getEl().dom);
39572         }
39573         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39574             var p = this.panels.first();
39575             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39576             tempEl.appendChild(p.getEl().dom);
39577             this.bodyEl.update("");
39578             this.bodyEl.dom.appendChild(p.getEl().dom);
39579             tempEl = null;
39580             this.updateTitle(p.getTitle());
39581             this.tabs = null;
39582             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39583             this.setActivePanel(p);
39584         }
39585         panel.setRegion(null);
39586         if(this.activePanel == panel){
39587             this.activePanel = null;
39588         }
39589         if(this.config.autoDestroy !== false && preservePanel !== true){
39590             try{panel.destroy();}catch(e){}
39591         }
39592         this.fireEvent("panelremoved", this, panel);
39593         return panel;
39594     },
39595
39596     /**
39597      * Returns the TabPanel component used by this region
39598      * @return {Roo.TabPanel}
39599      */
39600     getTabs : function(){
39601         return this.tabs;
39602     },
39603
39604     createTool : function(parentEl, className){
39605         var btn = Roo.DomHelper.append(parentEl, {
39606             tag: "div",
39607             cls: "x-layout-tools-button",
39608             children: [ {
39609                 tag: "div",
39610                 cls: "roo-layout-tools-button-inner " + className,
39611                 html: "&#160;"
39612             }]
39613         }, true);
39614         btn.addClassOnOver("roo-layout-tools-button-over");
39615         return btn;
39616     }
39617 });/*
39618  * Based on:
39619  * Ext JS Library 1.1.1
39620  * Copyright(c) 2006-2007, Ext JS, LLC.
39621  *
39622  * Originally Released Under LGPL - original licence link has changed is not relivant.
39623  *
39624  * Fork - LGPL
39625  * <script type="text/javascript">
39626  */
39627  
39628
39629
39630 /**
39631  * @class Roo.SplitLayoutRegion
39632  * @extends Roo.LayoutRegion
39633  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39634  */
39635 Roo.bootstrap.layout.Split = function(config){
39636     this.cursor = config.cursor;
39637     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39638 };
39639
39640 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39641 {
39642     splitTip : "Drag to resize.",
39643     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39644     useSplitTips : false,
39645
39646     applyConfig : function(config){
39647         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39648     },
39649     
39650     onRender : function(ctr,pos) {
39651         
39652         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39653         if(!this.config.split){
39654             return;
39655         }
39656         if(!this.split){
39657             
39658             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39659                             tag: "div",
39660                             id: this.el.id + "-split",
39661                             cls: "roo-layout-split roo-layout-split-"+this.position,
39662                             html: "&#160;"
39663             });
39664             /** The SplitBar for this region 
39665             * @type Roo.SplitBar */
39666             // does not exist yet...
39667             Roo.log([this.position, this.orientation]);
39668             
39669             this.split = new Roo.bootstrap.SplitBar({
39670                 dragElement : splitEl,
39671                 resizingElement: this.el,
39672                 orientation : this.orientation
39673             });
39674             
39675             this.split.on("moved", this.onSplitMove, this);
39676             this.split.useShim = this.config.useShim === true;
39677             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39678             if(this.useSplitTips){
39679                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39680             }
39681             //if(config.collapsible){
39682             //    this.split.el.on("dblclick", this.collapse,  this);
39683             //}
39684         }
39685         if(typeof this.config.minSize != "undefined"){
39686             this.split.minSize = this.config.minSize;
39687         }
39688         if(typeof this.config.maxSize != "undefined"){
39689             this.split.maxSize = this.config.maxSize;
39690         }
39691         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39692             this.hideSplitter();
39693         }
39694         
39695     },
39696
39697     getHMaxSize : function(){
39698          var cmax = this.config.maxSize || 10000;
39699          var center = this.mgr.getRegion("center");
39700          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39701     },
39702
39703     getVMaxSize : function(){
39704          var cmax = this.config.maxSize || 10000;
39705          var center = this.mgr.getRegion("center");
39706          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39707     },
39708
39709     onSplitMove : function(split, newSize){
39710         this.fireEvent("resized", this, newSize);
39711     },
39712     
39713     /** 
39714      * Returns the {@link Roo.SplitBar} for this region.
39715      * @return {Roo.SplitBar}
39716      */
39717     getSplitBar : function(){
39718         return this.split;
39719     },
39720     
39721     hide : function(){
39722         this.hideSplitter();
39723         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39724     },
39725
39726     hideSplitter : function(){
39727         if(this.split){
39728             this.split.el.setLocation(-2000,-2000);
39729             this.split.el.hide();
39730         }
39731     },
39732
39733     show : function(){
39734         if(this.split){
39735             this.split.el.show();
39736         }
39737         Roo.bootstrap.layout.Split.superclass.show.call(this);
39738     },
39739     
39740     beforeSlide: function(){
39741         if(Roo.isGecko){// firefox overflow auto bug workaround
39742             this.bodyEl.clip();
39743             if(this.tabs) {
39744                 this.tabs.bodyEl.clip();
39745             }
39746             if(this.activePanel){
39747                 this.activePanel.getEl().clip();
39748                 
39749                 if(this.activePanel.beforeSlide){
39750                     this.activePanel.beforeSlide();
39751                 }
39752             }
39753         }
39754     },
39755     
39756     afterSlide : function(){
39757         if(Roo.isGecko){// firefox overflow auto bug workaround
39758             this.bodyEl.unclip();
39759             if(this.tabs) {
39760                 this.tabs.bodyEl.unclip();
39761             }
39762             if(this.activePanel){
39763                 this.activePanel.getEl().unclip();
39764                 if(this.activePanel.afterSlide){
39765                     this.activePanel.afterSlide();
39766                 }
39767             }
39768         }
39769     },
39770
39771     initAutoHide : function(){
39772         if(this.autoHide !== false){
39773             if(!this.autoHideHd){
39774                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39775                 this.autoHideHd = {
39776                     "mouseout": function(e){
39777                         if(!e.within(this.el, true)){
39778                             st.delay(500);
39779                         }
39780                     },
39781                     "mouseover" : function(e){
39782                         st.cancel();
39783                     },
39784                     scope : this
39785                 };
39786             }
39787             this.el.on(this.autoHideHd);
39788         }
39789     },
39790
39791     clearAutoHide : function(){
39792         if(this.autoHide !== false){
39793             this.el.un("mouseout", this.autoHideHd.mouseout);
39794             this.el.un("mouseover", this.autoHideHd.mouseover);
39795         }
39796     },
39797
39798     clearMonitor : function(){
39799         Roo.get(document).un("click", this.slideInIf, this);
39800     },
39801
39802     // these names are backwards but not changed for compat
39803     slideOut : function(){
39804         if(this.isSlid || this.el.hasActiveFx()){
39805             return;
39806         }
39807         this.isSlid = true;
39808         if(this.collapseBtn){
39809             this.collapseBtn.hide();
39810         }
39811         this.closeBtnState = this.closeBtn.getStyle('display');
39812         this.closeBtn.hide();
39813         if(this.stickBtn){
39814             this.stickBtn.show();
39815         }
39816         this.el.show();
39817         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39818         this.beforeSlide();
39819         this.el.setStyle("z-index", 10001);
39820         this.el.slideIn(this.getSlideAnchor(), {
39821             callback: function(){
39822                 this.afterSlide();
39823                 this.initAutoHide();
39824                 Roo.get(document).on("click", this.slideInIf, this);
39825                 this.fireEvent("slideshow", this);
39826             },
39827             scope: this,
39828             block: true
39829         });
39830     },
39831
39832     afterSlideIn : function(){
39833         this.clearAutoHide();
39834         this.isSlid = false;
39835         this.clearMonitor();
39836         this.el.setStyle("z-index", "");
39837         if(this.collapseBtn){
39838             this.collapseBtn.show();
39839         }
39840         this.closeBtn.setStyle('display', this.closeBtnState);
39841         if(this.stickBtn){
39842             this.stickBtn.hide();
39843         }
39844         this.fireEvent("slidehide", this);
39845     },
39846
39847     slideIn : function(cb){
39848         if(!this.isSlid || this.el.hasActiveFx()){
39849             Roo.callback(cb);
39850             return;
39851         }
39852         this.isSlid = false;
39853         this.beforeSlide();
39854         this.el.slideOut(this.getSlideAnchor(), {
39855             callback: function(){
39856                 this.el.setLeftTop(-10000, -10000);
39857                 this.afterSlide();
39858                 this.afterSlideIn();
39859                 Roo.callback(cb);
39860             },
39861             scope: this,
39862             block: true
39863         });
39864     },
39865     
39866     slideInIf : function(e){
39867         if(!e.within(this.el)){
39868             this.slideIn();
39869         }
39870     },
39871
39872     animateCollapse : function(){
39873         this.beforeSlide();
39874         this.el.setStyle("z-index", 20000);
39875         var anchor = this.getSlideAnchor();
39876         this.el.slideOut(anchor, {
39877             callback : function(){
39878                 this.el.setStyle("z-index", "");
39879                 this.collapsedEl.slideIn(anchor, {duration:.3});
39880                 this.afterSlide();
39881                 this.el.setLocation(-10000,-10000);
39882                 this.el.hide();
39883                 this.fireEvent("collapsed", this);
39884             },
39885             scope: this,
39886             block: true
39887         });
39888     },
39889
39890     animateExpand : function(){
39891         this.beforeSlide();
39892         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39893         this.el.setStyle("z-index", 20000);
39894         this.collapsedEl.hide({
39895             duration:.1
39896         });
39897         this.el.slideIn(this.getSlideAnchor(), {
39898             callback : function(){
39899                 this.el.setStyle("z-index", "");
39900                 this.afterSlide();
39901                 if(this.split){
39902                     this.split.el.show();
39903                 }
39904                 this.fireEvent("invalidated", this);
39905                 this.fireEvent("expanded", this);
39906             },
39907             scope: this,
39908             block: true
39909         });
39910     },
39911
39912     anchors : {
39913         "west" : "left",
39914         "east" : "right",
39915         "north" : "top",
39916         "south" : "bottom"
39917     },
39918
39919     sanchors : {
39920         "west" : "l",
39921         "east" : "r",
39922         "north" : "t",
39923         "south" : "b"
39924     },
39925
39926     canchors : {
39927         "west" : "tl-tr",
39928         "east" : "tr-tl",
39929         "north" : "tl-bl",
39930         "south" : "bl-tl"
39931     },
39932
39933     getAnchor : function(){
39934         return this.anchors[this.position];
39935     },
39936
39937     getCollapseAnchor : function(){
39938         return this.canchors[this.position];
39939     },
39940
39941     getSlideAnchor : function(){
39942         return this.sanchors[this.position];
39943     },
39944
39945     getAlignAdj : function(){
39946         var cm = this.cmargins;
39947         switch(this.position){
39948             case "west":
39949                 return [0, 0];
39950             break;
39951             case "east":
39952                 return [0, 0];
39953             break;
39954             case "north":
39955                 return [0, 0];
39956             break;
39957             case "south":
39958                 return [0, 0];
39959             break;
39960         }
39961     },
39962
39963     getExpandAdj : function(){
39964         var c = this.collapsedEl, cm = this.cmargins;
39965         switch(this.position){
39966             case "west":
39967                 return [-(cm.right+c.getWidth()+cm.left), 0];
39968             break;
39969             case "east":
39970                 return [cm.right+c.getWidth()+cm.left, 0];
39971             break;
39972             case "north":
39973                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39974             break;
39975             case "south":
39976                 return [0, cm.top+cm.bottom+c.getHeight()];
39977             break;
39978         }
39979     }
39980 });/*
39981  * Based on:
39982  * Ext JS Library 1.1.1
39983  * Copyright(c) 2006-2007, Ext JS, LLC.
39984  *
39985  * Originally Released Under LGPL - original licence link has changed is not relivant.
39986  *
39987  * Fork - LGPL
39988  * <script type="text/javascript">
39989  */
39990 /*
39991  * These classes are private internal classes
39992  */
39993 Roo.bootstrap.layout.Center = function(config){
39994     config.region = "center";
39995     Roo.bootstrap.layout.Region.call(this, config);
39996     this.visible = true;
39997     this.minWidth = config.minWidth || 20;
39998     this.minHeight = config.minHeight || 20;
39999 };
40000
40001 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40002     hide : function(){
40003         // center panel can't be hidden
40004     },
40005     
40006     show : function(){
40007         // center panel can't be hidden
40008     },
40009     
40010     getMinWidth: function(){
40011         return this.minWidth;
40012     },
40013     
40014     getMinHeight: function(){
40015         return this.minHeight;
40016     }
40017 });
40018
40019
40020
40021
40022  
40023
40024
40025
40026
40027
40028
40029 Roo.bootstrap.layout.North = function(config)
40030 {
40031     config.region = 'north';
40032     config.cursor = 'n-resize';
40033     
40034     Roo.bootstrap.layout.Split.call(this, config);
40035     
40036     
40037     if(this.split){
40038         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40039         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40040         this.split.el.addClass("roo-layout-split-v");
40041     }
40042     //var size = config.initialSize || config.height;
40043     //if(this.el && typeof size != "undefined"){
40044     //    this.el.setHeight(size);
40045     //}
40046 };
40047 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40048 {
40049     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40050      
40051      
40052     onRender : function(ctr, pos)
40053     {
40054         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40055         var size = this.config.initialSize || this.config.height;
40056         if(this.el && typeof size != "undefined"){
40057             this.el.setHeight(size);
40058         }
40059     
40060     },
40061     
40062     getBox : function(){
40063         if(this.collapsed){
40064             return this.collapsedEl.getBox();
40065         }
40066         var box = this.el.getBox();
40067         if(this.split){
40068             box.height += this.split.el.getHeight();
40069         }
40070         return box;
40071     },
40072     
40073     updateBox : function(box){
40074         if(this.split && !this.collapsed){
40075             box.height -= this.split.el.getHeight();
40076             this.split.el.setLeft(box.x);
40077             this.split.el.setTop(box.y+box.height);
40078             this.split.el.setWidth(box.width);
40079         }
40080         if(this.collapsed){
40081             this.updateBody(box.width, null);
40082         }
40083         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40084     }
40085 });
40086
40087
40088
40089
40090
40091 Roo.bootstrap.layout.South = function(config){
40092     config.region = 'south';
40093     config.cursor = 's-resize';
40094     Roo.bootstrap.layout.Split.call(this, config);
40095     if(this.split){
40096         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40097         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40098         this.split.el.addClass("roo-layout-split-v");
40099     }
40100     
40101 };
40102
40103 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40104     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40105     
40106     onRender : function(ctr, pos)
40107     {
40108         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40109         var size = this.config.initialSize || this.config.height;
40110         if(this.el && typeof size != "undefined"){
40111             this.el.setHeight(size);
40112         }
40113     
40114     },
40115     
40116     getBox : function(){
40117         if(this.collapsed){
40118             return this.collapsedEl.getBox();
40119         }
40120         var box = this.el.getBox();
40121         if(this.split){
40122             var sh = this.split.el.getHeight();
40123             box.height += sh;
40124             box.y -= sh;
40125         }
40126         return box;
40127     },
40128     
40129     updateBox : function(box){
40130         if(this.split && !this.collapsed){
40131             var sh = this.split.el.getHeight();
40132             box.height -= sh;
40133             box.y += sh;
40134             this.split.el.setLeft(box.x);
40135             this.split.el.setTop(box.y-sh);
40136             this.split.el.setWidth(box.width);
40137         }
40138         if(this.collapsed){
40139             this.updateBody(box.width, null);
40140         }
40141         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40142     }
40143 });
40144
40145 Roo.bootstrap.layout.East = function(config){
40146     config.region = "east";
40147     config.cursor = "e-resize";
40148     Roo.bootstrap.layout.Split.call(this, config);
40149     if(this.split){
40150         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40151         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40152         this.split.el.addClass("roo-layout-split-h");
40153     }
40154     
40155 };
40156 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40157     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40158     
40159     onRender : function(ctr, pos)
40160     {
40161         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40162         var size = this.config.initialSize || this.config.width;
40163         if(this.el && typeof size != "undefined"){
40164             this.el.setWidth(size);
40165         }
40166     
40167     },
40168     
40169     getBox : function(){
40170         if(this.collapsed){
40171             return this.collapsedEl.getBox();
40172         }
40173         var box = this.el.getBox();
40174         if(this.split){
40175             var sw = this.split.el.getWidth();
40176             box.width += sw;
40177             box.x -= sw;
40178         }
40179         return box;
40180     },
40181
40182     updateBox : function(box){
40183         if(this.split && !this.collapsed){
40184             var sw = this.split.el.getWidth();
40185             box.width -= sw;
40186             this.split.el.setLeft(box.x);
40187             this.split.el.setTop(box.y);
40188             this.split.el.setHeight(box.height);
40189             box.x += sw;
40190         }
40191         if(this.collapsed){
40192             this.updateBody(null, box.height);
40193         }
40194         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40195     }
40196 });
40197
40198 Roo.bootstrap.layout.West = function(config){
40199     config.region = "west";
40200     config.cursor = "w-resize";
40201     
40202     Roo.bootstrap.layout.Split.call(this, config);
40203     if(this.split){
40204         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40205         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40206         this.split.el.addClass("roo-layout-split-h");
40207     }
40208     
40209 };
40210 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40211     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40212     
40213     onRender: function(ctr, pos)
40214     {
40215         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40216         var size = this.config.initialSize || this.config.width;
40217         if(typeof size != "undefined"){
40218             this.el.setWidth(size);
40219         }
40220     },
40221     
40222     getBox : function(){
40223         if(this.collapsed){
40224             return this.collapsedEl.getBox();
40225         }
40226         var box = this.el.getBox();
40227         if (box.width == 0) {
40228             box.width = this.config.width; // kludge?
40229         }
40230         if(this.split){
40231             box.width += this.split.el.getWidth();
40232         }
40233         return box;
40234     },
40235     
40236     updateBox : function(box){
40237         if(this.split && !this.collapsed){
40238             var sw = this.split.el.getWidth();
40239             box.width -= sw;
40240             this.split.el.setLeft(box.x+box.width);
40241             this.split.el.setTop(box.y);
40242             this.split.el.setHeight(box.height);
40243         }
40244         if(this.collapsed){
40245             this.updateBody(null, box.height);
40246         }
40247         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40248     }
40249 });/*
40250  * Based on:
40251  * Ext JS Library 1.1.1
40252  * Copyright(c) 2006-2007, Ext JS, LLC.
40253  *
40254  * Originally Released Under LGPL - original licence link has changed is not relivant.
40255  *
40256  * Fork - LGPL
40257  * <script type="text/javascript">
40258  */
40259 /**
40260  * @class Roo.bootstrap.paenl.Content
40261  * @extends Roo.util.Observable
40262  * @children Roo.bootstrap.Component
40263  * @parent builder Roo.bootstrap.layout.Border
40264  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40265  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40266  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40267  * @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
40268  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40269  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40270  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40271  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40272  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40273  * @cfg {String} title          The title for this panel
40274  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40275  * @cfg {String} url            Calls {@link #setUrl} with this value
40276  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40277  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40278  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40279  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40280  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40281  * @cfg {Boolean} badges render the badges
40282  * @cfg {String} cls  extra classes to use  
40283  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40284  
40285  * @constructor
40286  * Create a new ContentPanel.
40287  * @param {String/Object} config A string to set only the title or a config object
40288  
40289  */
40290 Roo.bootstrap.panel.Content = function( config){
40291     
40292     this.tpl = config.tpl || false;
40293     
40294     var el = config.el;
40295     var content = config.content;
40296
40297     if(config.autoCreate){ // xtype is available if this is called from factory
40298         el = Roo.id();
40299     }
40300     this.el = Roo.get(el);
40301     if(!this.el && config && config.autoCreate){
40302         if(typeof config.autoCreate == "object"){
40303             if(!config.autoCreate.id){
40304                 config.autoCreate.id = config.id||el;
40305             }
40306             this.el = Roo.DomHelper.append(document.body,
40307                         config.autoCreate, true);
40308         }else{
40309             var elcfg =  {
40310                 tag: "div",
40311                 cls: (config.cls || '') +
40312                     (config.background ? ' bg-' + config.background : '') +
40313                     " roo-layout-inactive-content",
40314                 id: config.id||el
40315             };
40316             if (config.iframe) {
40317                 elcfg.cn = [
40318                     {
40319                         tag : 'iframe',
40320                         style : 'border: 0px',
40321                         src : 'about:blank'
40322                     }
40323                 ];
40324             }
40325               
40326             if (config.html) {
40327                 elcfg.html = config.html;
40328                 
40329             }
40330                         
40331             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40332             if (config.iframe) {
40333                 this.iframeEl = this.el.select('iframe',true).first();
40334             }
40335             
40336         }
40337     } 
40338     this.closable = false;
40339     this.loaded = false;
40340     this.active = false;
40341    
40342       
40343     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40344         
40345         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40346         
40347         this.wrapEl = this.el; //this.el.wrap();
40348         var ti = [];
40349         if (config.toolbar.items) {
40350             ti = config.toolbar.items ;
40351             delete config.toolbar.items ;
40352         }
40353         
40354         var nitems = [];
40355         this.toolbar.render(this.wrapEl, 'before');
40356         for(var i =0;i < ti.length;i++) {
40357           //  Roo.log(['add child', items[i]]);
40358             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40359         }
40360         this.toolbar.items = nitems;
40361         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40362         delete config.toolbar;
40363         
40364     }
40365     /*
40366     // xtype created footer. - not sure if will work as we normally have to render first..
40367     if (this.footer && !this.footer.el && this.footer.xtype) {
40368         if (!this.wrapEl) {
40369             this.wrapEl = this.el.wrap();
40370         }
40371     
40372         this.footer.container = this.wrapEl.createChild();
40373          
40374         this.footer = Roo.factory(this.footer, Roo);
40375         
40376     }
40377     */
40378     
40379      if(typeof config == "string"){
40380         this.title = config;
40381     }else{
40382         Roo.apply(this, config);
40383     }
40384     
40385     if(this.resizeEl){
40386         this.resizeEl = Roo.get(this.resizeEl, true);
40387     }else{
40388         this.resizeEl = this.el;
40389     }
40390     // handle view.xtype
40391     
40392  
40393     
40394     
40395     this.addEvents({
40396         /**
40397          * @event activate
40398          * Fires when this panel is activated. 
40399          * @param {Roo.ContentPanel} this
40400          */
40401         "activate" : true,
40402         /**
40403          * @event deactivate
40404          * Fires when this panel is activated. 
40405          * @param {Roo.ContentPanel} this
40406          */
40407         "deactivate" : true,
40408
40409         /**
40410          * @event resize
40411          * Fires when this panel is resized if fitToFrame is true.
40412          * @param {Roo.ContentPanel} this
40413          * @param {Number} width The width after any component adjustments
40414          * @param {Number} height The height after any component adjustments
40415          */
40416         "resize" : true,
40417         
40418          /**
40419          * @event render
40420          * Fires when this tab is created
40421          * @param {Roo.ContentPanel} this
40422          */
40423         "render" : true,
40424         
40425           /**
40426          * @event scroll
40427          * Fires when this content is scrolled
40428          * @param {Roo.ContentPanel} this
40429          * @param {Event} scrollEvent
40430          */
40431         "scroll" : true
40432         
40433         
40434         
40435     });
40436     
40437
40438     
40439     
40440     if(this.autoScroll && !this.iframe){
40441         this.resizeEl.setStyle("overflow", "auto");
40442         this.resizeEl.on('scroll', this.onScroll, this);
40443     } else {
40444         // fix randome scrolling
40445         //this.el.on('scroll', function() {
40446         //    Roo.log('fix random scolling');
40447         //    this.scrollTo('top',0); 
40448         //});
40449     }
40450     content = content || this.content;
40451     if(content){
40452         this.setContent(content);
40453     }
40454     if(config && config.url){
40455         this.setUrl(this.url, this.params, this.loadOnce);
40456     }
40457     
40458     
40459     
40460     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40461     
40462     if (this.view && typeof(this.view.xtype) != 'undefined') {
40463         this.view.el = this.el.appendChild(document.createElement("div"));
40464         this.view = Roo.factory(this.view); 
40465         this.view.render  &&  this.view.render(false, '');  
40466     }
40467     
40468     
40469     this.fireEvent('render', this);
40470 };
40471
40472 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40473     
40474     cls : '',
40475     background : '',
40476     
40477     tabTip : '',
40478     
40479     iframe : false,
40480     iframeEl : false,
40481     
40482     /* Resize Element - use this to work out scroll etc. */
40483     resizeEl : false,
40484     
40485     setRegion : function(region){
40486         this.region = region;
40487         this.setActiveClass(region && !this.background);
40488     },
40489     
40490     
40491     setActiveClass: function(state)
40492     {
40493         if(state){
40494            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40495            this.el.setStyle('position','relative');
40496         }else{
40497            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40498            this.el.setStyle('position', 'absolute');
40499         } 
40500     },
40501     
40502     /**
40503      * Returns the toolbar for this Panel if one was configured. 
40504      * @return {Roo.Toolbar} 
40505      */
40506     getToolbar : function(){
40507         return this.toolbar;
40508     },
40509     
40510     setActiveState : function(active)
40511     {
40512         this.active = active;
40513         this.setActiveClass(active);
40514         if(!active){
40515             if(this.fireEvent("deactivate", this) === false){
40516                 return false;
40517             }
40518             return true;
40519         }
40520         this.fireEvent("activate", this);
40521         return true;
40522     },
40523     /**
40524      * Updates this panel's element (not for iframe)
40525      * @param {String} content The new content
40526      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40527     */
40528     setContent : function(content, loadScripts){
40529         if (this.iframe) {
40530             return;
40531         }
40532         
40533         this.el.update(content, loadScripts);
40534     },
40535
40536     ignoreResize : function(w, h)
40537     {
40538         //return false; // always resize?
40539         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40540             return true;
40541         }else{
40542             this.lastSize = {width: w, height: h};
40543             return false;
40544         }
40545     },
40546     /**
40547      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40548      * @return {Roo.UpdateManager} The UpdateManager
40549      */
40550     getUpdateManager : function(){
40551         if (this.iframe) {
40552             return false;
40553         }
40554         return this.el.getUpdateManager();
40555     },
40556      /**
40557      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40558      * Does not work with IFRAME contents
40559      * @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:
40560 <pre><code>
40561 panel.load({
40562     url: "your-url.php",
40563     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40564     callback: yourFunction,
40565     scope: yourObject, //(optional scope)
40566     discardUrl: false,
40567     nocache: false,
40568     text: "Loading...",
40569     timeout: 30,
40570     scripts: false
40571 });
40572 </code></pre>
40573      
40574      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40575      * 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.
40576      * @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}
40577      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40578      * @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.
40579      * @return {Roo.ContentPanel} this
40580      */
40581     load : function(){
40582         
40583         if (this.iframe) {
40584             return this;
40585         }
40586         
40587         var um = this.el.getUpdateManager();
40588         um.update.apply(um, arguments);
40589         return this;
40590     },
40591
40592
40593     /**
40594      * 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.
40595      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40596      * @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)
40597      * @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)
40598      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40599      */
40600     setUrl : function(url, params, loadOnce){
40601         if (this.iframe) {
40602             this.iframeEl.dom.src = url;
40603             return false;
40604         }
40605         
40606         if(this.refreshDelegate){
40607             this.removeListener("activate", this.refreshDelegate);
40608         }
40609         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40610         this.on("activate", this.refreshDelegate);
40611         return this.el.getUpdateManager();
40612     },
40613     
40614     _handleRefresh : function(url, params, loadOnce){
40615         if(!loadOnce || !this.loaded){
40616             var updater = this.el.getUpdateManager();
40617             updater.update(url, params, this._setLoaded.createDelegate(this));
40618         }
40619     },
40620     
40621     _setLoaded : function(){
40622         this.loaded = true;
40623     }, 
40624     
40625     /**
40626      * Returns this panel's id
40627      * @return {String} 
40628      */
40629     getId : function(){
40630         return this.el.id;
40631     },
40632     
40633     /** 
40634      * Returns this panel's element - used by regiosn to add.
40635      * @return {Roo.Element} 
40636      */
40637     getEl : function(){
40638         return this.wrapEl || this.el;
40639     },
40640     
40641    
40642     
40643     adjustForComponents : function(width, height)
40644     {
40645         //Roo.log('adjustForComponents ');
40646         if(this.resizeEl != this.el){
40647             width -= this.el.getFrameWidth('lr');
40648             height -= this.el.getFrameWidth('tb');
40649         }
40650         if(this.toolbar){
40651             var te = this.toolbar.getEl();
40652             te.setWidth(width);
40653             height -= te.getHeight();
40654         }
40655         if(this.footer){
40656             var te = this.footer.getEl();
40657             te.setWidth(width);
40658             height -= te.getHeight();
40659         }
40660         
40661         
40662         if(this.adjustments){
40663             width += this.adjustments[0];
40664             height += this.adjustments[1];
40665         }
40666         return {"width": width, "height": height};
40667     },
40668     
40669     setSize : function(width, height){
40670         if(this.fitToFrame && !this.ignoreResize(width, height)){
40671             if(this.fitContainer && this.resizeEl != this.el){
40672                 this.el.setSize(width, height);
40673             }
40674             var size = this.adjustForComponents(width, height);
40675             if (this.iframe) {
40676                 this.iframeEl.setSize(width,height);
40677             }
40678             
40679             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40680             this.fireEvent('resize', this, size.width, size.height);
40681             
40682             
40683         }
40684     },
40685     
40686     /**
40687      * Returns this panel's title
40688      * @return {String} 
40689      */
40690     getTitle : function(){
40691         
40692         if (typeof(this.title) != 'object') {
40693             return this.title;
40694         }
40695         
40696         var t = '';
40697         for (var k in this.title) {
40698             if (!this.title.hasOwnProperty(k)) {
40699                 continue;
40700             }
40701             
40702             if (k.indexOf('-') >= 0) {
40703                 var s = k.split('-');
40704                 for (var i = 0; i<s.length; i++) {
40705                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40706                 }
40707             } else {
40708                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40709             }
40710         }
40711         return t;
40712     },
40713     
40714     /**
40715      * Set this panel's title
40716      * @param {String} title
40717      */
40718     setTitle : function(title){
40719         this.title = title;
40720         if(this.region){
40721             this.region.updatePanelTitle(this, title);
40722         }
40723     },
40724     
40725     /**
40726      * Returns true is this panel was configured to be closable
40727      * @return {Boolean} 
40728      */
40729     isClosable : function(){
40730         return this.closable;
40731     },
40732     
40733     beforeSlide : function(){
40734         this.el.clip();
40735         this.resizeEl.clip();
40736     },
40737     
40738     afterSlide : function(){
40739         this.el.unclip();
40740         this.resizeEl.unclip();
40741     },
40742     
40743     /**
40744      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40745      *   Will fail silently if the {@link #setUrl} method has not been called.
40746      *   This does not activate the panel, just updates its content.
40747      */
40748     refresh : function(){
40749         if(this.refreshDelegate){
40750            this.loaded = false;
40751            this.refreshDelegate();
40752         }
40753     },
40754     
40755     /**
40756      * Destroys this panel
40757      */
40758     destroy : function(){
40759         this.el.removeAllListeners();
40760         var tempEl = document.createElement("span");
40761         tempEl.appendChild(this.el.dom);
40762         tempEl.innerHTML = "";
40763         this.el.remove();
40764         this.el = null;
40765     },
40766     
40767     /**
40768      * form - if the content panel contains a form - this is a reference to it.
40769      * @type {Roo.form.Form}
40770      */
40771     form : false,
40772     /**
40773      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40774      *    This contains a reference to it.
40775      * @type {Roo.View}
40776      */
40777     view : false,
40778     
40779       /**
40780      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40781      * <pre><code>
40782
40783 layout.addxtype({
40784        xtype : 'Form',
40785        items: [ .... ]
40786    }
40787 );
40788
40789 </code></pre>
40790      * @param {Object} cfg Xtype definition of item to add.
40791      */
40792     
40793     
40794     getChildContainer: function () {
40795         return this.getEl();
40796     },
40797     
40798     
40799     onScroll : function(e)
40800     {
40801         this.fireEvent('scroll', this, e);
40802     }
40803     
40804     
40805     /*
40806         var  ret = new Roo.factory(cfg);
40807         return ret;
40808         
40809         
40810         // add form..
40811         if (cfg.xtype.match(/^Form$/)) {
40812             
40813             var el;
40814             //if (this.footer) {
40815             //    el = this.footer.container.insertSibling(false, 'before');
40816             //} else {
40817                 el = this.el.createChild();
40818             //}
40819
40820             this.form = new  Roo.form.Form(cfg);
40821             
40822             
40823             if ( this.form.allItems.length) {
40824                 this.form.render(el.dom);
40825             }
40826             return this.form;
40827         }
40828         // should only have one of theses..
40829         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40830             // views.. should not be just added - used named prop 'view''
40831             
40832             cfg.el = this.el.appendChild(document.createElement("div"));
40833             // factory?
40834             
40835             var ret = new Roo.factory(cfg);
40836              
40837              ret.render && ret.render(false, ''); // render blank..
40838             this.view = ret;
40839             return ret;
40840         }
40841         return false;
40842     }
40843     \*/
40844 });
40845  
40846 /**
40847  * @class Roo.bootstrap.panel.Grid
40848  * @extends Roo.bootstrap.panel.Content
40849  * @constructor
40850  * Create a new GridPanel.
40851  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40852  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40853  * @param {Object} config A the config object
40854   
40855  */
40856
40857
40858
40859 Roo.bootstrap.panel.Grid = function(config)
40860 {
40861     
40862       
40863     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40864         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40865
40866     config.el = this.wrapper;
40867     //this.el = this.wrapper;
40868     
40869       if (config.container) {
40870         // ctor'ed from a Border/panel.grid
40871         
40872         
40873         this.wrapper.setStyle("overflow", "hidden");
40874         this.wrapper.addClass('roo-grid-container');
40875
40876     }
40877     
40878     
40879     if(config.toolbar){
40880         var tool_el = this.wrapper.createChild();    
40881         this.toolbar = Roo.factory(config.toolbar);
40882         var ti = [];
40883         if (config.toolbar.items) {
40884             ti = config.toolbar.items ;
40885             delete config.toolbar.items ;
40886         }
40887         
40888         var nitems = [];
40889         this.toolbar.render(tool_el);
40890         for(var i =0;i < ti.length;i++) {
40891           //  Roo.log(['add child', items[i]]);
40892             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40893         }
40894         this.toolbar.items = nitems;
40895         
40896         delete config.toolbar;
40897     }
40898     
40899     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40900     config.grid.scrollBody = true;;
40901     config.grid.monitorWindowResize = false; // turn off autosizing
40902     config.grid.autoHeight = false;
40903     config.grid.autoWidth = false;
40904     
40905     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40906     
40907     if (config.background) {
40908         // render grid on panel activation (if panel background)
40909         this.on('activate', function(gp) {
40910             if (!gp.grid.rendered) {
40911                 gp.grid.render(this.wrapper);
40912                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40913             }
40914         });
40915             
40916     } else {
40917         this.grid.render(this.wrapper);
40918         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40919
40920     }
40921     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40922     // ??? needed ??? config.el = this.wrapper;
40923     
40924     
40925     
40926   
40927     // xtype created footer. - not sure if will work as we normally have to render first..
40928     if (this.footer && !this.footer.el && this.footer.xtype) {
40929         
40930         var ctr = this.grid.getView().getFooterPanel(true);
40931         this.footer.dataSource = this.grid.dataSource;
40932         this.footer = Roo.factory(this.footer, Roo);
40933         this.footer.render(ctr);
40934         
40935     }
40936     
40937     
40938     
40939     
40940      
40941 };
40942
40943 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40944 {
40945   
40946     getId : function(){
40947         return this.grid.id;
40948     },
40949     
40950     /**
40951      * Returns the grid for this panel
40952      * @return {Roo.bootstrap.Table} 
40953      */
40954     getGrid : function(){
40955         return this.grid;    
40956     },
40957     
40958     setSize : function(width, height)
40959     {
40960      
40961         //if(!this.ignoreResize(width, height)){
40962             var grid = this.grid;
40963             var size = this.adjustForComponents(width, height);
40964             // tfoot is not a footer?
40965           
40966             
40967             var gridel = grid.getGridEl();
40968             gridel.setSize(size.width, size.height);
40969             
40970             var tbd = grid.getGridEl().select('tbody', true).first();
40971             var thd = grid.getGridEl().select('thead',true).first();
40972             var tbf= grid.getGridEl().select('tfoot', true).first();
40973
40974             if (tbf) {
40975                 size.height -= tbf.getHeight();
40976             }
40977             if (thd) {
40978                 size.height -= thd.getHeight();
40979             }
40980             
40981             tbd.setSize(size.width, size.height );
40982             // this is for the account management tab -seems to work there.
40983             var thd = grid.getGridEl().select('thead',true).first();
40984             //if (tbd) {
40985             //    tbd.setSize(size.width, size.height - thd.getHeight());
40986             //}
40987              
40988             grid.autoSize();
40989         //}
40990    
40991     },
40992      
40993     
40994     
40995     beforeSlide : function(){
40996         this.grid.getView().scroller.clip();
40997     },
40998     
40999     afterSlide : function(){
41000         this.grid.getView().scroller.unclip();
41001     },
41002     
41003     destroy : function(){
41004         this.grid.destroy();
41005         delete this.grid;
41006         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41007     }
41008 });
41009
41010 /**
41011  * @class Roo.bootstrap.panel.Nest
41012  * @extends Roo.bootstrap.panel.Content
41013  * @constructor
41014  * Create a new Panel, that can contain a layout.Border.
41015  * 
41016  * 
41017  * @param {String/Object} config A string to set only the title or a config object
41018  */
41019 Roo.bootstrap.panel.Nest = function(config)
41020 {
41021     // construct with only one argument..
41022     /* FIXME - implement nicer consturctors
41023     if (layout.layout) {
41024         config = layout;
41025         layout = config.layout;
41026         delete config.layout;
41027     }
41028     if (layout.xtype && !layout.getEl) {
41029         // then layout needs constructing..
41030         layout = Roo.factory(layout, Roo);
41031     }
41032     */
41033     
41034     config.el =  config.layout.getEl();
41035     
41036     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41037     
41038     config.layout.monitorWindowResize = false; // turn off autosizing
41039     this.layout = config.layout;
41040     this.layout.getEl().addClass("roo-layout-nested-layout");
41041     this.layout.parent = this;
41042     
41043     
41044     
41045     
41046 };
41047
41048 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41049     /**
41050     * @cfg {Roo.BorderLayout} layout The layout for this panel
41051     */
41052     layout : false,
41053
41054     setSize : function(width, height){
41055         if(!this.ignoreResize(width, height)){
41056             var size = this.adjustForComponents(width, height);
41057             var el = this.layout.getEl();
41058             if (size.height < 1) {
41059                 el.setWidth(size.width);   
41060             } else {
41061                 el.setSize(size.width, size.height);
41062             }
41063             var touch = el.dom.offsetWidth;
41064             this.layout.layout();
41065             // ie requires a double layout on the first pass
41066             if(Roo.isIE && !this.initialized){
41067                 this.initialized = true;
41068                 this.layout.layout();
41069             }
41070         }
41071     },
41072     
41073     // activate all subpanels if not currently active..
41074     
41075     setActiveState : function(active){
41076         this.active = active;
41077         this.setActiveClass(active);
41078         
41079         if(!active){
41080             this.fireEvent("deactivate", this);
41081             return;
41082         }
41083         
41084         this.fireEvent("activate", this);
41085         // not sure if this should happen before or after..
41086         if (!this.layout) {
41087             return; // should not happen..
41088         }
41089         var reg = false;
41090         for (var r in this.layout.regions) {
41091             reg = this.layout.getRegion(r);
41092             if (reg.getActivePanel()) {
41093                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41094                 reg.setActivePanel(reg.getActivePanel());
41095                 continue;
41096             }
41097             if (!reg.panels.length) {
41098                 continue;
41099             }
41100             reg.showPanel(reg.getPanel(0));
41101         }
41102         
41103         
41104         
41105         
41106     },
41107     
41108     /**
41109      * Returns the nested BorderLayout for this panel
41110      * @return {Roo.BorderLayout} 
41111      */
41112     getLayout : function(){
41113         return this.layout;
41114     },
41115     
41116      /**
41117      * Adds a xtype elements to the layout of the nested panel
41118      * <pre><code>
41119
41120 panel.addxtype({
41121        xtype : 'ContentPanel',
41122        region: 'west',
41123        items: [ .... ]
41124    }
41125 );
41126
41127 panel.addxtype({
41128         xtype : 'NestedLayoutPanel',
41129         region: 'west',
41130         layout: {
41131            center: { },
41132            west: { }   
41133         },
41134         items : [ ... list of content panels or nested layout panels.. ]
41135    }
41136 );
41137 </code></pre>
41138      * @param {Object} cfg Xtype definition of item to add.
41139      */
41140     addxtype : function(cfg) {
41141         return this.layout.addxtype(cfg);
41142     
41143     }
41144 });/*
41145  * Based on:
41146  * Ext JS Library 1.1.1
41147  * Copyright(c) 2006-2007, Ext JS, LLC.
41148  *
41149  * Originally Released Under LGPL - original licence link has changed is not relivant.
41150  *
41151  * Fork - LGPL
41152  * <script type="text/javascript">
41153  */
41154 /**
41155  * @class Roo.TabPanel
41156  * @extends Roo.util.Observable
41157  * A lightweight tab container.
41158  * <br><br>
41159  * Usage:
41160  * <pre><code>
41161 // basic tabs 1, built from existing content
41162 var tabs = new Roo.TabPanel("tabs1");
41163 tabs.addTab("script", "View Script");
41164 tabs.addTab("markup", "View Markup");
41165 tabs.activate("script");
41166
41167 // more advanced tabs, built from javascript
41168 var jtabs = new Roo.TabPanel("jtabs");
41169 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41170
41171 // set up the UpdateManager
41172 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41173 var updater = tab2.getUpdateManager();
41174 updater.setDefaultUrl("ajax1.htm");
41175 tab2.on('activate', updater.refresh, updater, true);
41176
41177 // Use setUrl for Ajax loading
41178 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41179 tab3.setUrl("ajax2.htm", null, true);
41180
41181 // Disabled tab
41182 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41183 tab4.disable();
41184
41185 jtabs.activate("jtabs-1");
41186  * </code></pre>
41187  * @constructor
41188  * Create a new TabPanel.
41189  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41190  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41191  */
41192 Roo.bootstrap.panel.Tabs = function(config){
41193     /**
41194     * The container element for this TabPanel.
41195     * @type Roo.Element
41196     */
41197     this.el = Roo.get(config.el);
41198     delete config.el;
41199     if(config){
41200         if(typeof config == "boolean"){
41201             this.tabPosition = config ? "bottom" : "top";
41202         }else{
41203             Roo.apply(this, config);
41204         }
41205     }
41206     
41207     if(this.tabPosition == "bottom"){
41208         // if tabs are at the bottom = create the body first.
41209         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41210         this.el.addClass("roo-tabs-bottom");
41211     }
41212     // next create the tabs holders
41213     
41214     if (this.tabPosition == "west"){
41215         
41216         var reg = this.region; // fake it..
41217         while (reg) {
41218             if (!reg.mgr.parent) {
41219                 break;
41220             }
41221             reg = reg.mgr.parent.region;
41222         }
41223         Roo.log("got nest?");
41224         Roo.log(reg);
41225         if (reg.mgr.getRegion('west')) {
41226             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41227             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41228             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41229             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41230             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41231         
41232             
41233         }
41234         
41235         
41236     } else {
41237      
41238         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41239         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41240         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41241         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41242     }
41243     
41244     
41245     if(Roo.isIE){
41246         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41247     }
41248     
41249     // finally - if tabs are at the top, then create the body last..
41250     if(this.tabPosition != "bottom"){
41251         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41252          * @type Roo.Element
41253          */
41254         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41255         this.el.addClass("roo-tabs-top");
41256     }
41257     this.items = [];
41258
41259     this.bodyEl.setStyle("position", "relative");
41260
41261     this.active = null;
41262     this.activateDelegate = this.activate.createDelegate(this);
41263
41264     this.addEvents({
41265         /**
41266          * @event tabchange
41267          * Fires when the active tab changes
41268          * @param {Roo.TabPanel} this
41269          * @param {Roo.TabPanelItem} activePanel The new active tab
41270          */
41271         "tabchange": true,
41272         /**
41273          * @event beforetabchange
41274          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41275          * @param {Roo.TabPanel} this
41276          * @param {Object} e Set cancel to true on this object to cancel the tab change
41277          * @param {Roo.TabPanelItem} tab The tab being changed to
41278          */
41279         "beforetabchange" : true
41280     });
41281
41282     Roo.EventManager.onWindowResize(this.onResize, this);
41283     this.cpad = this.el.getPadding("lr");
41284     this.hiddenCount = 0;
41285
41286
41287     // toolbar on the tabbar support...
41288     if (this.toolbar) {
41289         alert("no toolbar support yet");
41290         this.toolbar  = false;
41291         /*
41292         var tcfg = this.toolbar;
41293         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41294         this.toolbar = new Roo.Toolbar(tcfg);
41295         if (Roo.isSafari) {
41296             var tbl = tcfg.container.child('table', true);
41297             tbl.setAttribute('width', '100%');
41298         }
41299         */
41300         
41301     }
41302    
41303
41304
41305     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41306 };
41307
41308 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41309     /*
41310      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41311      */
41312     tabPosition : "top",
41313     /*
41314      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41315      */
41316     currentTabWidth : 0,
41317     /*
41318      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41319      */
41320     minTabWidth : 40,
41321     /*
41322      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41323      */
41324     maxTabWidth : 250,
41325     /*
41326      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41327      */
41328     preferredTabWidth : 175,
41329     /*
41330      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41331      */
41332     resizeTabs : false,
41333     /*
41334      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41335      */
41336     monitorResize : true,
41337     /*
41338      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41339      */
41340     toolbar : false,  // set by caller..
41341     
41342     region : false, /// set by caller
41343     
41344     disableTooltips : true, // not used yet...
41345
41346     /**
41347      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41348      * @param {String} id The id of the div to use <b>or create</b>
41349      * @param {String} text The text for the tab
41350      * @param {String} content (optional) Content to put in the TabPanelItem body
41351      * @param {Boolean} closable (optional) True to create a close icon on the tab
41352      * @return {Roo.TabPanelItem} The created TabPanelItem
41353      */
41354     addTab : function(id, text, content, closable, tpl)
41355     {
41356         var item = new Roo.bootstrap.panel.TabItem({
41357             panel: this,
41358             id : id,
41359             text : text,
41360             closable : closable,
41361             tpl : tpl
41362         });
41363         this.addTabItem(item);
41364         if(content){
41365             item.setContent(content);
41366         }
41367         return item;
41368     },
41369
41370     /**
41371      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41372      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41373      * @return {Roo.TabPanelItem}
41374      */
41375     getTab : function(id){
41376         return this.items[id];
41377     },
41378
41379     /**
41380      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41381      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41382      */
41383     hideTab : function(id){
41384         var t = this.items[id];
41385         if(!t.isHidden()){
41386            t.setHidden(true);
41387            this.hiddenCount++;
41388            this.autoSizeTabs();
41389         }
41390     },
41391
41392     /**
41393      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41394      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41395      */
41396     unhideTab : function(id){
41397         var t = this.items[id];
41398         if(t.isHidden()){
41399            t.setHidden(false);
41400            this.hiddenCount--;
41401            this.autoSizeTabs();
41402         }
41403     },
41404
41405     /**
41406      * Adds an existing {@link Roo.TabPanelItem}.
41407      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41408      */
41409     addTabItem : function(item)
41410     {
41411         this.items[item.id] = item;
41412         this.items.push(item);
41413         this.autoSizeTabs();
41414       //  if(this.resizeTabs){
41415     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41416   //         this.autoSizeTabs();
41417 //        }else{
41418 //            item.autoSize();
41419        // }
41420     },
41421
41422     /**
41423      * Removes a {@link Roo.TabPanelItem}.
41424      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41425      */
41426     removeTab : function(id){
41427         var items = this.items;
41428         var tab = items[id];
41429         if(!tab) { return; }
41430         var index = items.indexOf(tab);
41431         if(this.active == tab && items.length > 1){
41432             var newTab = this.getNextAvailable(index);
41433             if(newTab) {
41434                 newTab.activate();
41435             }
41436         }
41437         this.stripEl.dom.removeChild(tab.pnode.dom);
41438         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41439             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41440         }
41441         items.splice(index, 1);
41442         delete this.items[tab.id];
41443         tab.fireEvent("close", tab);
41444         tab.purgeListeners();
41445         this.autoSizeTabs();
41446     },
41447
41448     getNextAvailable : function(start){
41449         var items = this.items;
41450         var index = start;
41451         // look for a next tab that will slide over to
41452         // replace the one being removed
41453         while(index < items.length){
41454             var item = items[++index];
41455             if(item && !item.isHidden()){
41456                 return item;
41457             }
41458         }
41459         // if one isn't found select the previous tab (on the left)
41460         index = start;
41461         while(index >= 0){
41462             var item = items[--index];
41463             if(item && !item.isHidden()){
41464                 return item;
41465             }
41466         }
41467         return null;
41468     },
41469
41470     /**
41471      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41472      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41473      */
41474     disableTab : function(id){
41475         var tab = this.items[id];
41476         if(tab && this.active != tab){
41477             tab.disable();
41478         }
41479     },
41480
41481     /**
41482      * Enables a {@link Roo.TabPanelItem} that is disabled.
41483      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41484      */
41485     enableTab : function(id){
41486         var tab = this.items[id];
41487         tab.enable();
41488     },
41489
41490     /**
41491      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41492      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41493      * @return {Roo.TabPanelItem} The TabPanelItem.
41494      */
41495     activate : function(id)
41496     {
41497         //Roo.log('activite:'  + id);
41498         
41499         var tab = this.items[id];
41500         if(!tab){
41501             return null;
41502         }
41503         if(tab == this.active || tab.disabled){
41504             return tab;
41505         }
41506         var e = {};
41507         this.fireEvent("beforetabchange", this, e, tab);
41508         if(e.cancel !== true && !tab.disabled){
41509             if(this.active){
41510                 this.active.hide();
41511             }
41512             this.active = this.items[id];
41513             this.active.show();
41514             this.fireEvent("tabchange", this, this.active);
41515         }
41516         return tab;
41517     },
41518
41519     /**
41520      * Gets the active {@link Roo.TabPanelItem}.
41521      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41522      */
41523     getActiveTab : function(){
41524         return this.active;
41525     },
41526
41527     /**
41528      * Updates the tab body element to fit the height of the container element
41529      * for overflow scrolling
41530      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41531      */
41532     syncHeight : function(targetHeight){
41533         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41534         var bm = this.bodyEl.getMargins();
41535         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41536         this.bodyEl.setHeight(newHeight);
41537         return newHeight;
41538     },
41539
41540     onResize : function(){
41541         if(this.monitorResize){
41542             this.autoSizeTabs();
41543         }
41544     },
41545
41546     /**
41547      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41548      */
41549     beginUpdate : function(){
41550         this.updating = true;
41551     },
41552
41553     /**
41554      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41555      */
41556     endUpdate : function(){
41557         this.updating = false;
41558         this.autoSizeTabs();
41559     },
41560
41561     /**
41562      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41563      */
41564     autoSizeTabs : function()
41565     {
41566         var count = this.items.length;
41567         var vcount = count - this.hiddenCount;
41568         
41569         if (vcount < 2) {
41570             this.stripEl.hide();
41571         } else {
41572             this.stripEl.show();
41573         }
41574         
41575         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41576             return;
41577         }
41578         
41579         
41580         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41581         var availWidth = Math.floor(w / vcount);
41582         var b = this.stripBody;
41583         if(b.getWidth() > w){
41584             var tabs = this.items;
41585             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41586             if(availWidth < this.minTabWidth){
41587                 /*if(!this.sleft){    // incomplete scrolling code
41588                     this.createScrollButtons();
41589                 }
41590                 this.showScroll();
41591                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41592             }
41593         }else{
41594             if(this.currentTabWidth < this.preferredTabWidth){
41595                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41596             }
41597         }
41598     },
41599
41600     /**
41601      * Returns the number of tabs in this TabPanel.
41602      * @return {Number}
41603      */
41604      getCount : function(){
41605          return this.items.length;
41606      },
41607
41608     /**
41609      * Resizes all the tabs to the passed width
41610      * @param {Number} The new width
41611      */
41612     setTabWidth : function(width){
41613         this.currentTabWidth = width;
41614         for(var i = 0, len = this.items.length; i < len; i++) {
41615                 if(!this.items[i].isHidden()) {
41616                 this.items[i].setWidth(width);
41617             }
41618         }
41619     },
41620
41621     /**
41622      * Destroys this TabPanel
41623      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41624      */
41625     destroy : function(removeEl){
41626         Roo.EventManager.removeResizeListener(this.onResize, this);
41627         for(var i = 0, len = this.items.length; i < len; i++){
41628             this.items[i].purgeListeners();
41629         }
41630         if(removeEl === true){
41631             this.el.update("");
41632             this.el.remove();
41633         }
41634     },
41635     
41636     createStrip : function(container)
41637     {
41638         var strip = document.createElement("nav");
41639         strip.className = Roo.bootstrap.version == 4 ?
41640             "navbar-light bg-light" : 
41641             "navbar navbar-default"; //"x-tabs-wrap";
41642         container.appendChild(strip);
41643         return strip;
41644     },
41645     
41646     createStripList : function(strip)
41647     {
41648         // div wrapper for retard IE
41649         // returns the "tr" element.
41650         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41651         //'<div class="x-tabs-strip-wrap">'+
41652           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41653           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41654         return strip.firstChild; //.firstChild.firstChild.firstChild;
41655     },
41656     createBody : function(container)
41657     {
41658         var body = document.createElement("div");
41659         Roo.id(body, "tab-body");
41660         //Roo.fly(body).addClass("x-tabs-body");
41661         Roo.fly(body).addClass("tab-content");
41662         container.appendChild(body);
41663         return body;
41664     },
41665     createItemBody :function(bodyEl, id){
41666         var body = Roo.getDom(id);
41667         if(!body){
41668             body = document.createElement("div");
41669             body.id = id;
41670         }
41671         //Roo.fly(body).addClass("x-tabs-item-body");
41672         Roo.fly(body).addClass("tab-pane");
41673          bodyEl.insertBefore(body, bodyEl.firstChild);
41674         return body;
41675     },
41676     /** @private */
41677     createStripElements :  function(stripEl, text, closable, tpl)
41678     {
41679         var td = document.createElement("li"); // was td..
41680         td.className = 'nav-item';
41681         
41682         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41683         
41684         
41685         stripEl.appendChild(td);
41686         /*if(closable){
41687             td.className = "x-tabs-closable";
41688             if(!this.closeTpl){
41689                 this.closeTpl = new Roo.Template(
41690                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41691                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41692                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41693                 );
41694             }
41695             var el = this.closeTpl.overwrite(td, {"text": text});
41696             var close = el.getElementsByTagName("div")[0];
41697             var inner = el.getElementsByTagName("em")[0];
41698             return {"el": el, "close": close, "inner": inner};
41699         } else {
41700         */
41701         // not sure what this is..
41702 //            if(!this.tabTpl){
41703                 //this.tabTpl = new Roo.Template(
41704                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41705                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41706                 //);
41707 //                this.tabTpl = new Roo.Template(
41708 //                   '<a href="#">' +
41709 //                   '<span unselectable="on"' +
41710 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41711 //                            ' >{text}</span></a>'
41712 //                );
41713 //                
41714 //            }
41715
41716
41717             var template = tpl || this.tabTpl || false;
41718             
41719             if(!template){
41720                 template =  new Roo.Template(
41721                         Roo.bootstrap.version == 4 ? 
41722                             (
41723                                 '<a class="nav-link" href="#" unselectable="on"' +
41724                                      (this.disableTooltips ? '' : ' title="{text}"') +
41725                                      ' >{text}</a>'
41726                             ) : (
41727                                 '<a class="nav-link" href="#">' +
41728                                 '<span unselectable="on"' +
41729                                          (this.disableTooltips ? '' : ' title="{text}"') +
41730                                     ' >{text}</span></a>'
41731                             )
41732                 );
41733             }
41734             
41735             switch (typeof(template)) {
41736                 case 'object' :
41737                     break;
41738                 case 'string' :
41739                     template = new Roo.Template(template);
41740                     break;
41741                 default :
41742                     break;
41743             }
41744             
41745             var el = template.overwrite(td, {"text": text});
41746             
41747             var inner = el.getElementsByTagName("span")[0];
41748             
41749             return {"el": el, "inner": inner};
41750             
41751     }
41752         
41753     
41754 });
41755
41756 /**
41757  * @class Roo.TabPanelItem
41758  * @extends Roo.util.Observable
41759  * Represents an individual item (tab plus body) in a TabPanel.
41760  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41761  * @param {String} id The id of this TabPanelItem
41762  * @param {String} text The text for the tab of this TabPanelItem
41763  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41764  */
41765 Roo.bootstrap.panel.TabItem = function(config){
41766     /**
41767      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41768      * @type Roo.TabPanel
41769      */
41770     this.tabPanel = config.panel;
41771     /**
41772      * The id for this TabPanelItem
41773      * @type String
41774      */
41775     this.id = config.id;
41776     /** @private */
41777     this.disabled = false;
41778     /** @private */
41779     this.text = config.text;
41780     /** @private */
41781     this.loaded = false;
41782     this.closable = config.closable;
41783
41784     /**
41785      * The body element for this TabPanelItem.
41786      * @type Roo.Element
41787      */
41788     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41789     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41790     this.bodyEl.setStyle("display", "block");
41791     this.bodyEl.setStyle("zoom", "1");
41792     //this.hideAction();
41793
41794     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41795     /** @private */
41796     this.el = Roo.get(els.el);
41797     this.inner = Roo.get(els.inner, true);
41798      this.textEl = Roo.bootstrap.version == 4 ?
41799         this.el : Roo.get(this.el.dom.firstChild, true);
41800
41801     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41802     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41803
41804     
41805 //    this.el.on("mousedown", this.onTabMouseDown, this);
41806     this.el.on("click", this.onTabClick, this);
41807     /** @private */
41808     if(config.closable){
41809         var c = Roo.get(els.close, true);
41810         c.dom.title = this.closeText;
41811         c.addClassOnOver("close-over");
41812         c.on("click", this.closeClick, this);
41813      }
41814
41815     this.addEvents({
41816          /**
41817          * @event activate
41818          * Fires when this tab becomes the active tab.
41819          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41820          * @param {Roo.TabPanelItem} this
41821          */
41822         "activate": true,
41823         /**
41824          * @event beforeclose
41825          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41826          * @param {Roo.TabPanelItem} this
41827          * @param {Object} e Set cancel to true on this object to cancel the close.
41828          */
41829         "beforeclose": true,
41830         /**
41831          * @event close
41832          * Fires when this tab is closed.
41833          * @param {Roo.TabPanelItem} this
41834          */
41835          "close": true,
41836         /**
41837          * @event deactivate
41838          * Fires when this tab is no longer the active tab.
41839          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41840          * @param {Roo.TabPanelItem} this
41841          */
41842          "deactivate" : true
41843     });
41844     this.hidden = false;
41845
41846     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41847 };
41848
41849 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41850            {
41851     purgeListeners : function(){
41852        Roo.util.Observable.prototype.purgeListeners.call(this);
41853        this.el.removeAllListeners();
41854     },
41855     /**
41856      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41857      */
41858     show : function(){
41859         this.status_node.addClass("active");
41860         this.showAction();
41861         if(Roo.isOpera){
41862             this.tabPanel.stripWrap.repaint();
41863         }
41864         this.fireEvent("activate", this.tabPanel, this);
41865     },
41866
41867     /**
41868      * Returns true if this tab is the active tab.
41869      * @return {Boolean}
41870      */
41871     isActive : function(){
41872         return this.tabPanel.getActiveTab() == this;
41873     },
41874
41875     /**
41876      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41877      */
41878     hide : function(){
41879         this.status_node.removeClass("active");
41880         this.hideAction();
41881         this.fireEvent("deactivate", this.tabPanel, this);
41882     },
41883
41884     hideAction : function(){
41885         this.bodyEl.hide();
41886         this.bodyEl.setStyle("position", "absolute");
41887         this.bodyEl.setLeft("-20000px");
41888         this.bodyEl.setTop("-20000px");
41889     },
41890
41891     showAction : function(){
41892         this.bodyEl.setStyle("position", "relative");
41893         this.bodyEl.setTop("");
41894         this.bodyEl.setLeft("");
41895         this.bodyEl.show();
41896     },
41897
41898     /**
41899      * Set the tooltip for the tab.
41900      * @param {String} tooltip The tab's tooltip
41901      */
41902     setTooltip : function(text){
41903         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41904             this.textEl.dom.qtip = text;
41905             this.textEl.dom.removeAttribute('title');
41906         }else{
41907             this.textEl.dom.title = text;
41908         }
41909     },
41910
41911     onTabClick : function(e){
41912         e.preventDefault();
41913         this.tabPanel.activate(this.id);
41914     },
41915
41916     onTabMouseDown : function(e){
41917         e.preventDefault();
41918         this.tabPanel.activate(this.id);
41919     },
41920 /*
41921     getWidth : function(){
41922         return this.inner.getWidth();
41923     },
41924
41925     setWidth : function(width){
41926         var iwidth = width - this.linode.getPadding("lr");
41927         this.inner.setWidth(iwidth);
41928         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41929         this.linode.setWidth(width);
41930     },
41931 */
41932     /**
41933      * Show or hide the tab
41934      * @param {Boolean} hidden True to hide or false to show.
41935      */
41936     setHidden : function(hidden){
41937         this.hidden = hidden;
41938         this.linode.setStyle("display", hidden ? "none" : "");
41939     },
41940
41941     /**
41942      * Returns true if this tab is "hidden"
41943      * @return {Boolean}
41944      */
41945     isHidden : function(){
41946         return this.hidden;
41947     },
41948
41949     /**
41950      * Returns the text for this tab
41951      * @return {String}
41952      */
41953     getText : function(){
41954         return this.text;
41955     },
41956     /*
41957     autoSize : function(){
41958         //this.el.beginMeasure();
41959         this.textEl.setWidth(1);
41960         /*
41961          *  #2804 [new] Tabs in Roojs
41962          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41963          */
41964         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41965         //this.el.endMeasure();
41966     //},
41967
41968     /**
41969      * Sets the text for the tab (Note: this also sets the tooltip text)
41970      * @param {String} text The tab's text and tooltip
41971      */
41972     setText : function(text){
41973         this.text = text;
41974         this.textEl.update(text);
41975         this.setTooltip(text);
41976         //if(!this.tabPanel.resizeTabs){
41977         //    this.autoSize();
41978         //}
41979     },
41980     /**
41981      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41982      */
41983     activate : function(){
41984         this.tabPanel.activate(this.id);
41985     },
41986
41987     /**
41988      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41989      */
41990     disable : function(){
41991         if(this.tabPanel.active != this){
41992             this.disabled = true;
41993             this.status_node.addClass("disabled");
41994         }
41995     },
41996
41997     /**
41998      * Enables this TabPanelItem if it was previously disabled.
41999      */
42000     enable : function(){
42001         this.disabled = false;
42002         this.status_node.removeClass("disabled");
42003     },
42004
42005     /**
42006      * Sets the content for this TabPanelItem.
42007      * @param {String} content The content
42008      * @param {Boolean} loadScripts true to look for and load scripts
42009      */
42010     setContent : function(content, loadScripts){
42011         this.bodyEl.update(content, loadScripts);
42012     },
42013
42014     /**
42015      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42016      * @return {Roo.UpdateManager} The UpdateManager
42017      */
42018     getUpdateManager : function(){
42019         return this.bodyEl.getUpdateManager();
42020     },
42021
42022     /**
42023      * Set a URL to be used to load the content for this TabPanelItem.
42024      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42025      * @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)
42026      * @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)
42027      * @return {Roo.UpdateManager} The UpdateManager
42028      */
42029     setUrl : function(url, params, loadOnce){
42030         if(this.refreshDelegate){
42031             this.un('activate', this.refreshDelegate);
42032         }
42033         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42034         this.on("activate", this.refreshDelegate);
42035         return this.bodyEl.getUpdateManager();
42036     },
42037
42038     /** @private */
42039     _handleRefresh : function(url, params, loadOnce){
42040         if(!loadOnce || !this.loaded){
42041             var updater = this.bodyEl.getUpdateManager();
42042             updater.update(url, params, this._setLoaded.createDelegate(this));
42043         }
42044     },
42045
42046     /**
42047      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42048      *   Will fail silently if the setUrl method has not been called.
42049      *   This does not activate the panel, just updates its content.
42050      */
42051     refresh : function(){
42052         if(this.refreshDelegate){
42053            this.loaded = false;
42054            this.refreshDelegate();
42055         }
42056     },
42057
42058     /** @private */
42059     _setLoaded : function(){
42060         this.loaded = true;
42061     },
42062
42063     /** @private */
42064     closeClick : function(e){
42065         var o = {};
42066         e.stopEvent();
42067         this.fireEvent("beforeclose", this, o);
42068         if(o.cancel !== true){
42069             this.tabPanel.removeTab(this.id);
42070         }
42071     },
42072     /**
42073      * The text displayed in the tooltip for the close icon.
42074      * @type String
42075      */
42076     closeText : "Close this tab"
42077 });
42078 /**
42079 *    This script refer to:
42080 *    Title: International Telephone Input
42081 *    Author: Jack O'Connor
42082 *    Code version:  v12.1.12
42083 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42084 **/
42085
42086 Roo.bootstrap.form.PhoneInputData = function() {
42087     var d = [
42088       [
42089         "Afghanistan (‫افغانستان‬‎)",
42090         "af",
42091         "93"
42092       ],
42093       [
42094         "Albania (Shqipëri)",
42095         "al",
42096         "355"
42097       ],
42098       [
42099         "Algeria (‫الجزائر‬‎)",
42100         "dz",
42101         "213"
42102       ],
42103       [
42104         "American Samoa",
42105         "as",
42106         "1684"
42107       ],
42108       [
42109         "Andorra",
42110         "ad",
42111         "376"
42112       ],
42113       [
42114         "Angola",
42115         "ao",
42116         "244"
42117       ],
42118       [
42119         "Anguilla",
42120         "ai",
42121         "1264"
42122       ],
42123       [
42124         "Antigua and Barbuda",
42125         "ag",
42126         "1268"
42127       ],
42128       [
42129         "Argentina",
42130         "ar",
42131         "54"
42132       ],
42133       [
42134         "Armenia (Հայաստան)",
42135         "am",
42136         "374"
42137       ],
42138       [
42139         "Aruba",
42140         "aw",
42141         "297"
42142       ],
42143       [
42144         "Australia",
42145         "au",
42146         "61",
42147         0
42148       ],
42149       [
42150         "Austria (Österreich)",
42151         "at",
42152         "43"
42153       ],
42154       [
42155         "Azerbaijan (Azərbaycan)",
42156         "az",
42157         "994"
42158       ],
42159       [
42160         "Bahamas",
42161         "bs",
42162         "1242"
42163       ],
42164       [
42165         "Bahrain (‫البحرين‬‎)",
42166         "bh",
42167         "973"
42168       ],
42169       [
42170         "Bangladesh (বাংলাদেশ)",
42171         "bd",
42172         "880"
42173       ],
42174       [
42175         "Barbados",
42176         "bb",
42177         "1246"
42178       ],
42179       [
42180         "Belarus (Беларусь)",
42181         "by",
42182         "375"
42183       ],
42184       [
42185         "Belgium (België)",
42186         "be",
42187         "32"
42188       ],
42189       [
42190         "Belize",
42191         "bz",
42192         "501"
42193       ],
42194       [
42195         "Benin (Bénin)",
42196         "bj",
42197         "229"
42198       ],
42199       [
42200         "Bermuda",
42201         "bm",
42202         "1441"
42203       ],
42204       [
42205         "Bhutan (འབྲུག)",
42206         "bt",
42207         "975"
42208       ],
42209       [
42210         "Bolivia",
42211         "bo",
42212         "591"
42213       ],
42214       [
42215         "Bosnia and Herzegovina (Босна и Херцеговина)",
42216         "ba",
42217         "387"
42218       ],
42219       [
42220         "Botswana",
42221         "bw",
42222         "267"
42223       ],
42224       [
42225         "Brazil (Brasil)",
42226         "br",
42227         "55"
42228       ],
42229       [
42230         "British Indian Ocean Territory",
42231         "io",
42232         "246"
42233       ],
42234       [
42235         "British Virgin Islands",
42236         "vg",
42237         "1284"
42238       ],
42239       [
42240         "Brunei",
42241         "bn",
42242         "673"
42243       ],
42244       [
42245         "Bulgaria (България)",
42246         "bg",
42247         "359"
42248       ],
42249       [
42250         "Burkina Faso",
42251         "bf",
42252         "226"
42253       ],
42254       [
42255         "Burundi (Uburundi)",
42256         "bi",
42257         "257"
42258       ],
42259       [
42260         "Cambodia (កម្ពុជា)",
42261         "kh",
42262         "855"
42263       ],
42264       [
42265         "Cameroon (Cameroun)",
42266         "cm",
42267         "237"
42268       ],
42269       [
42270         "Canada",
42271         "ca",
42272         "1",
42273         1,
42274         ["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"]
42275       ],
42276       [
42277         "Cape Verde (Kabu Verdi)",
42278         "cv",
42279         "238"
42280       ],
42281       [
42282         "Caribbean Netherlands",
42283         "bq",
42284         "599",
42285         1
42286       ],
42287       [
42288         "Cayman Islands",
42289         "ky",
42290         "1345"
42291       ],
42292       [
42293         "Central African Republic (République centrafricaine)",
42294         "cf",
42295         "236"
42296       ],
42297       [
42298         "Chad (Tchad)",
42299         "td",
42300         "235"
42301       ],
42302       [
42303         "Chile",
42304         "cl",
42305         "56"
42306       ],
42307       [
42308         "China (中国)",
42309         "cn",
42310         "86"
42311       ],
42312       [
42313         "Christmas Island",
42314         "cx",
42315         "61",
42316         2
42317       ],
42318       [
42319         "Cocos (Keeling) Islands",
42320         "cc",
42321         "61",
42322         1
42323       ],
42324       [
42325         "Colombia",
42326         "co",
42327         "57"
42328       ],
42329       [
42330         "Comoros (‫جزر القمر‬‎)",
42331         "km",
42332         "269"
42333       ],
42334       [
42335         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42336         "cd",
42337         "243"
42338       ],
42339       [
42340         "Congo (Republic) (Congo-Brazzaville)",
42341         "cg",
42342         "242"
42343       ],
42344       [
42345         "Cook Islands",
42346         "ck",
42347         "682"
42348       ],
42349       [
42350         "Costa Rica",
42351         "cr",
42352         "506"
42353       ],
42354       [
42355         "Côte d’Ivoire",
42356         "ci",
42357         "225"
42358       ],
42359       [
42360         "Croatia (Hrvatska)",
42361         "hr",
42362         "385"
42363       ],
42364       [
42365         "Cuba",
42366         "cu",
42367         "53"
42368       ],
42369       [
42370         "Curaçao",
42371         "cw",
42372         "599",
42373         0
42374       ],
42375       [
42376         "Cyprus (Κύπρος)",
42377         "cy",
42378         "357"
42379       ],
42380       [
42381         "Czech Republic (Česká republika)",
42382         "cz",
42383         "420"
42384       ],
42385       [
42386         "Denmark (Danmark)",
42387         "dk",
42388         "45"
42389       ],
42390       [
42391         "Djibouti",
42392         "dj",
42393         "253"
42394       ],
42395       [
42396         "Dominica",
42397         "dm",
42398         "1767"
42399       ],
42400       [
42401         "Dominican Republic (República Dominicana)",
42402         "do",
42403         "1",
42404         2,
42405         ["809", "829", "849"]
42406       ],
42407       [
42408         "Ecuador",
42409         "ec",
42410         "593"
42411       ],
42412       [
42413         "Egypt (‫مصر‬‎)",
42414         "eg",
42415         "20"
42416       ],
42417       [
42418         "El Salvador",
42419         "sv",
42420         "503"
42421       ],
42422       [
42423         "Equatorial Guinea (Guinea Ecuatorial)",
42424         "gq",
42425         "240"
42426       ],
42427       [
42428         "Eritrea",
42429         "er",
42430         "291"
42431       ],
42432       [
42433         "Estonia (Eesti)",
42434         "ee",
42435         "372"
42436       ],
42437       [
42438         "Ethiopia",
42439         "et",
42440         "251"
42441       ],
42442       [
42443         "Falkland Islands (Islas Malvinas)",
42444         "fk",
42445         "500"
42446       ],
42447       [
42448         "Faroe Islands (Føroyar)",
42449         "fo",
42450         "298"
42451       ],
42452       [
42453         "Fiji",
42454         "fj",
42455         "679"
42456       ],
42457       [
42458         "Finland (Suomi)",
42459         "fi",
42460         "358",
42461         0
42462       ],
42463       [
42464         "France",
42465         "fr",
42466         "33"
42467       ],
42468       [
42469         "French Guiana (Guyane française)",
42470         "gf",
42471         "594"
42472       ],
42473       [
42474         "French Polynesia (Polynésie française)",
42475         "pf",
42476         "689"
42477       ],
42478       [
42479         "Gabon",
42480         "ga",
42481         "241"
42482       ],
42483       [
42484         "Gambia",
42485         "gm",
42486         "220"
42487       ],
42488       [
42489         "Georgia (საქართველო)",
42490         "ge",
42491         "995"
42492       ],
42493       [
42494         "Germany (Deutschland)",
42495         "de",
42496         "49"
42497       ],
42498       [
42499         "Ghana (Gaana)",
42500         "gh",
42501         "233"
42502       ],
42503       [
42504         "Gibraltar",
42505         "gi",
42506         "350"
42507       ],
42508       [
42509         "Greece (Ελλάδα)",
42510         "gr",
42511         "30"
42512       ],
42513       [
42514         "Greenland (Kalaallit Nunaat)",
42515         "gl",
42516         "299"
42517       ],
42518       [
42519         "Grenada",
42520         "gd",
42521         "1473"
42522       ],
42523       [
42524         "Guadeloupe",
42525         "gp",
42526         "590",
42527         0
42528       ],
42529       [
42530         "Guam",
42531         "gu",
42532         "1671"
42533       ],
42534       [
42535         "Guatemala",
42536         "gt",
42537         "502"
42538       ],
42539       [
42540         "Guernsey",
42541         "gg",
42542         "44",
42543         1
42544       ],
42545       [
42546         "Guinea (Guinée)",
42547         "gn",
42548         "224"
42549       ],
42550       [
42551         "Guinea-Bissau (Guiné Bissau)",
42552         "gw",
42553         "245"
42554       ],
42555       [
42556         "Guyana",
42557         "gy",
42558         "592"
42559       ],
42560       [
42561         "Haiti",
42562         "ht",
42563         "509"
42564       ],
42565       [
42566         "Honduras",
42567         "hn",
42568         "504"
42569       ],
42570       [
42571         "Hong Kong (香港)",
42572         "hk",
42573         "852"
42574       ],
42575       [
42576         "Hungary (Magyarország)",
42577         "hu",
42578         "36"
42579       ],
42580       [
42581         "Iceland (Ísland)",
42582         "is",
42583         "354"
42584       ],
42585       [
42586         "India (भारत)",
42587         "in",
42588         "91"
42589       ],
42590       [
42591         "Indonesia",
42592         "id",
42593         "62"
42594       ],
42595       [
42596         "Iran (‫ایران‬‎)",
42597         "ir",
42598         "98"
42599       ],
42600       [
42601         "Iraq (‫العراق‬‎)",
42602         "iq",
42603         "964"
42604       ],
42605       [
42606         "Ireland",
42607         "ie",
42608         "353"
42609       ],
42610       [
42611         "Isle of Man",
42612         "im",
42613         "44",
42614         2
42615       ],
42616       [
42617         "Israel (‫ישראל‬‎)",
42618         "il",
42619         "972"
42620       ],
42621       [
42622         "Italy (Italia)",
42623         "it",
42624         "39",
42625         0
42626       ],
42627       [
42628         "Jamaica",
42629         "jm",
42630         "1876"
42631       ],
42632       [
42633         "Japan (日本)",
42634         "jp",
42635         "81"
42636       ],
42637       [
42638         "Jersey",
42639         "je",
42640         "44",
42641         3
42642       ],
42643       [
42644         "Jordan (‫الأردن‬‎)",
42645         "jo",
42646         "962"
42647       ],
42648       [
42649         "Kazakhstan (Казахстан)",
42650         "kz",
42651         "7",
42652         1
42653       ],
42654       [
42655         "Kenya",
42656         "ke",
42657         "254"
42658       ],
42659       [
42660         "Kiribati",
42661         "ki",
42662         "686"
42663       ],
42664       [
42665         "Kosovo",
42666         "xk",
42667         "383"
42668       ],
42669       [
42670         "Kuwait (‫الكويت‬‎)",
42671         "kw",
42672         "965"
42673       ],
42674       [
42675         "Kyrgyzstan (Кыргызстан)",
42676         "kg",
42677         "996"
42678       ],
42679       [
42680         "Laos (ລາວ)",
42681         "la",
42682         "856"
42683       ],
42684       [
42685         "Latvia (Latvija)",
42686         "lv",
42687         "371"
42688       ],
42689       [
42690         "Lebanon (‫لبنان‬‎)",
42691         "lb",
42692         "961"
42693       ],
42694       [
42695         "Lesotho",
42696         "ls",
42697         "266"
42698       ],
42699       [
42700         "Liberia",
42701         "lr",
42702         "231"
42703       ],
42704       [
42705         "Libya (‫ليبيا‬‎)",
42706         "ly",
42707         "218"
42708       ],
42709       [
42710         "Liechtenstein",
42711         "li",
42712         "423"
42713       ],
42714       [
42715         "Lithuania (Lietuva)",
42716         "lt",
42717         "370"
42718       ],
42719       [
42720         "Luxembourg",
42721         "lu",
42722         "352"
42723       ],
42724       [
42725         "Macau (澳門)",
42726         "mo",
42727         "853"
42728       ],
42729       [
42730         "Macedonia (FYROM) (Македонија)",
42731         "mk",
42732         "389"
42733       ],
42734       [
42735         "Madagascar (Madagasikara)",
42736         "mg",
42737         "261"
42738       ],
42739       [
42740         "Malawi",
42741         "mw",
42742         "265"
42743       ],
42744       [
42745         "Malaysia",
42746         "my",
42747         "60"
42748       ],
42749       [
42750         "Maldives",
42751         "mv",
42752         "960"
42753       ],
42754       [
42755         "Mali",
42756         "ml",
42757         "223"
42758       ],
42759       [
42760         "Malta",
42761         "mt",
42762         "356"
42763       ],
42764       [
42765         "Marshall Islands",
42766         "mh",
42767         "692"
42768       ],
42769       [
42770         "Martinique",
42771         "mq",
42772         "596"
42773       ],
42774       [
42775         "Mauritania (‫موريتانيا‬‎)",
42776         "mr",
42777         "222"
42778       ],
42779       [
42780         "Mauritius (Moris)",
42781         "mu",
42782         "230"
42783       ],
42784       [
42785         "Mayotte",
42786         "yt",
42787         "262",
42788         1
42789       ],
42790       [
42791         "Mexico (México)",
42792         "mx",
42793         "52"
42794       ],
42795       [
42796         "Micronesia",
42797         "fm",
42798         "691"
42799       ],
42800       [
42801         "Moldova (Republica Moldova)",
42802         "md",
42803         "373"
42804       ],
42805       [
42806         "Monaco",
42807         "mc",
42808         "377"
42809       ],
42810       [
42811         "Mongolia (Монгол)",
42812         "mn",
42813         "976"
42814       ],
42815       [
42816         "Montenegro (Crna Gora)",
42817         "me",
42818         "382"
42819       ],
42820       [
42821         "Montserrat",
42822         "ms",
42823         "1664"
42824       ],
42825       [
42826         "Morocco (‫المغرب‬‎)",
42827         "ma",
42828         "212",
42829         0
42830       ],
42831       [
42832         "Mozambique (Moçambique)",
42833         "mz",
42834         "258"
42835       ],
42836       [
42837         "Myanmar (Burma) (မြန်မာ)",
42838         "mm",
42839         "95"
42840       ],
42841       [
42842         "Namibia (Namibië)",
42843         "na",
42844         "264"
42845       ],
42846       [
42847         "Nauru",
42848         "nr",
42849         "674"
42850       ],
42851       [
42852         "Nepal (नेपाल)",
42853         "np",
42854         "977"
42855       ],
42856       [
42857         "Netherlands (Nederland)",
42858         "nl",
42859         "31"
42860       ],
42861       [
42862         "New Caledonia (Nouvelle-Calédonie)",
42863         "nc",
42864         "687"
42865       ],
42866       [
42867         "New Zealand",
42868         "nz",
42869         "64"
42870       ],
42871       [
42872         "Nicaragua",
42873         "ni",
42874         "505"
42875       ],
42876       [
42877         "Niger (Nijar)",
42878         "ne",
42879         "227"
42880       ],
42881       [
42882         "Nigeria",
42883         "ng",
42884         "234"
42885       ],
42886       [
42887         "Niue",
42888         "nu",
42889         "683"
42890       ],
42891       [
42892         "Norfolk Island",
42893         "nf",
42894         "672"
42895       ],
42896       [
42897         "North Korea (조선 민주주의 인민 공화국)",
42898         "kp",
42899         "850"
42900       ],
42901       [
42902         "Northern Mariana Islands",
42903         "mp",
42904         "1670"
42905       ],
42906       [
42907         "Norway (Norge)",
42908         "no",
42909         "47",
42910         0
42911       ],
42912       [
42913         "Oman (‫عُمان‬‎)",
42914         "om",
42915         "968"
42916       ],
42917       [
42918         "Pakistan (‫پاکستان‬‎)",
42919         "pk",
42920         "92"
42921       ],
42922       [
42923         "Palau",
42924         "pw",
42925         "680"
42926       ],
42927       [
42928         "Palestine (‫فلسطين‬‎)",
42929         "ps",
42930         "970"
42931       ],
42932       [
42933         "Panama (Panamá)",
42934         "pa",
42935         "507"
42936       ],
42937       [
42938         "Papua New Guinea",
42939         "pg",
42940         "675"
42941       ],
42942       [
42943         "Paraguay",
42944         "py",
42945         "595"
42946       ],
42947       [
42948         "Peru (Perú)",
42949         "pe",
42950         "51"
42951       ],
42952       [
42953         "Philippines",
42954         "ph",
42955         "63"
42956       ],
42957       [
42958         "Poland (Polska)",
42959         "pl",
42960         "48"
42961       ],
42962       [
42963         "Portugal",
42964         "pt",
42965         "351"
42966       ],
42967       [
42968         "Puerto Rico",
42969         "pr",
42970         "1",
42971         3,
42972         ["787", "939"]
42973       ],
42974       [
42975         "Qatar (‫قطر‬‎)",
42976         "qa",
42977         "974"
42978       ],
42979       [
42980         "Réunion (La Réunion)",
42981         "re",
42982         "262",
42983         0
42984       ],
42985       [
42986         "Romania (România)",
42987         "ro",
42988         "40"
42989       ],
42990       [
42991         "Russia (Россия)",
42992         "ru",
42993         "7",
42994         0
42995       ],
42996       [
42997         "Rwanda",
42998         "rw",
42999         "250"
43000       ],
43001       [
43002         "Saint Barthélemy",
43003         "bl",
43004         "590",
43005         1
43006       ],
43007       [
43008         "Saint Helena",
43009         "sh",
43010         "290"
43011       ],
43012       [
43013         "Saint Kitts and Nevis",
43014         "kn",
43015         "1869"
43016       ],
43017       [
43018         "Saint Lucia",
43019         "lc",
43020         "1758"
43021       ],
43022       [
43023         "Saint Martin (Saint-Martin (partie française))",
43024         "mf",
43025         "590",
43026         2
43027       ],
43028       [
43029         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43030         "pm",
43031         "508"
43032       ],
43033       [
43034         "Saint Vincent and the Grenadines",
43035         "vc",
43036         "1784"
43037       ],
43038       [
43039         "Samoa",
43040         "ws",
43041         "685"
43042       ],
43043       [
43044         "San Marino",
43045         "sm",
43046         "378"
43047       ],
43048       [
43049         "São Tomé and Príncipe (São Tomé e Príncipe)",
43050         "st",
43051         "239"
43052       ],
43053       [
43054         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43055         "sa",
43056         "966"
43057       ],
43058       [
43059         "Senegal (Sénégal)",
43060         "sn",
43061         "221"
43062       ],
43063       [
43064         "Serbia (Србија)",
43065         "rs",
43066         "381"
43067       ],
43068       [
43069         "Seychelles",
43070         "sc",
43071         "248"
43072       ],
43073       [
43074         "Sierra Leone",
43075         "sl",
43076         "232"
43077       ],
43078       [
43079         "Singapore",
43080         "sg",
43081         "65"
43082       ],
43083       [
43084         "Sint Maarten",
43085         "sx",
43086         "1721"
43087       ],
43088       [
43089         "Slovakia (Slovensko)",
43090         "sk",
43091         "421"
43092       ],
43093       [
43094         "Slovenia (Slovenija)",
43095         "si",
43096         "386"
43097       ],
43098       [
43099         "Solomon Islands",
43100         "sb",
43101         "677"
43102       ],
43103       [
43104         "Somalia (Soomaaliya)",
43105         "so",
43106         "252"
43107       ],
43108       [
43109         "South Africa",
43110         "za",
43111         "27"
43112       ],
43113       [
43114         "South Korea (대한민국)",
43115         "kr",
43116         "82"
43117       ],
43118       [
43119         "South Sudan (‫جنوب السودان‬‎)",
43120         "ss",
43121         "211"
43122       ],
43123       [
43124         "Spain (España)",
43125         "es",
43126         "34"
43127       ],
43128       [
43129         "Sri Lanka (ශ්‍රී ලංකාව)",
43130         "lk",
43131         "94"
43132       ],
43133       [
43134         "Sudan (‫السودان‬‎)",
43135         "sd",
43136         "249"
43137       ],
43138       [
43139         "Suriname",
43140         "sr",
43141         "597"
43142       ],
43143       [
43144         "Svalbard and Jan Mayen",
43145         "sj",
43146         "47",
43147         1
43148       ],
43149       [
43150         "Swaziland",
43151         "sz",
43152         "268"
43153       ],
43154       [
43155         "Sweden (Sverige)",
43156         "se",
43157         "46"
43158       ],
43159       [
43160         "Switzerland (Schweiz)",
43161         "ch",
43162         "41"
43163       ],
43164       [
43165         "Syria (‫سوريا‬‎)",
43166         "sy",
43167         "963"
43168       ],
43169       [
43170         "Taiwan (台灣)",
43171         "tw",
43172         "886"
43173       ],
43174       [
43175         "Tajikistan",
43176         "tj",
43177         "992"
43178       ],
43179       [
43180         "Tanzania",
43181         "tz",
43182         "255"
43183       ],
43184       [
43185         "Thailand (ไทย)",
43186         "th",
43187         "66"
43188       ],
43189       [
43190         "Timor-Leste",
43191         "tl",
43192         "670"
43193       ],
43194       [
43195         "Togo",
43196         "tg",
43197         "228"
43198       ],
43199       [
43200         "Tokelau",
43201         "tk",
43202         "690"
43203       ],
43204       [
43205         "Tonga",
43206         "to",
43207         "676"
43208       ],
43209       [
43210         "Trinidad and Tobago",
43211         "tt",
43212         "1868"
43213       ],
43214       [
43215         "Tunisia (‫تونس‬‎)",
43216         "tn",
43217         "216"
43218       ],
43219       [
43220         "Turkey (Türkiye)",
43221         "tr",
43222         "90"
43223       ],
43224       [
43225         "Turkmenistan",
43226         "tm",
43227         "993"
43228       ],
43229       [
43230         "Turks and Caicos Islands",
43231         "tc",
43232         "1649"
43233       ],
43234       [
43235         "Tuvalu",
43236         "tv",
43237         "688"
43238       ],
43239       [
43240         "U.S. Virgin Islands",
43241         "vi",
43242         "1340"
43243       ],
43244       [
43245         "Uganda",
43246         "ug",
43247         "256"
43248       ],
43249       [
43250         "Ukraine (Україна)",
43251         "ua",
43252         "380"
43253       ],
43254       [
43255         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43256         "ae",
43257         "971"
43258       ],
43259       [
43260         "United Kingdom",
43261         "gb",
43262         "44",
43263         0
43264       ],
43265       [
43266         "United States",
43267         "us",
43268         "1",
43269         0
43270       ],
43271       [
43272         "Uruguay",
43273         "uy",
43274         "598"
43275       ],
43276       [
43277         "Uzbekistan (Oʻzbekiston)",
43278         "uz",
43279         "998"
43280       ],
43281       [
43282         "Vanuatu",
43283         "vu",
43284         "678"
43285       ],
43286       [
43287         "Vatican City (Città del Vaticano)",
43288         "va",
43289         "39",
43290         1
43291       ],
43292       [
43293         "Venezuela",
43294         "ve",
43295         "58"
43296       ],
43297       [
43298         "Vietnam (Việt Nam)",
43299         "vn",
43300         "84"
43301       ],
43302       [
43303         "Wallis and Futuna (Wallis-et-Futuna)",
43304         "wf",
43305         "681"
43306       ],
43307       [
43308         "Western Sahara (‫الصحراء الغربية‬‎)",
43309         "eh",
43310         "212",
43311         1
43312       ],
43313       [
43314         "Yemen (‫اليمن‬‎)",
43315         "ye",
43316         "967"
43317       ],
43318       [
43319         "Zambia",
43320         "zm",
43321         "260"
43322       ],
43323       [
43324         "Zimbabwe",
43325         "zw",
43326         "263"
43327       ],
43328       [
43329         "Åland Islands",
43330         "ax",
43331         "358",
43332         1
43333       ]
43334   ];
43335   
43336   return d;
43337 }/**
43338 *    This script refer to:
43339 *    Title: International Telephone Input
43340 *    Author: Jack O'Connor
43341 *    Code version:  v12.1.12
43342 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43343 **/
43344
43345 /**
43346  * @class Roo.bootstrap.form.PhoneInput
43347  * @extends Roo.bootstrap.form.TriggerField
43348  * An input with International dial-code selection
43349  
43350  * @cfg {String} defaultDialCode default '+852'
43351  * @cfg {Array} preferedCountries default []
43352   
43353  * @constructor
43354  * Create a new PhoneInput.
43355  * @param {Object} config Configuration options
43356  */
43357
43358 Roo.bootstrap.form.PhoneInput = function(config) {
43359     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43360 };
43361
43362 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43363         /**
43364         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43365         */
43366         listWidth: undefined,
43367         
43368         selectedClass: 'active',
43369         
43370         invalidClass : "has-warning",
43371         
43372         validClass: 'has-success',
43373         
43374         allowed: '0123456789',
43375         
43376         max_length: 15,
43377         
43378         /**
43379          * @cfg {String} defaultDialCode The default dial code when initializing the input
43380          */
43381         defaultDialCode: '+852',
43382         
43383         /**
43384          * @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
43385          */
43386         preferedCountries: false,
43387         
43388         getAutoCreate : function()
43389         {
43390             var data = Roo.bootstrap.form.PhoneInputData();
43391             var align = this.labelAlign || this.parentLabelAlign();
43392             var id = Roo.id();
43393             
43394             this.allCountries = [];
43395             this.dialCodeMapping = [];
43396             
43397             for (var i = 0; i < data.length; i++) {
43398               var c = data[i];
43399               this.allCountries[i] = {
43400                 name: c[0],
43401                 iso2: c[1],
43402                 dialCode: c[2],
43403                 priority: c[3] || 0,
43404                 areaCodes: c[4] || null
43405               };
43406               this.dialCodeMapping[c[2]] = {
43407                   name: c[0],
43408                   iso2: c[1],
43409                   priority: c[3] || 0,
43410                   areaCodes: c[4] || null
43411               };
43412             }
43413             
43414             var cfg = {
43415                 cls: 'form-group',
43416                 cn: []
43417             };
43418             
43419             var input =  {
43420                 tag: 'input',
43421                 id : id,
43422                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43423                 maxlength: this.max_length,
43424                 cls : 'form-control tel-input',
43425                 autocomplete: 'new-password'
43426             };
43427             
43428             var hiddenInput = {
43429                 tag: 'input',
43430                 type: 'hidden',
43431                 cls: 'hidden-tel-input'
43432             };
43433             
43434             if (this.name) {
43435                 hiddenInput.name = this.name;
43436             }
43437             
43438             if (this.disabled) {
43439                 input.disabled = true;
43440             }
43441             
43442             var flag_container = {
43443                 tag: 'div',
43444                 cls: 'flag-box',
43445                 cn: [
43446                     {
43447                         tag: 'div',
43448                         cls: 'flag'
43449                     },
43450                     {
43451                         tag: 'div',
43452                         cls: 'caret'
43453                     }
43454                 ]
43455             };
43456             
43457             var box = {
43458                 tag: 'div',
43459                 cls: this.hasFeedback ? 'has-feedback' : '',
43460                 cn: [
43461                     hiddenInput,
43462                     input,
43463                     {
43464                         tag: 'input',
43465                         cls: 'dial-code-holder',
43466                         disabled: true
43467                     }
43468                 ]
43469             };
43470             
43471             var container = {
43472                 cls: 'roo-select2-container input-group',
43473                 cn: [
43474                     flag_container,
43475                     box
43476                 ]
43477             };
43478             
43479             if (this.fieldLabel.length) {
43480                 var indicator = {
43481                     tag: 'i',
43482                     tooltip: 'This field is required'
43483                 };
43484                 
43485                 var label = {
43486                     tag: 'label',
43487                     'for':  id,
43488                     cls: 'control-label',
43489                     cn: []
43490                 };
43491                 
43492                 var label_text = {
43493                     tag: 'span',
43494                     html: this.fieldLabel
43495                 };
43496                 
43497                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43498                 label.cn = [
43499                     indicator,
43500                     label_text
43501                 ];
43502                 
43503                 if(this.indicatorpos == 'right') {
43504                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43505                     label.cn = [
43506                         label_text,
43507                         indicator
43508                     ];
43509                 }
43510                 
43511                 if(align == 'left') {
43512                     container = {
43513                         tag: 'div',
43514                         cn: [
43515                             container
43516                         ]
43517                     };
43518                     
43519                     if(this.labelWidth > 12){
43520                         label.style = "width: " + this.labelWidth + 'px';
43521                     }
43522                     if(this.labelWidth < 13 && this.labelmd == 0){
43523                         this.labelmd = this.labelWidth;
43524                     }
43525                     if(this.labellg > 0){
43526                         label.cls += ' col-lg-' + this.labellg;
43527                         input.cls += ' col-lg-' + (12 - this.labellg);
43528                     }
43529                     if(this.labelmd > 0){
43530                         label.cls += ' col-md-' + this.labelmd;
43531                         container.cls += ' col-md-' + (12 - this.labelmd);
43532                     }
43533                     if(this.labelsm > 0){
43534                         label.cls += ' col-sm-' + this.labelsm;
43535                         container.cls += ' col-sm-' + (12 - this.labelsm);
43536                     }
43537                     if(this.labelxs > 0){
43538                         label.cls += ' col-xs-' + this.labelxs;
43539                         container.cls += ' col-xs-' + (12 - this.labelxs);
43540                     }
43541                 }
43542             }
43543             
43544             cfg.cn = [
43545                 label,
43546                 container
43547             ];
43548             
43549             var settings = this;
43550             
43551             ['xs','sm','md','lg'].map(function(size){
43552                 if (settings[size]) {
43553                     cfg.cls += ' col-' + size + '-' + settings[size];
43554                 }
43555             });
43556             
43557             this.store = new Roo.data.Store({
43558                 proxy : new Roo.data.MemoryProxy({}),
43559                 reader : new Roo.data.JsonReader({
43560                     fields : [
43561                         {
43562                             'name' : 'name',
43563                             'type' : 'string'
43564                         },
43565                         {
43566                             'name' : 'iso2',
43567                             'type' : 'string'
43568                         },
43569                         {
43570                             'name' : 'dialCode',
43571                             'type' : 'string'
43572                         },
43573                         {
43574                             'name' : 'priority',
43575                             'type' : 'string'
43576                         },
43577                         {
43578                             'name' : 'areaCodes',
43579                             'type' : 'string'
43580                         }
43581                     ]
43582                 })
43583             });
43584             
43585             if(!this.preferedCountries) {
43586                 this.preferedCountries = [
43587                     'hk',
43588                     'gb',
43589                     'us'
43590                 ];
43591             }
43592             
43593             var p = this.preferedCountries.reverse();
43594             
43595             if(p) {
43596                 for (var i = 0; i < p.length; i++) {
43597                     for (var j = 0; j < this.allCountries.length; j++) {
43598                         if(this.allCountries[j].iso2 == p[i]) {
43599                             var t = this.allCountries[j];
43600                             this.allCountries.splice(j,1);
43601                             this.allCountries.unshift(t);
43602                         }
43603                     } 
43604                 }
43605             }
43606             
43607             this.store.proxy.data = {
43608                 success: true,
43609                 data: this.allCountries
43610             };
43611             
43612             return cfg;
43613         },
43614         
43615         initEvents : function()
43616         {
43617             this.createList();
43618             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43619             
43620             this.indicator = this.indicatorEl();
43621             this.flag = this.flagEl();
43622             this.dialCodeHolder = this.dialCodeHolderEl();
43623             
43624             this.trigger = this.el.select('div.flag-box',true).first();
43625             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43626             
43627             var _this = this;
43628             
43629             (function(){
43630                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43631                 _this.list.setWidth(lw);
43632             }).defer(100);
43633             
43634             this.list.on('mouseover', this.onViewOver, this);
43635             this.list.on('mousemove', this.onViewMove, this);
43636             this.inputEl().on("keyup", this.onKeyUp, this);
43637             this.inputEl().on("keypress", this.onKeyPress, this);
43638             
43639             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43640
43641             this.view = new Roo.View(this.list, this.tpl, {
43642                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43643             });
43644             
43645             this.view.on('click', this.onViewClick, this);
43646             this.setValue(this.defaultDialCode);
43647         },
43648         
43649         onTriggerClick : function(e)
43650         {
43651             Roo.log('trigger click');
43652             if(this.disabled){
43653                 return;
43654             }
43655             
43656             if(this.isExpanded()){
43657                 this.collapse();
43658                 this.hasFocus = false;
43659             }else {
43660                 this.store.load({});
43661                 this.hasFocus = true;
43662                 this.expand();
43663             }
43664         },
43665         
43666         isExpanded : function()
43667         {
43668             return this.list.isVisible();
43669         },
43670         
43671         collapse : function()
43672         {
43673             if(!this.isExpanded()){
43674                 return;
43675             }
43676             this.list.hide();
43677             Roo.get(document).un('mousedown', this.collapseIf, this);
43678             Roo.get(document).un('mousewheel', this.collapseIf, this);
43679             this.fireEvent('collapse', this);
43680             this.validate();
43681         },
43682         
43683         expand : function()
43684         {
43685             Roo.log('expand');
43686
43687             if(this.isExpanded() || !this.hasFocus){
43688                 return;
43689             }
43690             
43691             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43692             this.list.setWidth(lw);
43693             
43694             this.list.show();
43695             this.restrictHeight();
43696             
43697             Roo.get(document).on('mousedown', this.collapseIf, this);
43698             Roo.get(document).on('mousewheel', this.collapseIf, this);
43699             
43700             this.fireEvent('expand', this);
43701         },
43702         
43703         restrictHeight : function()
43704         {
43705             this.list.alignTo(this.inputEl(), this.listAlign);
43706             this.list.alignTo(this.inputEl(), this.listAlign);
43707         },
43708         
43709         onViewOver : function(e, t)
43710         {
43711             if(this.inKeyMode){
43712                 return;
43713             }
43714             var item = this.view.findItemFromChild(t);
43715             
43716             if(item){
43717                 var index = this.view.indexOf(item);
43718                 this.select(index, false);
43719             }
43720         },
43721
43722         // private
43723         onViewClick : function(view, doFocus, el, e)
43724         {
43725             var index = this.view.getSelectedIndexes()[0];
43726             
43727             var r = this.store.getAt(index);
43728             
43729             if(r){
43730                 this.onSelect(r, index);
43731             }
43732             if(doFocus !== false && !this.blockFocus){
43733                 this.inputEl().focus();
43734             }
43735         },
43736         
43737         onViewMove : function(e, t)
43738         {
43739             this.inKeyMode = false;
43740         },
43741         
43742         select : function(index, scrollIntoView)
43743         {
43744             this.selectedIndex = index;
43745             this.view.select(index);
43746             if(scrollIntoView !== false){
43747                 var el = this.view.getNode(index);
43748                 if(el){
43749                     this.list.scrollChildIntoView(el, false);
43750                 }
43751             }
43752         },
43753         
43754         createList : function()
43755         {
43756             this.list = Roo.get(document.body).createChild({
43757                 tag: 'ul',
43758                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43759                 style: 'display:none'
43760             });
43761             
43762             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43763         },
43764         
43765         collapseIf : function(e)
43766         {
43767             var in_combo  = e.within(this.el);
43768             var in_list =  e.within(this.list);
43769             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43770             
43771             if (in_combo || in_list || is_list) {
43772                 return;
43773             }
43774             this.collapse();
43775         },
43776         
43777         onSelect : function(record, index)
43778         {
43779             if(this.fireEvent('beforeselect', this, record, index) !== false){
43780                 
43781                 this.setFlagClass(record.data.iso2);
43782                 this.setDialCode(record.data.dialCode);
43783                 this.hasFocus = false;
43784                 this.collapse();
43785                 this.fireEvent('select', this, record, index);
43786             }
43787         },
43788         
43789         flagEl : function()
43790         {
43791             var flag = this.el.select('div.flag',true).first();
43792             if(!flag){
43793                 return false;
43794             }
43795             return flag;
43796         },
43797         
43798         dialCodeHolderEl : function()
43799         {
43800             var d = this.el.select('input.dial-code-holder',true).first();
43801             if(!d){
43802                 return false;
43803             }
43804             return d;
43805         },
43806         
43807         setDialCode : function(v)
43808         {
43809             this.dialCodeHolder.dom.value = '+'+v;
43810         },
43811         
43812         setFlagClass : function(n)
43813         {
43814             this.flag.dom.className = 'flag '+n;
43815         },
43816         
43817         getValue : function()
43818         {
43819             var v = this.inputEl().getValue();
43820             if(this.dialCodeHolder) {
43821                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43822             }
43823             return v;
43824         },
43825         
43826         setValue : function(v)
43827         {
43828             var d = this.getDialCode(v);
43829             
43830             //invalid dial code
43831             if(v.length == 0 || !d || d.length == 0) {
43832                 if(this.rendered){
43833                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43834                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43835                 }
43836                 return;
43837             }
43838             
43839             //valid dial code
43840             this.setFlagClass(this.dialCodeMapping[d].iso2);
43841             this.setDialCode(d);
43842             this.inputEl().dom.value = v.replace('+'+d,'');
43843             this.hiddenEl().dom.value = this.getValue();
43844             
43845             this.validate();
43846         },
43847         
43848         getDialCode : function(v)
43849         {
43850             v = v ||  '';
43851             
43852             if (v.length == 0) {
43853                 return this.dialCodeHolder.dom.value;
43854             }
43855             
43856             var dialCode = "";
43857             if (v.charAt(0) != "+") {
43858                 return false;
43859             }
43860             var numericChars = "";
43861             for (var i = 1; i < v.length; i++) {
43862               var c = v.charAt(i);
43863               if (!isNaN(c)) {
43864                 numericChars += c;
43865                 if (this.dialCodeMapping[numericChars]) {
43866                   dialCode = v.substr(1, i);
43867                 }
43868                 if (numericChars.length == 4) {
43869                   break;
43870                 }
43871               }
43872             }
43873             return dialCode;
43874         },
43875         
43876         reset : function()
43877         {
43878             this.setValue(this.defaultDialCode);
43879             this.validate();
43880         },
43881         
43882         hiddenEl : function()
43883         {
43884             return this.el.select('input.hidden-tel-input',true).first();
43885         },
43886         
43887         // after setting val
43888         onKeyUp : function(e){
43889             this.setValue(this.getValue());
43890         },
43891         
43892         onKeyPress : function(e){
43893             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43894                 e.stopEvent();
43895             }
43896         }
43897         
43898 });
43899 /**
43900  * @class Roo.bootstrap.form.MoneyField
43901  * @extends Roo.bootstrap.form.ComboBox
43902  * Bootstrap MoneyField class
43903  * 
43904  * @constructor
43905  * Create a new MoneyField.
43906  * @param {Object} config Configuration options
43907  */
43908
43909 Roo.bootstrap.form.MoneyField = function(config) {
43910     
43911     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43912     
43913 };
43914
43915 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43916     
43917     /**
43918      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43919      */
43920     allowDecimals : true,
43921     /**
43922      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43923      */
43924     decimalSeparator : ".",
43925     /**
43926      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43927      */
43928     decimalPrecision : 0,
43929     /**
43930      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43931      */
43932     allowNegative : true,
43933     /**
43934      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43935      */
43936     allowZero: true,
43937     /**
43938      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43939      */
43940     minValue : Number.NEGATIVE_INFINITY,
43941     /**
43942      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43943      */
43944     maxValue : Number.MAX_VALUE,
43945     /**
43946      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43947      */
43948     minText : "The minimum value for this field is {0}",
43949     /**
43950      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43951      */
43952     maxText : "The maximum value for this field is {0}",
43953     /**
43954      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43955      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43956      */
43957     nanText : "{0} is not a valid number",
43958     /**
43959      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43960      */
43961     castInt : true,
43962     /**
43963      * @cfg {String} defaults currency of the MoneyField
43964      * value should be in lkey
43965      */
43966     defaultCurrency : false,
43967     /**
43968      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43969      */
43970     thousandsDelimiter : false,
43971     /**
43972      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43973      */
43974     max_length: false,
43975     
43976     inputlg : 9,
43977     inputmd : 9,
43978     inputsm : 9,
43979     inputxs : 6,
43980      /**
43981      * @cfg {Roo.data.Store} store  Store to lookup currency??
43982      */
43983     store : false,
43984     
43985     getAutoCreate : function()
43986     {
43987         var align = this.labelAlign || this.parentLabelAlign();
43988         
43989         var id = Roo.id();
43990
43991         var cfg = {
43992             cls: 'form-group',
43993             cn: []
43994         };
43995
43996         var input =  {
43997             tag: 'input',
43998             id : id,
43999             cls : 'form-control roo-money-amount-input',
44000             autocomplete: 'new-password'
44001         };
44002         
44003         var hiddenInput = {
44004             tag: 'input',
44005             type: 'hidden',
44006             id: Roo.id(),
44007             cls: 'hidden-number-input'
44008         };
44009         
44010         if(this.max_length) {
44011             input.maxlength = this.max_length; 
44012         }
44013         
44014         if (this.name) {
44015             hiddenInput.name = this.name;
44016         }
44017
44018         if (this.disabled) {
44019             input.disabled = true;
44020         }
44021
44022         var clg = 12 - this.inputlg;
44023         var cmd = 12 - this.inputmd;
44024         var csm = 12 - this.inputsm;
44025         var cxs = 12 - this.inputxs;
44026         
44027         var container = {
44028             tag : 'div',
44029             cls : 'row roo-money-field',
44030             cn : [
44031                 {
44032                     tag : 'div',
44033                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44034                     cn : [
44035                         {
44036                             tag : 'div',
44037                             cls: 'roo-select2-container input-group',
44038                             cn: [
44039                                 {
44040                                     tag : 'input',
44041                                     cls : 'form-control roo-money-currency-input',
44042                                     autocomplete: 'new-password',
44043                                     readOnly : 1,
44044                                     name : this.currencyName
44045                                 },
44046                                 {
44047                                     tag :'span',
44048                                     cls : 'input-group-addon',
44049                                     cn : [
44050                                         {
44051                                             tag: 'span',
44052                                             cls: 'caret'
44053                                         }
44054                                     ]
44055                                 }
44056                             ]
44057                         }
44058                     ]
44059                 },
44060                 {
44061                     tag : 'div',
44062                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44063                     cn : [
44064                         {
44065                             tag: 'div',
44066                             cls: this.hasFeedback ? 'has-feedback' : '',
44067                             cn: [
44068                                 input
44069                             ]
44070                         }
44071                     ]
44072                 }
44073             ]
44074             
44075         };
44076         
44077         if (this.fieldLabel.length) {
44078             var indicator = {
44079                 tag: 'i',
44080                 tooltip: 'This field is required'
44081             };
44082
44083             var label = {
44084                 tag: 'label',
44085                 'for':  id,
44086                 cls: 'control-label',
44087                 cn: []
44088             };
44089
44090             var label_text = {
44091                 tag: 'span',
44092                 html: this.fieldLabel
44093             };
44094
44095             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44096             label.cn = [
44097                 indicator,
44098                 label_text
44099             ];
44100
44101             if(this.indicatorpos == 'right') {
44102                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44103                 label.cn = [
44104                     label_text,
44105                     indicator
44106                 ];
44107             }
44108
44109             if(align == 'left') {
44110                 container = {
44111                     tag: 'div',
44112                     cn: [
44113                         container
44114                     ]
44115                 };
44116
44117                 if(this.labelWidth > 12){
44118                     label.style = "width: " + this.labelWidth + 'px';
44119                 }
44120                 if(this.labelWidth < 13 && this.labelmd == 0){
44121                     this.labelmd = this.labelWidth;
44122                 }
44123                 if(this.labellg > 0){
44124                     label.cls += ' col-lg-' + this.labellg;
44125                     input.cls += ' col-lg-' + (12 - this.labellg);
44126                 }
44127                 if(this.labelmd > 0){
44128                     label.cls += ' col-md-' + this.labelmd;
44129                     container.cls += ' col-md-' + (12 - this.labelmd);
44130                 }
44131                 if(this.labelsm > 0){
44132                     label.cls += ' col-sm-' + this.labelsm;
44133                     container.cls += ' col-sm-' + (12 - this.labelsm);
44134                 }
44135                 if(this.labelxs > 0){
44136                     label.cls += ' col-xs-' + this.labelxs;
44137                     container.cls += ' col-xs-' + (12 - this.labelxs);
44138                 }
44139             }
44140         }
44141
44142         cfg.cn = [
44143             label,
44144             container,
44145             hiddenInput
44146         ];
44147         
44148         var settings = this;
44149
44150         ['xs','sm','md','lg'].map(function(size){
44151             if (settings[size]) {
44152                 cfg.cls += ' col-' + size + '-' + settings[size];
44153             }
44154         });
44155         
44156         return cfg;
44157     },
44158     
44159     initEvents : function()
44160     {
44161         this.indicator = this.indicatorEl();
44162         
44163         this.initCurrencyEvent();
44164         
44165         this.initNumberEvent();
44166     },
44167     
44168     initCurrencyEvent : function()
44169     {
44170         if (!this.store) {
44171             throw "can not find store for combo";
44172         }
44173         
44174         this.store = Roo.factory(this.store, Roo.data);
44175         this.store.parent = this;
44176         
44177         this.createList();
44178         
44179         this.triggerEl = this.el.select('.input-group-addon', true).first();
44180         
44181         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44182         
44183         var _this = this;
44184         
44185         (function(){
44186             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44187             _this.list.setWidth(lw);
44188         }).defer(100);
44189         
44190         this.list.on('mouseover', this.onViewOver, this);
44191         this.list.on('mousemove', this.onViewMove, this);
44192         this.list.on('scroll', this.onViewScroll, this);
44193         
44194         if(!this.tpl){
44195             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44196         }
44197         
44198         this.view = new Roo.View(this.list, this.tpl, {
44199             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44200         });
44201         
44202         this.view.on('click', this.onViewClick, this);
44203         
44204         this.store.on('beforeload', this.onBeforeLoad, this);
44205         this.store.on('load', this.onLoad, this);
44206         this.store.on('loadexception', this.onLoadException, this);
44207         
44208         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44209             "up" : function(e){
44210                 this.inKeyMode = true;
44211                 this.selectPrev();
44212             },
44213
44214             "down" : function(e){
44215                 if(!this.isExpanded()){
44216                     this.onTriggerClick();
44217                 }else{
44218                     this.inKeyMode = true;
44219                     this.selectNext();
44220                 }
44221             },
44222
44223             "enter" : function(e){
44224                 this.collapse();
44225                 
44226                 if(this.fireEvent("specialkey", this, e)){
44227                     this.onViewClick(false);
44228                 }
44229                 
44230                 return true;
44231             },
44232
44233             "esc" : function(e){
44234                 this.collapse();
44235             },
44236
44237             "tab" : function(e){
44238                 this.collapse();
44239                 
44240                 if(this.fireEvent("specialkey", this, e)){
44241                     this.onViewClick(false);
44242                 }
44243                 
44244                 return true;
44245             },
44246
44247             scope : this,
44248
44249             doRelay : function(foo, bar, hname){
44250                 if(hname == 'down' || this.scope.isExpanded()){
44251                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44252                 }
44253                 return true;
44254             },
44255
44256             forceKeyDown: true
44257         });
44258         
44259         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44260         
44261     },
44262     
44263     initNumberEvent : function(e)
44264     {
44265         this.inputEl().on("keydown" , this.fireKey,  this);
44266         this.inputEl().on("focus", this.onFocus,  this);
44267         this.inputEl().on("blur", this.onBlur,  this);
44268         
44269         this.inputEl().relayEvent('keyup', this);
44270         
44271         if(this.indicator){
44272             this.indicator.addClass('invisible');
44273         }
44274  
44275         this.originalValue = this.getValue();
44276         
44277         if(this.validationEvent == 'keyup'){
44278             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44279             this.inputEl().on('keyup', this.filterValidation, this);
44280         }
44281         else if(this.validationEvent !== false){
44282             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44283         }
44284         
44285         if(this.selectOnFocus){
44286             this.on("focus", this.preFocus, this);
44287             
44288         }
44289         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44290             this.inputEl().on("keypress", this.filterKeys, this);
44291         } else {
44292             this.inputEl().relayEvent('keypress', this);
44293         }
44294         
44295         var allowed = "0123456789";
44296         
44297         if(this.allowDecimals){
44298             allowed += this.decimalSeparator;
44299         }
44300         
44301         if(this.allowNegative){
44302             allowed += "-";
44303         }
44304         
44305         if(this.thousandsDelimiter) {
44306             allowed += ",";
44307         }
44308         
44309         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44310         
44311         var keyPress = function(e){
44312             
44313             var k = e.getKey();
44314             
44315             var c = e.getCharCode();
44316             
44317             if(
44318                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44319                     allowed.indexOf(String.fromCharCode(c)) === -1
44320             ){
44321                 e.stopEvent();
44322                 return;
44323             }
44324             
44325             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44326                 return;
44327             }
44328             
44329             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44330                 e.stopEvent();
44331             }
44332         };
44333         
44334         this.inputEl().on("keypress", keyPress, this);
44335         
44336     },
44337     
44338     onTriggerClick : function(e)
44339     {   
44340         if(this.disabled){
44341             return;
44342         }
44343         
44344         this.page = 0;
44345         this.loadNext = false;
44346         
44347         if(this.isExpanded()){
44348             this.collapse();
44349             return;
44350         }
44351         
44352         this.hasFocus = true;
44353         
44354         if(this.triggerAction == 'all') {
44355             this.doQuery(this.allQuery, true);
44356             return;
44357         }
44358         
44359         this.doQuery(this.getRawValue());
44360     },
44361     
44362     getCurrency : function()
44363     {   
44364         var v = this.currencyEl().getValue();
44365         
44366         return v;
44367     },
44368     
44369     restrictHeight : function()
44370     {
44371         this.list.alignTo(this.currencyEl(), this.listAlign);
44372         this.list.alignTo(this.currencyEl(), this.listAlign);
44373     },
44374     
44375     onViewClick : function(view, doFocus, el, e)
44376     {
44377         var index = this.view.getSelectedIndexes()[0];
44378         
44379         var r = this.store.getAt(index);
44380         
44381         if(r){
44382             this.onSelect(r, index);
44383         }
44384     },
44385     
44386     onSelect : function(record, index){
44387         
44388         if(this.fireEvent('beforeselect', this, record, index) !== false){
44389         
44390             this.setFromCurrencyData(index > -1 ? record.data : false);
44391             
44392             this.collapse();
44393             
44394             this.fireEvent('select', this, record, index);
44395         }
44396     },
44397     
44398     setFromCurrencyData : function(o)
44399     {
44400         var currency = '';
44401         
44402         this.lastCurrency = o;
44403         
44404         if (this.currencyField) {
44405             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44406         } else {
44407             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44408         }
44409         
44410         this.lastSelectionText = currency;
44411         
44412         //setting default currency
44413         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44414             this.setCurrency(this.defaultCurrency);
44415             return;
44416         }
44417         
44418         this.setCurrency(currency);
44419     },
44420     
44421     setFromData : function(o)
44422     {
44423         var c = {};
44424         
44425         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44426         
44427         this.setFromCurrencyData(c);
44428         
44429         var value = '';
44430         
44431         if (this.name) {
44432             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44433         } else {
44434             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44435         }
44436         
44437         this.setValue(value);
44438         
44439     },
44440     
44441     setCurrency : function(v)
44442     {   
44443         this.currencyValue = v;
44444         
44445         if(this.rendered){
44446             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44447             this.validate();
44448         }
44449     },
44450     
44451     setValue : function(v)
44452     {
44453         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44454         
44455         this.value = v;
44456         
44457         if(this.rendered){
44458             
44459             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44460             
44461             this.inputEl().dom.value = (v == '') ? '' :
44462                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44463             
44464             if(!this.allowZero && v === '0') {
44465                 this.hiddenEl().dom.value = '';
44466                 this.inputEl().dom.value = '';
44467             }
44468             
44469             this.validate();
44470         }
44471     },
44472     
44473     getRawValue : function()
44474     {
44475         var v = this.inputEl().getValue();
44476         
44477         return v;
44478     },
44479     
44480     getValue : function()
44481     {
44482         return this.fixPrecision(this.parseValue(this.getRawValue()));
44483     },
44484     
44485     parseValue : function(value)
44486     {
44487         if(this.thousandsDelimiter) {
44488             value += "";
44489             r = new RegExp(",", "g");
44490             value = value.replace(r, "");
44491         }
44492         
44493         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44494         return isNaN(value) ? '' : value;
44495         
44496     },
44497     
44498     fixPrecision : function(value)
44499     {
44500         if(this.thousandsDelimiter) {
44501             value += "";
44502             r = new RegExp(",", "g");
44503             value = value.replace(r, "");
44504         }
44505         
44506         var nan = isNaN(value);
44507         
44508         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44509             return nan ? '' : value;
44510         }
44511         return parseFloat(value).toFixed(this.decimalPrecision);
44512     },
44513     
44514     decimalPrecisionFcn : function(v)
44515     {
44516         return Math.floor(v);
44517     },
44518     
44519     validateValue : function(value)
44520     {
44521         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44522             return false;
44523         }
44524         
44525         var num = this.parseValue(value);
44526         
44527         if(isNaN(num)){
44528             this.markInvalid(String.format(this.nanText, value));
44529             return false;
44530         }
44531         
44532         if(num < this.minValue){
44533             this.markInvalid(String.format(this.minText, this.minValue));
44534             return false;
44535         }
44536         
44537         if(num > this.maxValue){
44538             this.markInvalid(String.format(this.maxText, this.maxValue));
44539             return false;
44540         }
44541         
44542         return true;
44543     },
44544     
44545     validate : function()
44546     {
44547         if(this.disabled || this.allowBlank){
44548             this.markValid();
44549             return true;
44550         }
44551         
44552         var currency = this.getCurrency();
44553         
44554         if(this.validateValue(this.getRawValue()) && currency.length){
44555             this.markValid();
44556             return true;
44557         }
44558         
44559         this.markInvalid();
44560         return false;
44561     },
44562     
44563     getName: function()
44564     {
44565         return this.name;
44566     },
44567     
44568     beforeBlur : function()
44569     {
44570         if(!this.castInt){
44571             return;
44572         }
44573         
44574         var v = this.parseValue(this.getRawValue());
44575         
44576         if(v || v == 0){
44577             this.setValue(v);
44578         }
44579     },
44580     
44581     onBlur : function()
44582     {
44583         this.beforeBlur();
44584         
44585         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44586             //this.el.removeClass(this.focusClass);
44587         }
44588         
44589         this.hasFocus = false;
44590         
44591         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44592             this.validate();
44593         }
44594         
44595         var v = this.getValue();
44596         
44597         if(String(v) !== String(this.startValue)){
44598             this.fireEvent('change', this, v, this.startValue);
44599         }
44600         
44601         this.fireEvent("blur", this);
44602     },
44603     
44604     inputEl : function()
44605     {
44606         return this.el.select('.roo-money-amount-input', true).first();
44607     },
44608     
44609     currencyEl : function()
44610     {
44611         return this.el.select('.roo-money-currency-input', true).first();
44612     },
44613     
44614     hiddenEl : function()
44615     {
44616         return this.el.select('input.hidden-number-input',true).first();
44617     }
44618     
44619 });/**
44620  * @class Roo.bootstrap.BezierSignature
44621  * @extends Roo.bootstrap.Component
44622  * Bootstrap BezierSignature class
44623  * This script refer to:
44624  *    Title: Signature Pad
44625  *    Author: szimek
44626  *    Availability: https://github.com/szimek/signature_pad
44627  *
44628  * @constructor
44629  * Create a new BezierSignature
44630  * @param {Object} config The config object
44631  */
44632
44633 Roo.bootstrap.BezierSignature = function(config){
44634     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44635     this.addEvents({
44636         "resize" : true
44637     });
44638 };
44639
44640 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44641 {
44642      
44643     curve_data: [],
44644     
44645     is_empty: true,
44646     
44647     mouse_btn_down: true,
44648     
44649     /**
44650      * @cfg {int} canvas height
44651      */
44652     canvas_height: '200px',
44653     
44654     /**
44655      * @cfg {float|function} Radius of a single dot.
44656      */ 
44657     dot_size: false,
44658     
44659     /**
44660      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44661      */
44662     min_width: 0.5,
44663     
44664     /**
44665      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44666      */
44667     max_width: 2.5,
44668     
44669     /**
44670      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44671      */
44672     throttle: 16,
44673     
44674     /**
44675      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44676      */
44677     min_distance: 5,
44678     
44679     /**
44680      * @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.
44681      */
44682     bg_color: 'rgba(0, 0, 0, 0)',
44683     
44684     /**
44685      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44686      */
44687     dot_color: 'black',
44688     
44689     /**
44690      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44691      */ 
44692     velocity_filter_weight: 0.7,
44693     
44694     /**
44695      * @cfg {function} Callback when stroke begin. 
44696      */
44697     onBegin: false,
44698     
44699     /**
44700      * @cfg {function} Callback when stroke end.
44701      */
44702     onEnd: false,
44703     
44704     getAutoCreate : function()
44705     {
44706         var cls = 'roo-signature column';
44707         
44708         if(this.cls){
44709             cls += ' ' + this.cls;
44710         }
44711         
44712         var col_sizes = [
44713             'lg',
44714             'md',
44715             'sm',
44716             'xs'
44717         ];
44718         
44719         for(var i = 0; i < col_sizes.length; i++) {
44720             if(this[col_sizes[i]]) {
44721                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44722             }
44723         }
44724         
44725         var cfg = {
44726             tag: 'div',
44727             cls: cls,
44728             cn: [
44729                 {
44730                     tag: 'div',
44731                     cls: 'roo-signature-body',
44732                     cn: [
44733                         {
44734                             tag: 'canvas',
44735                             cls: 'roo-signature-body-canvas',
44736                             height: this.canvas_height,
44737                             width: this.canvas_width
44738                         }
44739                     ]
44740                 },
44741                 {
44742                     tag: 'input',
44743                     type: 'file',
44744                     style: 'display: none'
44745                 }
44746             ]
44747         };
44748         
44749         return cfg;
44750     },
44751     
44752     initEvents: function() 
44753     {
44754         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44755         
44756         var canvas = this.canvasEl();
44757         
44758         // mouse && touch event swapping...
44759         canvas.dom.style.touchAction = 'none';
44760         canvas.dom.style.msTouchAction = 'none';
44761         
44762         this.mouse_btn_down = false;
44763         canvas.on('mousedown', this._handleMouseDown, this);
44764         canvas.on('mousemove', this._handleMouseMove, this);
44765         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44766         
44767         if (window.PointerEvent) {
44768             canvas.on('pointerdown', this._handleMouseDown, this);
44769             canvas.on('pointermove', this._handleMouseMove, this);
44770             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44771         }
44772         
44773         if ('ontouchstart' in window) {
44774             canvas.on('touchstart', this._handleTouchStart, this);
44775             canvas.on('touchmove', this._handleTouchMove, this);
44776             canvas.on('touchend', this._handleTouchEnd, this);
44777         }
44778         
44779         Roo.EventManager.onWindowResize(this.resize, this, true);
44780         
44781         // file input event
44782         this.fileEl().on('change', this.uploadImage, this);
44783         
44784         this.clear();
44785         
44786         this.resize();
44787     },
44788     
44789     resize: function(){
44790         
44791         var canvas = this.canvasEl().dom;
44792         var ctx = this.canvasElCtx();
44793         var img_data = false;
44794         
44795         if(canvas.width > 0) {
44796             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44797         }
44798         // setting canvas width will clean img data
44799         canvas.width = 0;
44800         
44801         var style = window.getComputedStyle ? 
44802             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44803             
44804         var padding_left = parseInt(style.paddingLeft) || 0;
44805         var padding_right = parseInt(style.paddingRight) || 0;
44806         
44807         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44808         
44809         if(img_data) {
44810             ctx.putImageData(img_data, 0, 0);
44811         }
44812     },
44813     
44814     _handleMouseDown: function(e)
44815     {
44816         if (e.browserEvent.which === 1) {
44817             this.mouse_btn_down = true;
44818             this.strokeBegin(e);
44819         }
44820     },
44821     
44822     _handleMouseMove: function (e)
44823     {
44824         if (this.mouse_btn_down) {
44825             this.strokeMoveUpdate(e);
44826         }
44827     },
44828     
44829     _handleMouseUp: function (e)
44830     {
44831         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44832             this.mouse_btn_down = false;
44833             this.strokeEnd(e);
44834         }
44835     },
44836     
44837     _handleTouchStart: function (e) {
44838         
44839         e.preventDefault();
44840         if (e.browserEvent.targetTouches.length === 1) {
44841             // var touch = e.browserEvent.changedTouches[0];
44842             // this.strokeBegin(touch);
44843             
44844              this.strokeBegin(e); // assume e catching the correct xy...
44845         }
44846     },
44847     
44848     _handleTouchMove: function (e) {
44849         e.preventDefault();
44850         // var touch = event.targetTouches[0];
44851         // _this._strokeMoveUpdate(touch);
44852         this.strokeMoveUpdate(e);
44853     },
44854     
44855     _handleTouchEnd: function (e) {
44856         var wasCanvasTouched = e.target === this.canvasEl().dom;
44857         if (wasCanvasTouched) {
44858             e.preventDefault();
44859             // var touch = event.changedTouches[0];
44860             // _this._strokeEnd(touch);
44861             this.strokeEnd(e);
44862         }
44863     },
44864     
44865     reset: function () {
44866         this._lastPoints = [];
44867         this._lastVelocity = 0;
44868         this._lastWidth = (this.min_width + this.max_width) / 2;
44869         this.canvasElCtx().fillStyle = this.dot_color;
44870     },
44871     
44872     strokeMoveUpdate: function(e)
44873     {
44874         this.strokeUpdate(e);
44875         
44876         if (this.throttle) {
44877             this.throttleStroke(this.strokeUpdate, this.throttle);
44878         }
44879         else {
44880             this.strokeUpdate(e);
44881         }
44882     },
44883     
44884     strokeBegin: function(e)
44885     {
44886         var newPointGroup = {
44887             color: this.dot_color,
44888             points: []
44889         };
44890         
44891         if (typeof this.onBegin === 'function') {
44892             this.onBegin(e);
44893         }
44894         
44895         this.curve_data.push(newPointGroup);
44896         this.reset();
44897         this.strokeUpdate(e);
44898     },
44899     
44900     strokeUpdate: function(e)
44901     {
44902         var rect = this.canvasEl().dom.getBoundingClientRect();
44903         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44904         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44905         var lastPoints = lastPointGroup.points;
44906         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44907         var isLastPointTooClose = lastPoint
44908             ? point.distanceTo(lastPoint) <= this.min_distance
44909             : false;
44910         var color = lastPointGroup.color;
44911         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44912             var curve = this.addPoint(point);
44913             if (!lastPoint) {
44914                 this.drawDot({color: color, point: point});
44915             }
44916             else if (curve) {
44917                 this.drawCurve({color: color, curve: curve});
44918             }
44919             lastPoints.push({
44920                 time: point.time,
44921                 x: point.x,
44922                 y: point.y
44923             });
44924         }
44925     },
44926     
44927     strokeEnd: function(e)
44928     {
44929         this.strokeUpdate(e);
44930         if (typeof this.onEnd === 'function') {
44931             this.onEnd(e);
44932         }
44933     },
44934     
44935     addPoint:  function (point) {
44936         var _lastPoints = this._lastPoints;
44937         _lastPoints.push(point);
44938         if (_lastPoints.length > 2) {
44939             if (_lastPoints.length === 3) {
44940                 _lastPoints.unshift(_lastPoints[0]);
44941             }
44942             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44943             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44944             _lastPoints.shift();
44945             return curve;
44946         }
44947         return null;
44948     },
44949     
44950     calculateCurveWidths: function (startPoint, endPoint) {
44951         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44952             (1 - this.velocity_filter_weight) * this._lastVelocity;
44953
44954         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44955         var widths = {
44956             end: newWidth,
44957             start: this._lastWidth
44958         };
44959         
44960         this._lastVelocity = velocity;
44961         this._lastWidth = newWidth;
44962         return widths;
44963     },
44964     
44965     drawDot: function (_a) {
44966         var color = _a.color, point = _a.point;
44967         var ctx = this.canvasElCtx();
44968         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44969         ctx.beginPath();
44970         this.drawCurveSegment(point.x, point.y, width);
44971         ctx.closePath();
44972         ctx.fillStyle = color;
44973         ctx.fill();
44974     },
44975     
44976     drawCurve: function (_a) {
44977         var color = _a.color, curve = _a.curve;
44978         var ctx = this.canvasElCtx();
44979         var widthDelta = curve.endWidth - curve.startWidth;
44980         var drawSteps = Math.floor(curve.length()) * 2;
44981         ctx.beginPath();
44982         ctx.fillStyle = color;
44983         for (var i = 0; i < drawSteps; i += 1) {
44984         var t = i / drawSteps;
44985         var tt = t * t;
44986         var ttt = tt * t;
44987         var u = 1 - t;
44988         var uu = u * u;
44989         var uuu = uu * u;
44990         var x = uuu * curve.startPoint.x;
44991         x += 3 * uu * t * curve.control1.x;
44992         x += 3 * u * tt * curve.control2.x;
44993         x += ttt * curve.endPoint.x;
44994         var y = uuu * curve.startPoint.y;
44995         y += 3 * uu * t * curve.control1.y;
44996         y += 3 * u * tt * curve.control2.y;
44997         y += ttt * curve.endPoint.y;
44998         var width = curve.startWidth + ttt * widthDelta;
44999         this.drawCurveSegment(x, y, width);
45000         }
45001         ctx.closePath();
45002         ctx.fill();
45003     },
45004     
45005     drawCurveSegment: function (x, y, width) {
45006         var ctx = this.canvasElCtx();
45007         ctx.moveTo(x, y);
45008         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45009         this.is_empty = false;
45010     },
45011     
45012     clear: function()
45013     {
45014         var ctx = this.canvasElCtx();
45015         var canvas = this.canvasEl().dom;
45016         ctx.fillStyle = this.bg_color;
45017         ctx.clearRect(0, 0, canvas.width, canvas.height);
45018         ctx.fillRect(0, 0, canvas.width, canvas.height);
45019         this.curve_data = [];
45020         this.reset();
45021         this.is_empty = true;
45022     },
45023     
45024     fileEl: function()
45025     {
45026         return  this.el.select('input',true).first();
45027     },
45028     
45029     canvasEl: function()
45030     {
45031         return this.el.select('canvas',true).first();
45032     },
45033     
45034     canvasElCtx: function()
45035     {
45036         return this.el.select('canvas',true).first().dom.getContext('2d');
45037     },
45038     
45039     getImage: function(type)
45040     {
45041         if(this.is_empty) {
45042             return false;
45043         }
45044         
45045         // encryption ?
45046         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45047     },
45048     
45049     drawFromImage: function(img_src)
45050     {
45051         var img = new Image();
45052         
45053         img.onload = function(){
45054             this.canvasElCtx().drawImage(img, 0, 0);
45055         }.bind(this);
45056         
45057         img.src = img_src;
45058         
45059         this.is_empty = false;
45060     },
45061     
45062     selectImage: function()
45063     {
45064         this.fileEl().dom.click();
45065     },
45066     
45067     uploadImage: function(e)
45068     {
45069         var reader = new FileReader();
45070         
45071         reader.onload = function(e){
45072             var img = new Image();
45073             img.onload = function(){
45074                 this.reset();
45075                 this.canvasElCtx().drawImage(img, 0, 0);
45076             }.bind(this);
45077             img.src = e.target.result;
45078         }.bind(this);
45079         
45080         reader.readAsDataURL(e.target.files[0]);
45081     },
45082     
45083     // Bezier Point Constructor
45084     Point: (function () {
45085         function Point(x, y, time) {
45086             this.x = x;
45087             this.y = y;
45088             this.time = time || Date.now();
45089         }
45090         Point.prototype.distanceTo = function (start) {
45091             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45092         };
45093         Point.prototype.equals = function (other) {
45094             return this.x === other.x && this.y === other.y && this.time === other.time;
45095         };
45096         Point.prototype.velocityFrom = function (start) {
45097             return this.time !== start.time
45098             ? this.distanceTo(start) / (this.time - start.time)
45099             : 0;
45100         };
45101         return Point;
45102     }()),
45103     
45104     
45105     // Bezier Constructor
45106     Bezier: (function () {
45107         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45108             this.startPoint = startPoint;
45109             this.control2 = control2;
45110             this.control1 = control1;
45111             this.endPoint = endPoint;
45112             this.startWidth = startWidth;
45113             this.endWidth = endWidth;
45114         }
45115         Bezier.fromPoints = function (points, widths, scope) {
45116             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45117             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45118             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45119         };
45120         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45121             var dx1 = s1.x - s2.x;
45122             var dy1 = s1.y - s2.y;
45123             var dx2 = s2.x - s3.x;
45124             var dy2 = s2.y - s3.y;
45125             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45126             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45127             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45128             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45129             var dxm = m1.x - m2.x;
45130             var dym = m1.y - m2.y;
45131             var k = l2 / (l1 + l2);
45132             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45133             var tx = s2.x - cm.x;
45134             var ty = s2.y - cm.y;
45135             return {
45136                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45137                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45138             };
45139         };
45140         Bezier.prototype.length = function () {
45141             var steps = 10;
45142             var length = 0;
45143             var px;
45144             var py;
45145             for (var i = 0; i <= steps; i += 1) {
45146                 var t = i / steps;
45147                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45148                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45149                 if (i > 0) {
45150                     var xdiff = cx - px;
45151                     var ydiff = cy - py;
45152                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45153                 }
45154                 px = cx;
45155                 py = cy;
45156             }
45157             return length;
45158         };
45159         Bezier.prototype.point = function (t, start, c1, c2, end) {
45160             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45161             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45162             + (3.0 * c2 * (1.0 - t) * t * t)
45163             + (end * t * t * t);
45164         };
45165         return Bezier;
45166     }()),
45167     
45168     throttleStroke: function(fn, wait) {
45169       if (wait === void 0) { wait = 250; }
45170       var previous = 0;
45171       var timeout = null;
45172       var result;
45173       var storedContext;
45174       var storedArgs;
45175       var later = function () {
45176           previous = Date.now();
45177           timeout = null;
45178           result = fn.apply(storedContext, storedArgs);
45179           if (!timeout) {
45180               storedContext = null;
45181               storedArgs = [];
45182           }
45183       };
45184       return function wrapper() {
45185           var args = [];
45186           for (var _i = 0; _i < arguments.length; _i++) {
45187               args[_i] = arguments[_i];
45188           }
45189           var now = Date.now();
45190           var remaining = wait - (now - previous);
45191           storedContext = this;
45192           storedArgs = args;
45193           if (remaining <= 0 || remaining > wait) {
45194               if (timeout) {
45195                   clearTimeout(timeout);
45196                   timeout = null;
45197               }
45198               previous = now;
45199               result = fn.apply(storedContext, storedArgs);
45200               if (!timeout) {
45201                   storedContext = null;
45202                   storedArgs = [];
45203               }
45204           }
45205           else if (!timeout) {
45206               timeout = window.setTimeout(later, remaining);
45207           }
45208           return result;
45209       };
45210   }
45211   
45212 });
45213
45214  
45215
45216  // old names for form elements
45217 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45218 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45219 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45220 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45221 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45222 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45223 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45224 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45225 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45226 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45227 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45228 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45229 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45230 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45231 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45232 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45233 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45234 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45235 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45236 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45237 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45238 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45239 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45240 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45241 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45242 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45243
45244 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45245 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45246
45247 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45248 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45249
45250 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45251 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45252 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45253 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45254