Fix #6922 - replace builder to with @parent builder
[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.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
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.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                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault || 
6546                 this.href == '#' 
6547         ){
6548             Roo.log("NavItem - prevent Default?");
6549             e.preventDefault();
6550         }
6551         
6552         if (this.disabled) {
6553             return;
6554         }
6555         
6556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557         if (tg && tg.transition) {
6558             Roo.log("waiting for the transitionend");
6559             return;
6560         }
6561         
6562         
6563         
6564         //Roo.log("fire event clicked");
6565         if(this.fireEvent('click', this, e) === false){
6566             return;
6567         };
6568         
6569         if(this.tagtype == 'span'){
6570             return;
6571         }
6572         
6573         //Roo.log(this.href);
6574         var ael = this.el.select('a',true).first();
6575         //Roo.log(ael);
6576         
6577         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580                 return; // ignore... - it's a 'hash' to another page.
6581             }
6582             Roo.log("NavItem - prevent Default?");
6583             e.preventDefault();
6584             this.scrollToElement(e);
6585         }
6586         
6587         
6588         var p =  this.parent();
6589    
6590         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591             if (typeof(p.setActiveItem) !== 'undefined') {
6592                 p.setActiveItem(this);
6593             }
6594         }
6595         
6596         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598             // remove the collapsed menu expand...
6599             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6600         }
6601     },
6602     
6603     isActive: function () {
6604         return this.active
6605     },
6606     setActive : function(state, fire, is_was_active)
6607     {
6608         if (this.active && !state && this.navId) {
6609             this.was_active = true;
6610             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6611             if (nv) {
6612                 nv.clearWasActive(this);
6613             }
6614             
6615         }
6616         this.active = state;
6617         
6618         if (!state ) {
6619             this.el.removeClass('active');
6620             this.navLink ? this.navLink.removeClass('active') : false;
6621         } else if (!this.el.hasClass('active')) {
6622             
6623             this.el.addClass('active');
6624             if (Roo.bootstrap.version == 4 && this.navLink ) {
6625                 this.navLink.addClass('active');
6626             }
6627             
6628         }
6629         if (fire) {
6630             this.fireEvent('changed', this, state);
6631         }
6632         
6633         // show a panel if it's registered and related..
6634         
6635         if (!this.navId || !this.tabId || !state || is_was_active) {
6636             return;
6637         }
6638         
6639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6640         if (!tg) {
6641             return;
6642         }
6643         var pan = tg.getPanelByName(this.tabId);
6644         if (!pan) {
6645             return;
6646         }
6647         // if we can not flip to new panel - go back to old nav highlight..
6648         if (false == tg.showPanel(pan)) {
6649             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6650             if (nv) {
6651                 var onav = nv.getWasActive();
6652                 if (onav) {
6653                     onav.setActive(true, false, true);
6654                 }
6655             }
6656             
6657         }
6658         
6659         
6660         
6661     },
6662      // this should not be here...
6663     setDisabled : function(state)
6664     {
6665         this.disabled = state;
6666         if (!state ) {
6667             this.el.removeClass('disabled');
6668         } else if (!this.el.hasClass('disabled')) {
6669             this.el.addClass('disabled');
6670         }
6671         
6672     },
6673     
6674     /**
6675      * Fetch the element to display the tooltip on.
6676      * @return {Roo.Element} defaults to this.el
6677      */
6678     tooltipEl : function()
6679     {
6680         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6681     },
6682     
6683     scrollToElement : function(e)
6684     {
6685         var c = document.body;
6686         
6687         /*
6688          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6689          */
6690         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691             c = document.documentElement;
6692         }
6693         
6694         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695         
6696         if(!target){
6697             return;
6698         }
6699
6700         var o = target.calcOffsetsTo(c);
6701         
6702         var options = {
6703             target : target,
6704             value : o[1]
6705         };
6706         
6707         this.fireEvent('scrollto', this, options, e);
6708         
6709         Roo.get(c).scrollTo('top', options.value, true);
6710         
6711         return;
6712     },
6713     /**
6714      * Set the HTML (text content) of the item
6715      * @param {string} html  content for the nav item
6716      */
6717     setHtml : function(html)
6718     {
6719         this.html = html;
6720         this.htmlEl.dom.innerHTML = html;
6721         
6722     } 
6723 });
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * sidebar item
6730  *
6731  *  li
6732  *    <span> icon </span>
6733  *    <span> text </span>
6734  *    <span>badge </span>
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.nav.SidebarItem
6739  * @extends Roo.bootstrap.nav.Item
6740  * Bootstrap Navbar.NavSidebarItem class
6741  * 
6742  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743  * {Boolean} open is the menu open
6744  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746  * {String} buttonSize (sm|md|lg)the extra classes for the button
6747  * {Boolean} showArrow show arrow next to the text (default true)
6748  * @constructor
6749  * Create a new Navbar Button
6750  * @param {Object} config The config object
6751  */
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6754     this.addEvents({
6755         // raw events
6756         /**
6757          * @event click
6758          * The raw click event for the entire grid.
6759          * @param {Roo.EventObject} e
6760          */
6761         "click" : true,
6762          /**
6763             * @event changed
6764             * Fires when the active item active state changes
6765             * @param {Roo.bootstrap.nav.SidebarItem} this
6766             * @param {boolean} state the new state
6767              
6768          */
6769         'changed': true
6770     });
6771    
6772 };
6773
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6775     
6776     badgeWeight : 'default',
6777     
6778     open: false,
6779     
6780     buttonView : false,
6781     
6782     buttonWeight : 'default',
6783     
6784     buttonSize : 'md',
6785     
6786     showArrow : true,
6787     
6788     getAutoCreate : function(){
6789         
6790         
6791         var a = {
6792                 tag: 'a',
6793                 href : this.href || '#',
6794                 cls: '',
6795                 html : '',
6796                 cn : []
6797         };
6798         
6799         if(this.buttonView){
6800             a = {
6801                 tag: 'button',
6802                 href : this.href || '#',
6803                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804                 html : this.html,
6805                 cn : []
6806             };
6807         }
6808         
6809         var cfg = {
6810             tag: 'li',
6811             cls: '',
6812             cn: [ a ]
6813         };
6814         
6815         if (this.active) {
6816             cfg.cls += ' active';
6817         }
6818         
6819         if (this.disabled) {
6820             cfg.cls += ' disabled';
6821         }
6822         if (this.open) {
6823             cfg.cls += ' open x-open';
6824         }
6825         // left icon..
6826         if (this.glyphicon || this.icon) {
6827             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6828             a.cn.push({ tag : 'i', cls : c }) ;
6829         }
6830         
6831         if(!this.buttonView){
6832             var span = {
6833                 tag: 'span',
6834                 html : this.html || ''
6835             };
6836
6837             a.cn.push(span);
6838             
6839         }
6840         
6841         if (this.badge !== '') {
6842             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6843         }
6844         
6845         if (this.menu) {
6846             
6847             if(this.showArrow){
6848                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849             }
6850             
6851             a.cls += ' dropdown-toggle treeview' ;
6852         }
6853         
6854         return cfg;
6855     },
6856     
6857     initEvents : function()
6858     { 
6859         if (typeof (this.menu) != 'undefined') {
6860             this.menu.parentType = this.xtype;
6861             this.menu.triggerEl = this.el;
6862             this.menu = this.addxtype(Roo.apply({}, this.menu));
6863         }
6864         
6865         this.el.on('click', this.onClick, this);
6866         
6867         if(this.badge !== ''){
6868             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6869         }
6870         
6871     },
6872     
6873     onClick : function(e)
6874     {
6875         if(this.disabled){
6876             e.preventDefault();
6877             return;
6878         }
6879         
6880         if(this.preventDefault){
6881             e.preventDefault();
6882         }
6883         
6884         this.fireEvent('click', this, e);
6885     },
6886     
6887     disable : function()
6888     {
6889         this.setDisabled(true);
6890     },
6891     
6892     enable : function()
6893     {
6894         this.setDisabled(false);
6895     },
6896     
6897     setDisabled : function(state)
6898     {
6899         if(this.disabled == state){
6900             return;
6901         }
6902         
6903         this.disabled = state;
6904         
6905         if (state) {
6906             this.el.addClass('disabled');
6907             return;
6908         }
6909         
6910         this.el.removeClass('disabled');
6911         
6912         return;
6913     },
6914     
6915     setActive : function(state)
6916     {
6917         if(this.active == state){
6918             return;
6919         }
6920         
6921         this.active = state;
6922         
6923         if (state) {
6924             this.el.addClass('active');
6925             return;
6926         }
6927         
6928         this.el.removeClass('active');
6929         
6930         return;
6931     },
6932     
6933     isActive: function () 
6934     {
6935         return this.active;
6936     },
6937     
6938     setBadge : function(str)
6939     {
6940         if(!this.badgeEl){
6941             return;
6942         }
6943         
6944         this.badgeEl.dom.innerHTML = str;
6945     }
6946     
6947    
6948      
6949  
6950 });
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * nav progress bar
6957  * 
6958  */
6959
6960 /**
6961  * @class Roo.bootstrap.nav.ProgressBar
6962  * @extends Roo.bootstrap.Component
6963  * @children Roo.bootstrap.nav.ProgressBarItem
6964  * Bootstrap NavProgressBar class
6965  * 
6966  * @constructor
6967  * Create a new nav progress bar - a bar indicating step along a process
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6973
6974     this.bullets = this.bullets || [];
6975    
6976 //    Roo.bootstrap.nav.ProgressBar.register(this);
6977      this.addEvents({
6978         /**
6979              * @event changed
6980              * Fires when the active item changes
6981              * @param {Roo.bootstrap.nav.ProgressBar} this
6982              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6984          */
6985         'changed': true
6986      });
6987     
6988 };
6989
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6991     /**
6992      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993      * Bullets for the Nav Progress bar for the toolbar
6994      */
6995     bullets : [],
6996     barItems : [],
6997     
6998     getAutoCreate : function()
6999     {
7000         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7001         
7002         cfg = {
7003             tag : 'div',
7004             cls : 'roo-navigation-bar-group',
7005             cn : [
7006                 {
7007                     tag : 'div',
7008                     cls : 'roo-navigation-top-bar'
7009                 },
7010                 {
7011                     tag : 'div',
7012                     cls : 'roo-navigation-bullets-bar',
7013                     cn : [
7014                         {
7015                             tag : 'ul',
7016                             cls : 'roo-navigation-bar'
7017                         }
7018                     ]
7019                 },
7020                 
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bottom-bar'
7024                 }
7025             ]
7026             
7027         };
7028         
7029         return cfg;
7030         
7031     },
7032     
7033     initEvents: function() 
7034     {
7035         
7036     },
7037     
7038     onRender : function(ct, position) 
7039     {
7040         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7041         
7042         if(this.bullets.length){
7043             Roo.each(this.bullets, function(b){
7044                this.addItem(b);
7045             }, this);
7046         }
7047         
7048         this.format();
7049         
7050     },
7051     
7052     addItem : function(cfg)
7053     {
7054         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7055         
7056         item.parentId = this.id;
7057         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058         
7059         if(cfg.html){
7060             var top = new Roo.bootstrap.Element({
7061                 tag : 'div',
7062                 cls : 'roo-navigation-bar-text'
7063             });
7064             
7065             var bottom = new Roo.bootstrap.Element({
7066                 tag : 'div',
7067                 cls : 'roo-navigation-bar-text'
7068             });
7069             
7070             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7072             
7073             var topText = new Roo.bootstrap.Element({
7074                 tag : 'span',
7075                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076             });
7077             
7078             var bottomText = new Roo.bootstrap.Element({
7079                 tag : 'span',
7080                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081             });
7082             
7083             topText.onRender(top.el, null);
7084             bottomText.onRender(bottom.el, null);
7085             
7086             item.topEl = top;
7087             item.bottomEl = bottom;
7088         }
7089         
7090         this.barItems.push(item);
7091         
7092         return item;
7093     },
7094     
7095     getActive : function()
7096     {
7097         var active = false;
7098         
7099         Roo.each(this.barItems, function(v){
7100             
7101             if (!v.isActive()) {
7102                 return;
7103             }
7104             
7105             active = v;
7106             return false;
7107             
7108         });
7109         
7110         return active;
7111     },
7112     
7113     setActiveItem : function(item)
7114     {
7115         var prev = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             if (v.rid == item.rid) {
7119                 return ;
7120             }
7121             
7122             if (v.isActive()) {
7123                 v.setActive(false);
7124                 prev = v;
7125             }
7126         });
7127
7128         item.setActive(true);
7129         
7130         this.fireEvent('changed', this, item, prev);
7131     },
7132     
7133     getBarItem: function(rid)
7134     {
7135         var ret = false;
7136         
7137         Roo.each(this.barItems, function(e) {
7138             if (e.rid != rid) {
7139                 return;
7140             }
7141             
7142             ret =  e;
7143             return false;
7144         });
7145         
7146         return ret;
7147     },
7148     
7149     indexOfItem : function(item)
7150     {
7151         var index = false;
7152         
7153         Roo.each(this.barItems, function(v, i){
7154             
7155             if (v.rid != item.rid) {
7156                 return;
7157             }
7158             
7159             index = i;
7160             return false
7161         });
7162         
7163         return index;
7164     },
7165     
7166     setActiveNext : function()
7167     {
7168         var i = this.indexOfItem(this.getActive());
7169         
7170         if (i > this.barItems.length) {
7171             return;
7172         }
7173         
7174         this.setActiveItem(this.barItems[i+1]);
7175     },
7176     
7177     setActivePrev : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i  < 1) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i-1]);
7186     },
7187     
7188     format : function()
7189     {
7190         if(!this.barItems.length){
7191             return;
7192         }
7193      
7194         var width = 100 / this.barItems.length;
7195         
7196         Roo.each(this.barItems, function(i){
7197             i.el.setStyle('width', width + '%');
7198             i.topEl.el.setStyle('width', width + '%');
7199             i.bottomEl.el.setStyle('width', width + '%');
7200         }, this);
7201         
7202     }
7203     
7204 });
7205 /*
7206  * - LGPL
7207  *
7208  * Nav Progress Item
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.nav.ProgressBarItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap NavProgressBarItem class
7216  * @cfg {String} rid the reference id
7217  * @cfg {Boolean} active (true|false) Is item active default false
7218  * @cfg {Boolean} disabled (true|false) Is item active default false
7219  * @cfg {String} html
7220  * @cfg {String} position (top|bottom) text position default bottom
7221  * @cfg {String} icon show icon instead of number
7222  * 
7223  * @constructor
7224  * Create a new NavProgressBarItem
7225  * @param {Object} config The config object
7226  */
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7229     this.addEvents({
7230         // raw events
7231         /**
7232          * @event click
7233          * The raw click event for the entire grid.
7234          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235          * @param {Roo.EventObject} e
7236          */
7237         "click" : true
7238     });
7239    
7240 };
7241
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7243     
7244     rid : '',
7245     active : false,
7246     disabled : false,
7247     html : '',
7248     position : 'bottom',
7249     icon : false,
7250     
7251     getAutoCreate : function()
7252     {
7253         var iconCls = 'roo-navigation-bar-item-icon';
7254         
7255         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7256         
7257         var cfg = {
7258             tag: 'li',
7259             cls: 'roo-navigation-bar-item',
7260             cn : [
7261                 {
7262                     tag : 'i',
7263                     cls : iconCls
7264                 }
7265             ]
7266         };
7267         
7268         if(this.active){
7269             cfg.cls += ' active';
7270         }
7271         if(this.disabled){
7272             cfg.cls += ' disabled';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     disable : function()
7279     {
7280         this.setDisabled(true);
7281     },
7282     
7283     enable : function()
7284     {
7285         this.setDisabled(false);
7286     },
7287     
7288     initEvents: function() 
7289     {
7290         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7291         
7292         this.iconEl.on('click', this.onClick, this);
7293     },
7294     
7295     onClick : function(e)
7296     {
7297         e.preventDefault();
7298         
7299         if(this.disabled){
7300             return;
7301         }
7302         
7303         if(this.fireEvent('click', this, e) === false){
7304             return;
7305         };
7306         
7307         this.parent().setActiveItem(this);
7308     },
7309     
7310     isActive: function () 
7311     {
7312         return this.active;
7313     },
7314     
7315     setActive : function(state)
7316     {
7317         if(this.active == state){
7318             return;
7319         }
7320         
7321         this.active = state;
7322         
7323         if (state) {
7324             this.el.addClass('active');
7325             return;
7326         }
7327         
7328         this.el.removeClass('active');
7329         
7330         return;
7331     },
7332     
7333     setDisabled : function(state)
7334     {
7335         if(this.disabled == state){
7336             return;
7337         }
7338         
7339         this.disabled = state;
7340         
7341         if (state) {
7342             this.el.addClass('disabled');
7343             return;
7344         }
7345         
7346         this.el.removeClass('disabled');
7347     },
7348     
7349     tooltipEl : function()
7350     {
7351         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7352     }
7353 });
7354  
7355
7356  /*
7357  * - LGPL
7358  *
7359  *  Breadcrumb Nav
7360  * 
7361  */
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7363
7364
7365 /**
7366  * @class Roo.bootstrap.breadcrumb.Nav
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap Breadcrumb Nav Class
7369  *  
7370  * @children Roo.bootstrap.breadcrumb.Item
7371  * 
7372  * @constructor
7373  * Create a new breadcrumb.Nav
7374  * @param {Object} config The config object
7375  */
7376
7377
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7380     
7381     
7382 };
7383
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7385     
7386     getAutoCreate : function()
7387     {
7388
7389         var cfg = {
7390             tag: 'nav',
7391             cn : [
7392                 {
7393                     tag : 'ol',
7394                     cls : 'breadcrumb'
7395                 }
7396             ]
7397             
7398         };
7399           
7400         return cfg;
7401     },
7402     
7403     initEvents: function()
7404     {
7405         this.olEl = this.el.select('ol',true).first();    
7406     },
7407     getChildContainer : function()
7408     {
7409         return this.olEl;  
7410     }
7411     
7412 });
7413
7414  /*
7415  * - LGPL
7416  *
7417  *  Breadcrumb Item
7418  * 
7419  */
7420
7421
7422 /**
7423  * @class Roo.bootstrap.breadcrumb.Nav
7424  * @extends Roo.bootstrap.Component
7425  * @children Roo.bootstrap.Component
7426  * @parent Roo.bootstrap.breadcrumb.Nav
7427  * Bootstrap Breadcrumb Nav Class
7428  *  
7429  * 
7430  * @cfg {String} html the content of the link.
7431  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432  * @cfg {Boolean} active is it active
7433
7434  * 
7435  * @constructor
7436  * Create a new breadcrumb.Nav
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7442     this.addEvents({
7443         // img events
7444         /**
7445          * @event click
7446          * The img click event for the img.
7447          * @param {Roo.EventObject} e
7448          */
7449         "click" : true
7450     });
7451     
7452 };
7453
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7455     
7456     href: false,
7457     html : '',
7458     
7459     getAutoCreate : function()
7460     {
7461
7462         var cfg = {
7463             tag: 'li',
7464             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7465         };
7466         if (this.href !== false) {
7467             cfg.cn = [{
7468                 tag : 'a',
7469                 href : this.href,
7470                 html : this.html
7471             }];
7472         } else {
7473             cfg.html = this.html;
7474         }
7475         
7476         return cfg;
7477     },
7478     
7479     initEvents: function()
7480     {
7481         if (this.href) {
7482             this.el.select('a', true).first().on('click',this.onClick, this)
7483         }
7484         
7485     },
7486     onClick : function(e)
7487     {
7488         e.preventDefault();
7489         this.fireEvent('click',this,  e);
7490     }
7491     
7492 });
7493
7494  /*
7495  * - LGPL
7496  *
7497  * row
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Row
7503  * @extends Roo.bootstrap.Component
7504  * @children Roo.bootstrap.Component
7505  * Bootstrap Row class (contains columns...)
7506  * 
7507  * @constructor
7508  * Create a new Row
7509  * @param {Object} config The config object
7510  */
7511
7512 Roo.bootstrap.Row = function(config){
7513     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7514 };
7515
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7517     
7518     getAutoCreate : function(){
7519        return {
7520             cls: 'row clearfix'
7521        };
7522     }
7523     
7524     
7525 });
7526
7527  
7528
7529  /*
7530  * - LGPL
7531  *
7532  * pagination
7533  * 
7534  */
7535
7536 /**
7537  * @class Roo.bootstrap.Pagination
7538  * @extends Roo.bootstrap.Component
7539  * @children Roo.bootstrap.Pagination
7540  * Bootstrap Pagination class
7541  * 
7542  * @cfg {String} size (xs|sm|md|lg|xl)
7543  * @cfg {Boolean} inverse 
7544  * 
7545  * @constructor
7546  * Create a new Pagination
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.Pagination = function(config){
7551     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     size: false,
7558     inverse: false,
7559     
7560     getAutoCreate : function(){
7561         var cfg = {
7562             tag: 'ul',
7563                 cls: 'pagination'
7564         };
7565         if (this.inverse) {
7566             cfg.cls += ' inverse';
7567         }
7568         if (this.html) {
7569             cfg.html=this.html;
7570         }
7571         if (this.cls) {
7572             cfg.cls += " " + this.cls;
7573         }
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * Pagination item
7585  * 
7586  */
7587
7588
7589 /**
7590  * @class Roo.bootstrap.PaginationItem
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap PaginationItem class
7593  * @cfg {String} html text
7594  * @cfg {String} href the link
7595  * @cfg {Boolean} preventDefault (true | false) default true
7596  * @cfg {Boolean} active (true | false) default false
7597  * @cfg {Boolean} disabled default false
7598  * 
7599  * 
7600  * @constructor
7601  * Create a new PaginationItem
7602  * @param {Object} config The config object
7603  */
7604
7605
7606 Roo.bootstrap.PaginationItem = function(config){
7607     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7608     this.addEvents({
7609         // raw events
7610         /**
7611          * @event click
7612          * The raw click event for the entire grid.
7613          * @param {Roo.EventObject} e
7614          */
7615         "click" : true
7616     });
7617 };
7618
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7620     
7621     href : false,
7622     html : false,
7623     preventDefault: true,
7624     active : false,
7625     cls : false,
7626     disabled: false,
7627     
7628     getAutoCreate : function(){
7629         var cfg= {
7630             tag: 'li',
7631             cn: [
7632                 {
7633                     tag : 'a',
7634                     href : this.href ? this.href : '#',
7635                     html : this.html ? this.html : ''
7636                 }
7637             ]
7638         };
7639         
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         
7644         if(this.disabled){
7645             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646         }
7647         
7648         if(this.active){
7649             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7650         }
7651         
7652         return cfg;
7653     },
7654     
7655     initEvents: function() {
7656         
7657         this.el.on('click', this.onClick, this);
7658         
7659     },
7660     onClick : function(e)
7661     {
7662         Roo.log('PaginationItem on click ');
7663         if(this.preventDefault){
7664             e.preventDefault();
7665         }
7666         
7667         if(this.disabled){
7668             return;
7669         }
7670         
7671         this.fireEvent('click', this, e);
7672     }
7673    
7674 });
7675
7676  
7677
7678  /*
7679  * - LGPL
7680  *
7681  * slider
7682  * 
7683  */
7684
7685
7686 /**
7687  * @class Roo.bootstrap.Slider
7688  * @extends Roo.bootstrap.Component
7689  * Bootstrap Slider class
7690  *    
7691  * @constructor
7692  * Create a new Slider
7693  * @param {Object} config The config object
7694  */
7695
7696 Roo.bootstrap.Slider = function(config){
7697     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7698 };
7699
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7701     
7702     getAutoCreate : function(){
7703         
7704         var cfg = {
7705             tag: 'div',
7706             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707             cn: [
7708                 {
7709                     tag: 'a',
7710                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7711                 }
7712             ]
7713         };
7714         
7715         return cfg;
7716     }
7717    
7718 });
7719
7720  /*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  /**
7731  * @extends Roo.dd.DDProxy
7732  * @class Roo.grid.SplitDragZone
7733  * Support for Column Header resizing
7734  * @constructor
7735  * @param {Object} config
7736  */
7737 // private
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7740     this.grid = grid;
7741     this.view = grid.getView();
7742     this.proxy = this.view.resizeProxy;
7743     Roo.grid.SplitDragZone.superclass.constructor.call(
7744         this,
7745         hd, // ID
7746         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7747         {  // CONFIG
7748             dragElId : Roo.id(this.proxy.dom),
7749             resizeFrame:false
7750         }
7751     );
7752     
7753     this.setHandleElId(Roo.id(hd));
7754     if (hd2 !== false) {
7755         this.setOuterHandleElId(Roo.id(hd2));
7756     }
7757     
7758     this.scroll = false;
7759 };
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761     fly: Roo.Element.fly,
7762
7763     b4StartDrag : function(x, y){
7764         this.view.headersDisabled = true;
7765         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7767         );
7768         this.proxy.setHeight(h);
7769         
7770         // for old system colWidth really stored the actual width?
7771         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772         // which in reality did not work.. - it worked only for fixed sizes
7773         // for resizable we need to use actual sizes.
7774         var w = this.cm.getColumnWidth(this.cellIndex);
7775         if (!this.view.mainWrap) {
7776             // bootstrap.
7777             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7778         }
7779         
7780         
7781         
7782         // this was w-this.grid.minColumnWidth;
7783         // doesnt really make sense? - w = thie curren width or the rendered one?
7784         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785         this.resetConstraints();
7786         this.setXConstraint(minw, 1000);
7787         this.setYConstraint(0, 0);
7788         this.minX = x - minw;
7789         this.maxX = x + 1000;
7790         this.startPos = x;
7791         if (!this.view.mainWrap) { // this is Bootstrap code..
7792             this.getDragEl().style.display='block';
7793         }
7794         
7795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796     },
7797
7798
7799     handleMouseDown : function(e){
7800         ev = Roo.EventObject.setEvent(e);
7801         var t = this.fly(ev.getTarget());
7802         if(t.hasClass("x-grid-split")){
7803             this.cellIndex = this.view.getCellIndex(t.dom);
7804             this.split = t.dom;
7805             this.cm = this.grid.colModel;
7806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7808             }
7809         }
7810     },
7811
7812     endDrag : function(e){
7813         this.view.headersDisabled = false;
7814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815         var diff = endX - this.startPos;
7816         // 
7817         var w = this.cm.getColumnWidth(this.cellIndex);
7818         if (!this.view.mainWrap) {
7819             w = 0;
7820         }
7821         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7822     },
7823
7824     autoOffset : function(){
7825         this.setDelta(0,0);
7826     }
7827 });/*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837
7838 /**
7839  * @class Roo.grid.AbstractSelectionModel
7840  * @extends Roo.util.Observable
7841  * @abstract
7842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7843  * implemented by descendant classes.  This class should not be directly instantiated.
7844  * @constructor
7845  */
7846 Roo.grid.AbstractSelectionModel = function(){
7847     this.locked = false;
7848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7849 };
7850
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7852     /** @ignore Called by the grid automatically. Do not call directly. */
7853     init : function(grid){
7854         this.grid = grid;
7855         this.initEvents();
7856     },
7857
7858     /**
7859      * Locks the selections.
7860      */
7861     lock : function(){
7862         this.locked = true;
7863     },
7864
7865     /**
7866      * Unlocks the selections.
7867      */
7868     unlock : function(){
7869         this.locked = false;
7870     },
7871
7872     /**
7873      * Returns true if the selections are locked.
7874      * @return {Boolean}
7875      */
7876     isLocked : function(){
7877         return this.locked;
7878     }
7879 });/*
7880  * Based on:
7881  * Ext JS Library 1.1.1
7882  * Copyright(c) 2006-2007, Ext JS, LLC.
7883  *
7884  * Originally Released Under LGPL - original licence link has changed is not relivant.
7885  *
7886  * Fork - LGPL
7887  * <script type="text/javascript">
7888  */
7889 /**
7890  * @extends Roo.grid.AbstractSelectionModel
7891  * @class Roo.grid.RowSelectionModel
7892  * The default SelectionModel used by {@link Roo.grid.Grid}.
7893  * It supports multiple selections and keyboard selection/navigation. 
7894  * @constructor
7895  * @param {Object} config
7896  */
7897 Roo.grid.RowSelectionModel = function(config){
7898     Roo.apply(this, config);
7899     this.selections = new Roo.util.MixedCollection(false, function(o){
7900         return o.id;
7901     });
7902
7903     this.last = false;
7904     this.lastActive = false;
7905
7906     this.addEvents({
7907         /**
7908         * @event selectionchange
7909         * Fires when the selection changes
7910         * @param {SelectionModel} this
7911         */
7912        "selectionchange" : true,
7913        /**
7914         * @event afterselectionchange
7915         * Fires after the selection changes (eg. by key press or clicking)
7916         * @param {SelectionModel} this
7917         */
7918        "afterselectionchange" : true,
7919        /**
7920         * @event beforerowselect
7921         * Fires when a row is selected being selected, return false to cancel.
7922         * @param {SelectionModel} this
7923         * @param {Number} rowIndex The selected index
7924         * @param {Boolean} keepExisting False if other selections will be cleared
7925         */
7926        "beforerowselect" : true,
7927        /**
7928         * @event rowselect
7929         * Fires when a row is selected.
7930         * @param {SelectionModel} this
7931         * @param {Number} rowIndex The selected index
7932         * @param {Roo.data.Record} r The record
7933         */
7934        "rowselect" : true,
7935        /**
7936         * @event rowdeselect
7937         * Fires when a row is deselected.
7938         * @param {SelectionModel} this
7939         * @param {Number} rowIndex The selected index
7940         */
7941         "rowdeselect" : true
7942     });
7943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944     this.locked = false;
7945 };
7946
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7948     /**
7949      * @cfg {Boolean} singleSelect
7950      * True to allow selection of only one row at a time (defaults to false)
7951      */
7952     singleSelect : false,
7953
7954     // private
7955     initEvents : function(){
7956
7957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958             this.grid.on("mousedown", this.handleMouseDown, this);
7959         }else{ // allow click to work like normal
7960             this.grid.on("rowclick", this.handleDragableRowClick, this);
7961         }
7962         // bootstrap does not have a view..
7963         var view = this.grid.view ? this.grid.view : this.grid;
7964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7965             "up" : function(e){
7966                 if(!e.shiftKey){
7967                     this.selectPrevious(e.shiftKey);
7968                 }else if(this.last !== false && this.lastActive !== false){
7969                     var last = this.last;
7970                     this.selectRange(this.last,  this.lastActive-1);
7971                     view.focusRow(this.lastActive);
7972                     if(last !== false){
7973                         this.last = last;
7974                     }
7975                 }else{
7976                     this.selectFirstRow();
7977                 }
7978                 this.fireEvent("afterselectionchange", this);
7979             },
7980             "down" : function(e){
7981                 if(!e.shiftKey){
7982                     this.selectNext(e.shiftKey);
7983                 }else if(this.last !== false && this.lastActive !== false){
7984                     var last = this.last;
7985                     this.selectRange(this.last,  this.lastActive+1);
7986                     view.focusRow(this.lastActive);
7987                     if(last !== false){
7988                         this.last = last;
7989                     }
7990                 }else{
7991                     this.selectFirstRow();
7992                 }
7993                 this.fireEvent("afterselectionchange", this);
7994             },
7995             scope: this
7996         });
7997
7998          
7999         view.on("refresh", this.onRefresh, this);
8000         view.on("rowupdated", this.onRowUpdated, this);
8001         view.on("rowremoved", this.onRemove, this);
8002     },
8003
8004     // private
8005     onRefresh : function(){
8006         var ds = this.grid.ds, i, v = this.grid.view;
8007         var s = this.selections;
8008         s.each(function(r){
8009             if((i = ds.indexOfId(r.id)) != -1){
8010                 v.onRowSelect(i);
8011                 s.add(ds.getAt(i)); // updating the selection relate data
8012             }else{
8013                 s.remove(r);
8014             }
8015         });
8016     },
8017
8018     // private
8019     onRemove : function(v, index, r){
8020         this.selections.remove(r);
8021     },
8022
8023     // private
8024     onRowUpdated : function(v, index, r){
8025         if(this.isSelected(r)){
8026             v.onRowSelect(index);
8027         }
8028     },
8029
8030     /**
8031      * Select records.
8032      * @param {Array} records The records to select
8033      * @param {Boolean} keepExisting (optional) True to keep existing selections
8034      */
8035     selectRecords : function(records, keepExisting){
8036         if(!keepExisting){
8037             this.clearSelections();
8038         }
8039         var ds = this.grid.ds;
8040         for(var i = 0, len = records.length; i < len; i++){
8041             this.selectRow(ds.indexOf(records[i]), true);
8042         }
8043     },
8044
8045     /**
8046      * Gets the number of selected rows.
8047      * @return {Number}
8048      */
8049     getCount : function(){
8050         return this.selections.length;
8051     },
8052
8053     /**
8054      * Selects the first row in the grid.
8055      */
8056     selectFirstRow : function(){
8057         this.selectRow(0);
8058     },
8059
8060     /**
8061      * Select the last row.
8062      * @param {Boolean} keepExisting (optional) True to keep existing selections
8063      */
8064     selectLastRow : function(keepExisting){
8065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066     },
8067
8068     /**
8069      * Selects the row immediately following the last selected row.
8070      * @param {Boolean} keepExisting (optional) True to keep existing selections
8071      */
8072     selectNext : function(keepExisting){
8073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074             this.selectRow(this.last+1, keepExisting);
8075             var view = this.grid.view ? this.grid.view : this.grid;
8076             view.focusRow(this.last);
8077         }
8078     },
8079
8080     /**
8081      * Selects the row that precedes the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectPrevious : function(keepExisting){
8085         if(this.last){
8086             this.selectRow(this.last-1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Returns the selected records
8094      * @return {Array} Array of selected records
8095      */
8096     getSelections : function(){
8097         return [].concat(this.selections.items);
8098     },
8099
8100     /**
8101      * Returns the first selected record.
8102      * @return {Record}
8103      */
8104     getSelected : function(){
8105         return this.selections.itemAt(0);
8106     },
8107
8108
8109     /**
8110      * Clears all selections.
8111      */
8112     clearSelections : function(fast){
8113         if(this.locked) {
8114             return;
8115         }
8116         if(fast !== true){
8117             var ds = this.grid.ds;
8118             var s = this.selections;
8119             s.each(function(r){
8120                 this.deselectRow(ds.indexOfId(r.id));
8121             }, this);
8122             s.clear();
8123         }else{
8124             this.selections.clear();
8125         }
8126         this.last = false;
8127     },
8128
8129
8130     /**
8131      * Selects all rows.
8132      */
8133     selectAll : function(){
8134         if(this.locked) {
8135             return;
8136         }
8137         this.selections.clear();
8138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139             this.selectRow(i, true);
8140         }
8141     },
8142
8143     /**
8144      * Returns True if there is a selection.
8145      * @return {Boolean}
8146      */
8147     hasSelection : function(){
8148         return this.selections.length > 0;
8149     },
8150
8151     /**
8152      * Returns True if the specified row is selected.
8153      * @param {Number/Record} record The record or index of the record to check
8154      * @return {Boolean}
8155      */
8156     isSelected : function(index){
8157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158         return (r && this.selections.key(r.id) ? true : false);
8159     },
8160
8161     /**
8162      * Returns True if the specified record id is selected.
8163      * @param {String} id The id of record to check
8164      * @return {Boolean}
8165      */
8166     isIdSelected : function(id){
8167         return (this.selections.key(id) ? true : false);
8168     },
8169
8170     // private
8171     handleMouseDown : function(e, t)
8172     {
8173         var view = this.grid.view ? this.grid.view : this.grid;
8174         var rowIndex;
8175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8176             return;
8177         };
8178         if(e.shiftKey && this.last !== false){
8179             var last = this.last;
8180             this.selectRange(last, rowIndex, e.ctrlKey);
8181             this.last = last; // reset the last
8182             view.focusRow(rowIndex);
8183         }else{
8184             var isSelected = this.isSelected(rowIndex);
8185             if(e.button !== 0 && isSelected){
8186                 view.focusRow(rowIndex);
8187             }else if(e.ctrlKey && isSelected){
8188                 this.deselectRow(rowIndex);
8189             }else if(!isSelected){
8190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191                 view.focusRow(rowIndex);
8192             }
8193         }
8194         this.fireEvent("afterselectionchange", this);
8195     },
8196     // private
8197     handleDragableRowClick :  function(grid, rowIndex, e) 
8198     {
8199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200             this.selectRow(rowIndex, false);
8201             var view = this.grid.view ? this.grid.view : this.grid;
8202             view.focusRow(rowIndex);
8203              this.fireEvent("afterselectionchange", this);
8204         }
8205     },
8206     
8207     /**
8208      * Selects multiple rows.
8209      * @param {Array} rows Array of the indexes of the row to select
8210      * @param {Boolean} keepExisting (optional) True to keep existing selections
8211      */
8212     selectRows : function(rows, keepExisting){
8213         if(!keepExisting){
8214             this.clearSelections();
8215         }
8216         for(var i = 0, len = rows.length; i < len; i++){
8217             this.selectRow(rows[i], true);
8218         }
8219     },
8220
8221     /**
8222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223      * @param {Number} startRow The index of the first row in the range
8224      * @param {Number} endRow The index of the last row in the range
8225      * @param {Boolean} keepExisting (optional) True to retain existing selections
8226      */
8227     selectRange : function(startRow, endRow, keepExisting){
8228         if(this.locked) {
8229             return;
8230         }
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         if(startRow <= endRow){
8235             for(var i = startRow; i <= endRow; i++){
8236                 this.selectRow(i, true);
8237             }
8238         }else{
8239             for(var i = startRow; i >= endRow; i--){
8240                 this.selectRow(i, true);
8241             }
8242         }
8243     },
8244
8245     /**
8246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      */
8250     deselectRange : function(startRow, endRow, preventViewNotify){
8251         if(this.locked) {
8252             return;
8253         }
8254         for(var i = startRow; i <= endRow; i++){
8255             this.deselectRow(i, preventViewNotify);
8256         }
8257     },
8258
8259     /**
8260      * Selects a row.
8261      * @param {Number} row The index of the row to select
8262      * @param {Boolean} keepExisting (optional) True to keep existing selections
8263      */
8264     selectRow : function(index, keepExisting, preventViewNotify){
8265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8266             return;
8267         }
8268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269             if(!keepExisting || this.singleSelect){
8270                 this.clearSelections();
8271             }
8272             var r = this.grid.ds.getAt(index);
8273             this.selections.add(r);
8274             this.last = this.lastActive = index;
8275             if(!preventViewNotify){
8276                 var view = this.grid.view ? this.grid.view : this.grid;
8277                 view.onRowSelect(index);
8278             }
8279             this.fireEvent("rowselect", this, index, r);
8280             this.fireEvent("selectionchange", this);
8281         }
8282     },
8283
8284     /**
8285      * Deselects a row.
8286      * @param {Number} row The index of the row to deselect
8287      */
8288     deselectRow : function(index, preventViewNotify){
8289         if(this.locked) {
8290             return;
8291         }
8292         if(this.last == index){
8293             this.last = false;
8294         }
8295         if(this.lastActive == index){
8296             this.lastActive = false;
8297         }
8298         var r = this.grid.ds.getAt(index);
8299         this.selections.remove(r);
8300         if(!preventViewNotify){
8301             var view = this.grid.view ? this.grid.view : this.grid;
8302             view.onRowDeselect(index);
8303         }
8304         this.fireEvent("rowdeselect", this, index);
8305         this.fireEvent("selectionchange", this);
8306     },
8307
8308     // private
8309     restoreLast : function(){
8310         if(this._last){
8311             this.last = this._last;
8312         }
8313     },
8314
8315     // private
8316     acceptsNav : function(row, col, cm){
8317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318     },
8319
8320     // private
8321     onEditorKey : function(field, e){
8322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8323         if(k == e.TAB){
8324             e.stopEvent();
8325             ed.completeEdit();
8326             if(e.shiftKey){
8327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8328             }else{
8329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8330             }
8331         }else if(k == e.ENTER && !e.ctrlKey){
8332             e.stopEvent();
8333             ed.completeEdit();
8334             if(e.shiftKey){
8335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8336             }else{
8337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8338             }
8339         }else if(k == e.ESC){
8340             ed.cancelEdit();
8341         }
8342         if(newCell){
8343             g.startEditing(newCell[0], newCell[1]);
8344         }
8345     }
8346 });/*
8347  * Based on:
8348  * Ext JS Library 1.1.1
8349  * Copyright(c) 2006-2007, Ext JS, LLC.
8350  *
8351  * Originally Released Under LGPL - original licence link has changed is not relivant.
8352  *
8353  * Fork - LGPL
8354  * <script type="text/javascript">
8355  */
8356  
8357
8358 /**
8359  * @class Roo.grid.ColumnModel
8360  * @extends Roo.util.Observable
8361  * This is the default implementation of a ColumnModel used by the Grid. It defines
8362  * the columns in the grid.
8363  * <br>Usage:<br>
8364  <pre><code>
8365  var colModel = new Roo.grid.ColumnModel([
8366         {header: "Ticker", width: 60, sortable: true, locked: true},
8367         {header: "Company Name", width: 150, sortable: true},
8368         {header: "Market Cap.", width: 100, sortable: true},
8369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370         {header: "Employees", width: 100, sortable: true, resizable: false}
8371  ]);
8372  </code></pre>
8373  * <p>
8374  
8375  * The config options listed for this class are options which may appear in each
8376  * individual column definition.
8377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8378  * @constructor
8379  * @param {Object} config An Array of column config objects. See this class's
8380  * config objects for details.
8381 */
8382 Roo.grid.ColumnModel = function(config){
8383         /**
8384      * The config passed into the constructor
8385      */
8386     this.config = []; //config;
8387     this.lookup = {};
8388
8389     // if no id, create one
8390     // if the column does not have a dataIndex mapping,
8391     // map it to the order it is in the config
8392     for(var i = 0, len = config.length; i < len; i++){
8393         this.addColumn(config[i]);
8394         
8395     }
8396
8397     /**
8398      * The width of columns which have no width specified (defaults to 100)
8399      * @type Number
8400      */
8401     this.defaultWidth = 100;
8402
8403     /**
8404      * Default sortable of columns which have no sortable specified (defaults to false)
8405      * @type Boolean
8406      */
8407     this.defaultSortable = false;
8408
8409     this.addEvents({
8410         /**
8411              * @event widthchange
8412              * Fires when the width of a column changes.
8413              * @param {ColumnModel} this
8414              * @param {Number} columnIndex The column index
8415              * @param {Number} newWidth The new width
8416              */
8417             "widthchange": true,
8418         /**
8419              * @event headerchange
8420              * Fires when the text of a header changes.
8421              * @param {ColumnModel} this
8422              * @param {Number} columnIndex The column index
8423              * @param {Number} newText The new header text
8424              */
8425             "headerchange": true,
8426         /**
8427              * @event hiddenchange
8428              * Fires when a column is hidden or "unhidden".
8429              * @param {ColumnModel} this
8430              * @param {Number} columnIndex The column index
8431              * @param {Boolean} hidden true if hidden, false otherwise
8432              */
8433             "hiddenchange": true,
8434             /**
8435          * @event columnmoved
8436          * Fires when a column is moved.
8437          * @param {ColumnModel} this
8438          * @param {Number} oldIndex
8439          * @param {Number} newIndex
8440          */
8441         "columnmoved" : true,
8442         /**
8443          * @event columlockchange
8444          * Fires when a column's locked state is changed
8445          * @param {ColumnModel} this
8446          * @param {Number} colIndex
8447          * @param {Boolean} locked true if locked
8448          */
8449         "columnlockchange" : true
8450     });
8451     Roo.grid.ColumnModel.superclass.constructor.call(this);
8452 };
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8454     /**
8455      * @cfg {String} header The header text to display in the Grid view.
8456      */
8457         /**
8458      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8459      */
8460         /**
8461      * @cfg {String} smHeader Header at Bootsrap Small width
8462      */
8463         /**
8464      * @cfg {String} mdHeader Header at Bootsrap Medium width
8465      */
8466         /**
8467      * @cfg {String} lgHeader Header at Bootsrap Large width
8468      */
8469         /**
8470      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8471      */
8472     /**
8473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475      * specified, the column's index is used as an index into the Record's data Array.
8476      */
8477     /**
8478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8480      */
8481     /**
8482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483      * Defaults to the value of the {@link #defaultSortable} property.
8484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8485      */
8486     /**
8487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8491      */
8492     /**
8493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8494      */
8495     /**
8496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8497      */
8498     /**
8499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8503      */
8504        /**
8505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8506      */
8507     /**
8508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8512      */
8513     /**
8514      * @cfg {String} cursor (Optional)
8515      */
8516     /**
8517      * @cfg {String} tooltip (Optional)
8518      */
8519     /**
8520      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528     /**
8529      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531         /**
8532      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * Returns the id of the column at the specified index.
8536      * @param {Number} index The column index
8537      * @return {String} the id
8538      */
8539     getColumnId : function(index){
8540         return this.config[index].id;
8541     },
8542
8543     /**
8544      * Returns the column for a specified id.
8545      * @param {String} id The column id
8546      * @return {Object} the column
8547      */
8548     getColumnById : function(id){
8549         return this.lookup[id];
8550     },
8551
8552     
8553     /**
8554      * Returns the column Object for a specified dataIndex.
8555      * @param {String} dataIndex The column dataIndex
8556      * @return {Object|Boolean} the column or false if not found
8557      */
8558     getColumnByDataIndex: function(dataIndex){
8559         var index = this.findColumnIndex(dataIndex);
8560         return index > -1 ? this.config[index] : false;
8561     },
8562     
8563     /**
8564      * Returns the index for a specified column id.
8565      * @param {String} id The column id
8566      * @return {Number} the index, or -1 if not found
8567      */
8568     getIndexById : function(id){
8569         for(var i = 0, len = this.config.length; i < len; i++){
8570             if(this.config[i].id == id){
8571                 return i;
8572             }
8573         }
8574         return -1;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     
8583     findColumnIndex : function(dataIndex){
8584         for(var i = 0, len = this.config.length; i < len; i++){
8585             if(this.config[i].dataIndex == dataIndex){
8586                 return i;
8587             }
8588         }
8589         return -1;
8590     },
8591     
8592     
8593     moveColumn : function(oldIndex, newIndex){
8594         var c = this.config[oldIndex];
8595         this.config.splice(oldIndex, 1);
8596         this.config.splice(newIndex, 0, c);
8597         this.dataMap = null;
8598         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8599     },
8600
8601     isLocked : function(colIndex){
8602         return this.config[colIndex].locked === true;
8603     },
8604
8605     setLocked : function(colIndex, value, suppressEvent){
8606         if(this.isLocked(colIndex) == value){
8607             return;
8608         }
8609         this.config[colIndex].locked = value;
8610         if(!suppressEvent){
8611             this.fireEvent("columnlockchange", this, colIndex, value);
8612         }
8613     },
8614
8615     getTotalLockedWidth : function(){
8616         var totalWidth = 0;
8617         for(var i = 0; i < this.config.length; i++){
8618             if(this.isLocked(i) && !this.isHidden(i)){
8619                 this.totalWidth += this.getColumnWidth(i);
8620             }
8621         }
8622         return totalWidth;
8623     },
8624
8625     getLockedCount : function(){
8626         for(var i = 0, len = this.config.length; i < len; i++){
8627             if(!this.isLocked(i)){
8628                 return i;
8629             }
8630         }
8631         
8632         return this.config.length;
8633     },
8634
8635     /**
8636      * Returns the number of columns.
8637      * @return {Number}
8638      */
8639     getColumnCount : function(visibleOnly){
8640         if(visibleOnly === true){
8641             var c = 0;
8642             for(var i = 0, len = this.config.length; i < len; i++){
8643                 if(!this.isHidden(i)){
8644                     c++;
8645                 }
8646             }
8647             return c;
8648         }
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654      * @param {Function} fn
8655      * @param {Object} scope (optional)
8656      * @return {Array} result
8657      */
8658     getColumnsBy : function(fn, scope){
8659         var r = [];
8660         for(var i = 0, len = this.config.length; i < len; i++){
8661             var c = this.config[i];
8662             if(fn.call(scope||this, c, i) === true){
8663                 r[r.length] = c;
8664             }
8665         }
8666         return r;
8667     },
8668
8669     /**
8670      * Returns true if the specified column is sortable.
8671      * @param {Number} col The column index
8672      * @return {Boolean}
8673      */
8674     isSortable : function(col){
8675         if(typeof this.config[col].sortable == "undefined"){
8676             return this.defaultSortable;
8677         }
8678         return this.config[col].sortable;
8679     },
8680
8681     /**
8682      * Returns the rendering (formatting) function defined for the column.
8683      * @param {Number} col The column index.
8684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8685      */
8686     getRenderer : function(col){
8687         if(!this.config[col].renderer){
8688             return Roo.grid.ColumnModel.defaultRenderer;
8689         }
8690         return this.config[col].renderer;
8691     },
8692
8693     /**
8694      * Sets the rendering (formatting) function for a column.
8695      * @param {Number} col The column index
8696      * @param {Function} fn The function to use to process the cell's raw data
8697      * to return HTML markup for the grid view. The render function is called with
8698      * the following parameters:<ul>
8699      * <li>Data value.</li>
8700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701      * <li>css A CSS style string to apply to the table cell.</li>
8702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704      * <li>Row index</li>
8705      * <li>Column index</li>
8706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8707      */
8708     setRenderer : function(col, fn){
8709         this.config[col].renderer = fn;
8710     },
8711
8712     /**
8713      * Returns the width for the specified column.
8714      * @param {Number} col The column index
8715      * @param (optional) {String} gridSize bootstrap width size.
8716      * @return {Number}
8717      */
8718     getColumnWidth : function(col, gridSize)
8719         {
8720                 var cfg = this.config[col];
8721                 
8722                 if (typeof(gridSize) == 'undefined') {
8723                         return cfg.width * 1 || this.defaultWidth;
8724                 }
8725                 if (gridSize === false) { // if we set it..
8726                         return cfg.width || false;
8727                 }
8728                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8729                 
8730                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8732                                 continue;
8733                         }
8734                         return cfg[ sizes[i] ];
8735                 }
8736                 return 1;
8737                 
8738     },
8739
8740     /**
8741      * Sets the width for a column.
8742      * @param {Number} col The column index
8743      * @param {Number} width The new width
8744      */
8745     setColumnWidth : function(col, width, suppressEvent){
8746         this.config[col].width = width;
8747         this.totalWidth = null;
8748         if(!suppressEvent){
8749              this.fireEvent("widthchange", this, col, width);
8750         }
8751     },
8752
8753     /**
8754      * Returns the total width of all columns.
8755      * @param {Boolean} includeHidden True to include hidden column widths
8756      * @return {Number}
8757      */
8758     getTotalWidth : function(includeHidden){
8759         if(!this.totalWidth){
8760             this.totalWidth = 0;
8761             for(var i = 0, len = this.config.length; i < len; i++){
8762                 if(includeHidden || !this.isHidden(i)){
8763                     this.totalWidth += this.getColumnWidth(i);
8764                 }
8765             }
8766         }
8767         return this.totalWidth;
8768     },
8769
8770     /**
8771      * Returns the header for the specified column.
8772      * @param {Number} col The column index
8773      * @return {String}
8774      */
8775     getColumnHeader : function(col){
8776         return this.config[col].header;
8777     },
8778
8779     /**
8780      * Sets the header for a column.
8781      * @param {Number} col The column index
8782      * @param {String} header The new header
8783      */
8784     setColumnHeader : function(col, header){
8785         this.config[col].header = header;
8786         this.fireEvent("headerchange", this, col, header);
8787     },
8788
8789     /**
8790      * Returns the tooltip for the specified column.
8791      * @param {Number} col The column index
8792      * @return {String}
8793      */
8794     getColumnTooltip : function(col){
8795             return this.config[col].tooltip;
8796     },
8797     /**
8798      * Sets the tooltip for a column.
8799      * @param {Number} col The column index
8800      * @param {String} tooltip The new tooltip
8801      */
8802     setColumnTooltip : function(col, tooltip){
8803             this.config[col].tooltip = tooltip;
8804     },
8805
8806     /**
8807      * Returns the dataIndex for the specified column.
8808      * @param {Number} col The column index
8809      * @return {Number}
8810      */
8811     getDataIndex : function(col){
8812         return this.config[col].dataIndex;
8813     },
8814
8815     /**
8816      * Sets the dataIndex for a column.
8817      * @param {Number} col The column index
8818      * @param {Number} dataIndex The new dataIndex
8819      */
8820     setDataIndex : function(col, dataIndex){
8821         this.config[col].dataIndex = dataIndex;
8822     },
8823
8824     
8825     
8826     /**
8827      * Returns true if the cell is editable.
8828      * @param {Number} colIndex The column index
8829      * @param {Number} rowIndex The row index - this is nto actually used..?
8830      * @return {Boolean}
8831      */
8832     isCellEditable : function(colIndex, rowIndex){
8833         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834     },
8835
8836     /**
8837      * Returns the editor defined for the cell/column.
8838      * return false or null to disable editing.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index
8841      * @return {Object}
8842      */
8843     getCellEditor : function(colIndex, rowIndex){
8844         return this.config[colIndex].editor;
8845     },
8846
8847     /**
8848      * Sets if a column is editable.
8849      * @param {Number} col The column index
8850      * @param {Boolean} editable True if the column is editable
8851      */
8852     setEditable : function(col, editable){
8853         this.config[col].editable = editable;
8854     },
8855
8856
8857     /**
8858      * Returns true if the column is hidden.
8859      * @param {Number} colIndex The column index
8860      * @return {Boolean}
8861      */
8862     isHidden : function(colIndex){
8863         return this.config[colIndex].hidden;
8864     },
8865
8866
8867     /**
8868      * Returns true if the column width cannot be changed
8869      */
8870     isFixed : function(colIndex){
8871         return this.config[colIndex].fixed;
8872     },
8873
8874     /**
8875      * Returns true if the column can be resized
8876      * @return {Boolean}
8877      */
8878     isResizable : function(colIndex){
8879         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8880     },
8881     /**
8882      * Sets if a column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @param {Boolean} hidden True if the column is hidden
8885      */
8886     setHidden : function(colIndex, hidden){
8887         this.config[colIndex].hidden = hidden;
8888         this.totalWidth = null;
8889         this.fireEvent("hiddenchange", this, colIndex, hidden);
8890     },
8891
8892     /**
8893      * Sets the editor for a column.
8894      * @param {Number} col The column index
8895      * @param {Object} editor The editor object
8896      */
8897     setEditor : function(col, editor){
8898         this.config[col].editor = editor;
8899     },
8900     /**
8901      * Add a column (experimental...) - defaults to adding to the end..
8902      * @param {Object} config 
8903     */
8904     addColumn : function(c)
8905     {
8906     
8907         var i = this.config.length;
8908         this.config[i] = c;
8909         
8910         if(typeof c.dataIndex == "undefined"){
8911             c.dataIndex = i;
8912         }
8913         if(typeof c.renderer == "string"){
8914             c.renderer = Roo.util.Format[c.renderer];
8915         }
8916         if(typeof c.id == "undefined"){
8917             c.id = Roo.id();
8918         }
8919         if(c.editor && c.editor.xtype){
8920             c.editor  = Roo.factory(c.editor, Roo.grid);
8921         }
8922         if(c.editor && c.editor.isFormField){
8923             c.editor = new Roo.grid.GridEditor(c.editor);
8924         }
8925         this.lookup[c.id] = c;
8926     }
8927     
8928 });
8929
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8931 {
8932     if(typeof value == "object") {
8933         return value;
8934     }
8935         if(typeof value == "string" && value.length < 1){
8936             return "&#160;";
8937         }
8938     
8939         return String.format("{0}", value);
8940 };
8941
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8944 /*
8945  * Based on:
8946  * Ext JS Library 1.1.1
8947  * Copyright(c) 2006-2007, Ext JS, LLC.
8948  *
8949  * Originally Released Under LGPL - original licence link has changed is not relivant.
8950  *
8951  * Fork - LGPL
8952  * <script type="text/javascript">
8953  */
8954  
8955 /**
8956  * @class Roo.LoadMask
8957  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8958  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8960  * element's UpdateManager load indicator and will be destroyed after the initial load.
8961  * @constructor
8962  * Create a new LoadMask
8963  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964  * @param {Object} config The config object
8965  */
8966 Roo.LoadMask = function(el, config){
8967     this.el = Roo.get(el);
8968     Roo.apply(this, config);
8969     if(this.store){
8970         this.store.on('beforeload', this.onBeforeLoad, this);
8971         this.store.on('load', this.onLoad, this);
8972         this.store.on('loadexception', this.onLoadException, this);
8973         this.removeMask = false;
8974     }else{
8975         var um = this.el.getUpdateManager();
8976         um.showLoadIndicator = false; // disable the default indicator
8977         um.on('beforeupdate', this.onBeforeLoad, this);
8978         um.on('update', this.onLoad, this);
8979         um.on('failure', this.onLoad, this);
8980         this.removeMask = true;
8981     }
8982 };
8983
8984 Roo.LoadMask.prototype = {
8985     /**
8986      * @cfg {Boolean} removeMask
8987      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8989      */
8990     removeMask : false,
8991     /**
8992      * @cfg {String} msg
8993      * The text to display in a centered loading message box (defaults to 'Loading...')
8994      */
8995     msg : 'Loading...',
8996     /**
8997      * @cfg {String} msgCls
8998      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8999      */
9000     msgCls : 'x-mask-loading',
9001
9002     /**
9003      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9004      * @type Boolean
9005      */
9006     disabled: false,
9007
9008     /**
9009      * Disables the mask to prevent it from being displayed
9010      */
9011     disable : function(){
9012        this.disabled = true;
9013     },
9014
9015     /**
9016      * Enables the mask so that it can be displayed
9017      */
9018     enable : function(){
9019         this.disabled = false;
9020     },
9021     
9022     onLoadException : function()
9023     {
9024         Roo.log(arguments);
9025         
9026         if (typeof(arguments[3]) != 'undefined') {
9027             Roo.MessageBox.alert("Error loading",arguments[3]);
9028         } 
9029         /*
9030         try {
9031             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9033             }   
9034         } catch(e) {
9035             
9036         }
9037         */
9038     
9039         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9040     },
9041     // private
9042     onLoad : function()
9043     {
9044         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045     },
9046
9047     // private
9048     onBeforeLoad : function(){
9049         if(!this.disabled){
9050             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9051         }
9052     },
9053
9054     // private
9055     destroy : function(){
9056         if(this.store){
9057             this.store.un('beforeload', this.onBeforeLoad, this);
9058             this.store.un('load', this.onLoad, this);
9059             this.store.un('loadexception', this.onLoadException, this);
9060         }else{
9061             var um = this.el.getUpdateManager();
9062             um.un('beforeupdate', this.onBeforeLoad, this);
9063             um.un('update', this.onLoad, this);
9064             um.un('failure', this.onLoad, this);
9065         }
9066     }
9067 };/**
9068  * @class Roo.bootstrap.Table
9069  * @licence LGBL
9070  * @extends Roo.bootstrap.Component
9071  * @children Roo.bootstrap.TableBody
9072  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9073  * Similar to Roo.grid.Grid
9074  * <pre><code>
9075  var table = Roo.factory({
9076     xtype : 'Table',
9077     xns : Roo.bootstrap,
9078     autoSizeColumns: true,
9079     
9080     
9081     store : {
9082         xtype : 'Store',
9083         xns : Roo.data,
9084         remoteSort : true,
9085         sortInfo : { direction : 'ASC', field: 'name' },
9086         proxy : {
9087            xtype : 'HttpProxy',
9088            xns : Roo.data,
9089            method : 'GET',
9090            url : 'https://example.com/some.data.url.json'
9091         },
9092         reader : {
9093            xtype : 'JsonReader',
9094            xns : Roo.data,
9095            fields : [ 'id', 'name', whatever' ],
9096            id : 'id',
9097            root : 'data'
9098         }
9099     },
9100     cm : [
9101         {
9102             xtype : 'ColumnModel',
9103             xns : Roo.grid,
9104             align : 'center',
9105             cursor : 'pointer',
9106             dataIndex : 'is_in_group',
9107             header : "Name",
9108             sortable : true,
9109             renderer : function(v, x , r) {  
9110             
9111                 return String.format("{0}", v)
9112             }
9113             width : 3
9114         } // more columns..
9115     ],
9116     selModel : {
9117         xtype : 'RowSelectionModel',
9118         xns : Roo.bootstrap.Table
9119         // you can add listeners to catch selection change here....
9120     }
9121      
9122
9123  });
9124  // set any options
9125  grid.render(Roo.get("some-div"));
9126 </code></pre>
9127
9128 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9129
9130
9131
9132  *
9133  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134  * @cfg {Roo.data.Store} store The data store to use
9135  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9136  * 
9137  * @cfg {String} cls table class
9138  *
9139  * 
9140  * @cfg {boolean} striped Should the rows be alternative striped
9141  * @cfg {boolean} bordered Add borders to the table
9142  * @cfg {boolean} hover Add hover highlighting
9143  * @cfg {boolean} condensed Format condensed
9144  * @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,
9145  *                also adds table-responsive (see bootstrap docs for details)
9146  * @cfg {Boolean} loadMask (true|false) default false
9147  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9148  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9149  * @cfg {Boolean} rowSelection (true|false) default false
9150  * @cfg {Boolean} cellSelection (true|false) default false
9151  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9152  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9153  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9154  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9155  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9156  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9157  * 
9158  * @constructor
9159  * Create a new Table
9160  * @param {Object} config The config object
9161  */
9162
9163 Roo.bootstrap.Table = function(config)
9164 {
9165     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9166      
9167     // BC...
9168     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9169     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9170     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9171     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9172     
9173     this.view = this; // compat with grid.
9174     
9175     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9176     if (this.sm) {
9177         this.sm.grid = this;
9178         this.selModel = Roo.factory(this.sm, Roo.grid);
9179         this.sm = this.selModel;
9180         this.sm.xmodule = this.xmodule || false;
9181     }
9182     
9183     if (this.cm && typeof(this.cm.config) == 'undefined') {
9184         this.colModel = new Roo.grid.ColumnModel(this.cm);
9185         this.cm = this.colModel;
9186         this.cm.xmodule = this.xmodule || false;
9187     }
9188     if (this.store) {
9189         this.store= Roo.factory(this.store, Roo.data);
9190         this.ds = this.store;
9191         this.ds.xmodule = this.xmodule || false;
9192          
9193     }
9194     if (this.footer && this.store) {
9195         this.footer.dataSource = this.ds;
9196         this.footer = Roo.factory(this.footer);
9197     }
9198     
9199     /** @private */
9200     this.addEvents({
9201         /**
9202          * @event cellclick
9203          * Fires when a cell is clicked
9204          * @param {Roo.bootstrap.Table} this
9205          * @param {Roo.Element} el
9206          * @param {Number} rowIndex
9207          * @param {Number} columnIndex
9208          * @param {Roo.EventObject} e
9209          */
9210         "cellclick" : true,
9211         /**
9212          * @event celldblclick
9213          * Fires when a cell is double clicked
9214          * @param {Roo.bootstrap.Table} this
9215          * @param {Roo.Element} el
9216          * @param {Number} rowIndex
9217          * @param {Number} columnIndex
9218          * @param {Roo.EventObject} e
9219          */
9220         "celldblclick" : true,
9221         /**
9222          * @event rowclick
9223          * Fires when a row is clicked
9224          * @param {Roo.bootstrap.Table} this
9225          * @param {Roo.Element} el
9226          * @param {Number} rowIndex
9227          * @param {Roo.EventObject} e
9228          */
9229         "rowclick" : true,
9230         /**
9231          * @event rowdblclick
9232          * Fires when a row is double clicked
9233          * @param {Roo.bootstrap.Table} this
9234          * @param {Roo.Element} el
9235          * @param {Number} rowIndex
9236          * @param {Roo.EventObject} e
9237          */
9238         "rowdblclick" : true,
9239         /**
9240          * @event mouseover
9241          * Fires when a mouseover occur
9242          * @param {Roo.bootstrap.Table} this
9243          * @param {Roo.Element} el
9244          * @param {Number} rowIndex
9245          * @param {Number} columnIndex
9246          * @param {Roo.EventObject} e
9247          */
9248         "mouseover" : true,
9249         /**
9250          * @event mouseout
9251          * Fires when a mouseout occur
9252          * @param {Roo.bootstrap.Table} this
9253          * @param {Roo.Element} el
9254          * @param {Number} rowIndex
9255          * @param {Number} columnIndex
9256          * @param {Roo.EventObject} e
9257          */
9258         "mouseout" : true,
9259         /**
9260          * @event rowclass
9261          * Fires when a row is rendered, so you can change add a style to it.
9262          * @param {Roo.bootstrap.Table} this
9263          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9264          */
9265         'rowclass' : true,
9266           /**
9267          * @event rowsrendered
9268          * Fires when all the  rows have been rendered
9269          * @param {Roo.bootstrap.Table} this
9270          */
9271         'rowsrendered' : true,
9272         /**
9273          * @event contextmenu
9274          * The raw contextmenu event for the entire grid.
9275          * @param {Roo.EventObject} e
9276          */
9277         "contextmenu" : true,
9278         /**
9279          * @event rowcontextmenu
9280          * Fires when a row is right clicked
9281          * @param {Roo.bootstrap.Table} this
9282          * @param {Number} rowIndex
9283          * @param {Roo.EventObject} e
9284          */
9285         "rowcontextmenu" : true,
9286         /**
9287          * @event cellcontextmenu
9288          * Fires when a cell is right clicked
9289          * @param {Roo.bootstrap.Table} this
9290          * @param {Number} rowIndex
9291          * @param {Number} cellIndex
9292          * @param {Roo.EventObject} e
9293          */
9294          "cellcontextmenu" : true,
9295          /**
9296          * @event headercontextmenu
9297          * Fires when a header is right clicked
9298          * @param {Roo.bootstrap.Table} this
9299          * @param {Number} columnIndex
9300          * @param {Roo.EventObject} e
9301          */
9302         "headercontextmenu" : true,
9303         /**
9304          * @event mousedown
9305          * The raw mousedown event for the entire grid.
9306          * @param {Roo.EventObject} e
9307          */
9308         "mousedown" : true
9309         
9310     });
9311 };
9312
9313 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9314     
9315     cls: false,
9316     
9317     striped : false,
9318     scrollBody : false,
9319     bordered: false,
9320     hover:  false,
9321     condensed : false,
9322     responsive : false,
9323     sm : false,
9324     cm : false,
9325     store : false,
9326     loadMask : false,
9327     footerShow : true,
9328     headerShow : true,
9329     enableColumnResize: true,
9330   
9331     rowSelection : false,
9332     cellSelection : false,
9333     layout : false,
9334
9335     minColumnWidth : 50,
9336     
9337     // Roo.Element - the tbody
9338     bodyEl: false,  // <tbody> Roo.Element - thead element    
9339     headEl: false,  // <thead> Roo.Element - thead element
9340     resizeProxy : false, // proxy element for dragging?
9341
9342
9343     
9344     container: false, // used by gridpanel...
9345     
9346     lazyLoad : false,
9347     
9348     CSS : Roo.util.CSS,
9349     
9350     auto_hide_footer : false,
9351     
9352     view: false, // actually points to this..
9353     
9354     getAutoCreate : function()
9355     {
9356         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9357         
9358         cfg = {
9359             tag: 'table',
9360             cls : 'table', 
9361             cn : []
9362         };
9363         // this get's auto added by panel.Grid
9364         if (this.scrollBody) {
9365             cfg.cls += ' table-body-fixed';
9366         }    
9367         if (this.striped) {
9368             cfg.cls += ' table-striped';
9369         }
9370         
9371         if (this.hover) {
9372             cfg.cls += ' table-hover';
9373         }
9374         if (this.bordered) {
9375             cfg.cls += ' table-bordered';
9376         }
9377         if (this.condensed) {
9378             cfg.cls += ' table-condensed';
9379         }
9380         
9381         if (this.responsive) {
9382             cfg.cls += ' table-responsive';
9383         }
9384         
9385         if (this.cls) {
9386             cfg.cls+=  ' ' +this.cls;
9387         }
9388         
9389         
9390         
9391         if (this.layout) {
9392             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9393         }
9394         
9395         if(this.store || this.cm){
9396             if(this.headerShow){
9397                 cfg.cn.push(this.renderHeader());
9398             }
9399             
9400             cfg.cn.push(this.renderBody());
9401             
9402             if(this.footerShow){
9403                 cfg.cn.push(this.renderFooter());
9404             }
9405             // where does this come from?
9406             //cfg.cls+=  ' TableGrid';
9407         }
9408         
9409         return { cn : [ cfg ] };
9410     },
9411     
9412     initEvents : function()
9413     {   
9414         if(!this.store || !this.cm){
9415             return;
9416         }
9417         if (this.selModel) {
9418             this.selModel.initEvents();
9419         }
9420         
9421         
9422         //Roo.log('initEvents with ds!!!!');
9423         
9424         this.bodyEl = this.el.select('tbody', true).first();
9425         this.headEl = this.el.select('thead', true).first();
9426         this.mainFoot = this.el.select('tfoot', true).first();
9427         
9428         
9429         
9430         
9431         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9432             e.on('click', this.sort, this);
9433         }, this);
9434         
9435         
9436         // why is this done????? = it breaks dialogs??
9437         //this.parent().el.setStyle('position', 'relative');
9438         
9439         
9440         if (this.footer) {
9441             this.footer.parentId = this.id;
9442             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9443             
9444             if(this.lazyLoad){
9445                 this.el.select('tfoot tr td').first().addClass('hide');
9446             }
9447         } 
9448         
9449         if(this.loadMask) {
9450             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9451         }
9452         
9453         this.store.on('load', this.onLoad, this);
9454         this.store.on('beforeload', this.onBeforeLoad, this);
9455         this.store.on('update', this.onUpdate, this);
9456         this.store.on('add', this.onAdd, this);
9457         this.store.on("clear", this.clear, this);
9458         
9459         this.el.on("contextmenu", this.onContextMenu, this);
9460         
9461         
9462         this.cm.on("headerchange", this.onHeaderChange, this);
9463         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9464
9465  //?? does bodyEl get replaced on render?
9466         this.bodyEl.on("click", this.onClick, this);
9467         this.bodyEl.on("dblclick", this.onDblClick, this);        
9468         this.bodyEl.on('scroll', this.onBodyScroll, this);
9469
9470         // guessing mainbody will work - this relays usually caught by selmodel at present.
9471         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9472   
9473   
9474         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9475         
9476   
9477         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9478             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9479         }
9480         
9481         this.initCSS();
9482     },
9483     // Compatibility with grid - we implement all the view features at present.
9484     getView : function()
9485     {
9486         return this;
9487     },
9488     
9489     initCSS : function()
9490     {
9491         
9492         
9493         var cm = this.cm, styles = [];
9494         this.CSS.removeStyleSheet(this.id + '-cssrules');
9495         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9496         // we can honour xs/sm/md/xl  as widths...
9497         // we first have to decide what widht we are currently at...
9498         var sz = Roo.getGridSize();
9499         
9500         var total = 0;
9501         var last = -1;
9502         var cols = []; // visable cols.
9503         var total_abs = 0;
9504         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9505             var w = cm.getColumnWidth(i, false);
9506             if(cm.isHidden(i)){
9507                 cols.push( { rel : false, abs : 0 });
9508                 continue;
9509             }
9510             if (w !== false) {
9511                 cols.push( { rel : false, abs : w });
9512                 total_abs += w;
9513                 last = i; // not really..
9514                 continue;
9515             }
9516             var w = cm.getColumnWidth(i, sz);
9517             if (w > 0) {
9518                 last = i
9519             }
9520             total += w;
9521             cols.push( { rel : w, abs : false });
9522         }
9523         
9524         var avail = this.bodyEl.dom.clientWidth - total_abs;
9525         
9526         var unitWidth = Math.floor(avail / total);
9527         var rem = avail - (unitWidth * total);
9528         
9529         var hidden, width, pos = 0 , splithide , left;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             
9532             hidden = 'display:none;';
9533             left = '';
9534             width  = 'width:0px;';
9535             splithide = '';
9536             if(!cm.isHidden(i)){
9537                 hidden = '';
9538                 
9539                 
9540                 // we can honour xs/sm/md/xl ?
9541                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9542                 if (w===0) {
9543                     hidden = 'display:none;';
9544                 }
9545                 // width should return a small number...
9546                 if (i == last) {
9547                     w+=rem; // add the remaining with..
9548                 }
9549                 pos += w;
9550                 left = "left:" + (pos -4) + "px;";
9551                 width = "width:" + w+ "px;";
9552                 
9553             }
9554             if (this.responsive) {
9555                 width = '';
9556                 left = '';
9557                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9558                 splithide = 'display: none;';
9559             }
9560             
9561             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9562             if (this.headEl) {
9563                 if (i == last) {
9564                     splithide = 'display:none;';
9565                 }
9566                 
9567                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9568                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9569                 );
9570             }
9571             
9572         }
9573         //Roo.log(styles.join(''));
9574         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9575         
9576     },
9577     
9578     
9579     
9580     onContextMenu : function(e, t)
9581     {
9582         this.processEvent("contextmenu", e);
9583     },
9584     
9585     processEvent : function(name, e)
9586     {
9587         if (name != 'touchstart' ) {
9588             this.fireEvent(name, e);    
9589         }
9590         
9591         var t = e.getTarget();
9592         
9593         var cell = Roo.get(t);
9594         
9595         if(!cell){
9596             return;
9597         }
9598         
9599         if(cell.findParent('tfoot', false, true)){
9600             return;
9601         }
9602         
9603         if(cell.findParent('thead', false, true)){
9604             
9605             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9606                 cell = Roo.get(t).findParent('th', false, true);
9607                 if (!cell) {
9608                     Roo.log("failed to find th in thead?");
9609                     Roo.log(e.getTarget());
9610                     return;
9611                 }
9612             }
9613             
9614             var cellIndex = cell.dom.cellIndex;
9615             
9616             var ename = name == 'touchstart' ? 'click' : name;
9617             this.fireEvent("header" + ename, this, cellIndex, e);
9618             
9619             return;
9620         }
9621         
9622         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9623             cell = Roo.get(t).findParent('td', false, true);
9624             if (!cell) {
9625                 Roo.log("failed to find th in tbody?");
9626                 Roo.log(e.getTarget());
9627                 return;
9628             }
9629         }
9630         
9631         var row = cell.findParent('tr', false, true);
9632         var cellIndex = cell.dom.cellIndex;
9633         var rowIndex = row.dom.rowIndex - 1;
9634         
9635         if(row !== false){
9636             
9637             this.fireEvent("row" + name, this, rowIndex, e);
9638             
9639             if(cell !== false){
9640             
9641                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9642             }
9643         }
9644         
9645     },
9646     
9647     onMouseover : function(e, el)
9648     {
9649         var cell = Roo.get(el);
9650         
9651         if(!cell){
9652             return;
9653         }
9654         
9655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656             cell = cell.findParent('td', false, true);
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1; // start from 0
9662         
9663         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9664         
9665     },
9666     
9667     onMouseout : function(e, el)
9668     {
9669         var cell = Roo.get(el);
9670         
9671         if(!cell){
9672             return;
9673         }
9674         
9675         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9676             cell = cell.findParent('td', false, true);
9677         }
9678         
9679         var row = cell.findParent('tr', false, true);
9680         var cellIndex = cell.dom.cellIndex;
9681         var rowIndex = row.dom.rowIndex - 1; // start from 0
9682         
9683         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9684         
9685     },
9686     
9687     onClick : function(e, el)
9688     {
9689         var cell = Roo.get(el);
9690         
9691         if(!cell || (!this.cellSelection && !this.rowSelection)){
9692             return;
9693         }
9694         
9695         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9696             cell = cell.findParent('td', false, true);
9697         }
9698         
9699         if(!cell || typeof(cell) == 'undefined'){
9700             return;
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         
9705         if(!row || typeof(row) == 'undefined'){
9706             return;
9707         }
9708         
9709         var cellIndex = cell.dom.cellIndex;
9710         var rowIndex = this.getRowIndex(row);
9711         
9712         // why??? - should these not be based on SelectionModel?
9713         //if(this.cellSelection){
9714             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9715         //}
9716         
9717         //if(this.rowSelection){
9718             this.fireEvent('rowclick', this, row, rowIndex, e);
9719         //}
9720          
9721     },
9722         
9723     onDblClick : function(e,el)
9724     {
9725         var cell = Roo.get(el);
9726         
9727         if(!cell || (!this.cellSelection && !this.rowSelection)){
9728             return;
9729         }
9730         
9731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9732             cell = cell.findParent('td', false, true);
9733         }
9734         
9735         if(!cell || typeof(cell) == 'undefined'){
9736             return;
9737         }
9738         
9739         var row = cell.findParent('tr', false, true);
9740         
9741         if(!row || typeof(row) == 'undefined'){
9742             return;
9743         }
9744         
9745         var cellIndex = cell.dom.cellIndex;
9746         var rowIndex = this.getRowIndex(row);
9747         
9748         if(this.cellSelection){
9749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9750         }
9751         
9752         if(this.rowSelection){
9753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9754         }
9755     },
9756     findRowIndex : function(el)
9757     {
9758         var cell = Roo.get(el);
9759         if(!cell) {
9760             return false;
9761         }
9762         var row = cell.findParent('tr', false, true);
9763         
9764         if(!row || typeof(row) == 'undefined'){
9765             return false;
9766         }
9767         return this.getRowIndex(row);
9768     },
9769     sort : function(e,el)
9770     {
9771         var col = Roo.get(el);
9772         
9773         if(!col.hasClass('sortable')){
9774             return;
9775         }
9776         
9777         var sort = col.attr('sort');
9778         var dir = 'ASC';
9779         
9780         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9781             dir = 'DESC';
9782         }
9783         
9784         this.store.sortInfo = {field : sort, direction : dir};
9785         
9786         if (this.footer) {
9787             Roo.log("calling footer first");
9788             this.footer.onClick('first');
9789         } else {
9790         
9791             this.store.load({ params : { start : 0 } });
9792         }
9793     },
9794     
9795     renderHeader : function()
9796     {
9797         var header = {
9798             tag: 'thead',
9799             cn : []
9800         };
9801         
9802         var cm = this.cm;
9803         this.totalWidth = 0;
9804         
9805         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9806             
9807             var config = cm.config[i];
9808             
9809             var c = {
9810                 tag: 'th',
9811                 cls : 'x-hcol-' + i,
9812                 style : '',
9813                 
9814                 html: cm.getColumnHeader(i)
9815             };
9816             
9817             var tooltip = cm.getColumnTooltip(i);
9818             if (tooltip) {
9819                 c.tooltip = tooltip;
9820             }
9821             
9822             
9823             var hh = '';
9824             
9825             if(typeof(config.sortable) != 'undefined' && config.sortable){
9826                 c.cls += ' sortable';
9827                 c.html = '<i class="fa"></i>' + c.html;
9828             }
9829             
9830             // could use BS4 hidden-..-down 
9831             
9832             if(typeof(config.lgHeader) != 'undefined'){
9833                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9834             }
9835             
9836             if(typeof(config.mdHeader) != 'undefined'){
9837                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9838             }
9839             
9840             if(typeof(config.smHeader) != 'undefined'){
9841                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9842             }
9843             
9844             if(typeof(config.xsHeader) != 'undefined'){
9845                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9846             }
9847             
9848             if(hh.length){
9849                 c.html = hh;
9850             }
9851             
9852             if(typeof(config.tooltip) != 'undefined'){
9853                 c.tooltip = config.tooltip;
9854             }
9855             
9856             if(typeof(config.colspan) != 'undefined'){
9857                 c.colspan = config.colspan;
9858             }
9859             
9860             // hidden is handled by CSS now
9861             
9862             if(typeof(config.dataIndex) != 'undefined'){
9863                 c.sort = config.dataIndex;
9864             }
9865             
9866            
9867             
9868             if(typeof(config.align) != 'undefined' && config.align.length){
9869                 c.style += ' text-align:' + config.align + ';';
9870             }
9871             
9872             /* width is done in CSS
9873              *if(typeof(config.width) != 'undefined'){
9874                 c.style += ' width:' + config.width + 'px;';
9875                 this.totalWidth += config.width;
9876             } else {
9877                 this.totalWidth += 100; // assume minimum of 100 per column?
9878             }
9879             */
9880             
9881             if(typeof(config.cls) != 'undefined'){
9882                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9883             }
9884             // this is the bit that doesnt reall work at all...
9885             
9886             if (this.responsive) {
9887                  
9888             
9889                 ['xs','sm','md','lg'].map(function(size){
9890                     
9891                     if(typeof(config[size]) == 'undefined'){
9892                         return;
9893                     }
9894                      
9895                     if (!config[size]) { // 0 = hidden
9896                         // BS 4 '0' is treated as hide that column and below.
9897                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9898                         return;
9899                     }
9900                     
9901                     c.cls += ' col-' + size + '-' + config[size] + (
9902                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9903                     );
9904                     
9905                     
9906                 });
9907             }
9908             // at the end?
9909             
9910             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9911             
9912             
9913             
9914             
9915             header.cn.push(c)
9916         }
9917         
9918         return header;
9919     },
9920     
9921     renderBody : function()
9922     {
9923         var body = {
9924             tag: 'tbody',
9925             cn : [
9926                 {
9927                     tag: 'tr',
9928                     cn : [
9929                         {
9930                             tag : 'td',
9931                             colspan :  this.cm.getColumnCount()
9932                         }
9933                     ]
9934                 }
9935             ]
9936         };
9937         
9938         return body;
9939     },
9940     
9941     renderFooter : function()
9942     {
9943         var footer = {
9944             tag: 'tfoot',
9945             cn : [
9946                 {
9947                     tag: 'tr',
9948                     cn : [
9949                         {
9950                             tag : 'td',
9951                             colspan :  this.cm.getColumnCount()
9952                         }
9953                     ]
9954                 }
9955             ]
9956         };
9957         
9958         return footer;
9959     },
9960     
9961     
9962     
9963     onLoad : function()
9964     {
9965 //        Roo.log('ds onload');
9966         this.clear();
9967         
9968         var _this = this;
9969         var cm = this.cm;
9970         var ds = this.store;
9971         
9972         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9973             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9974             if (_this.store.sortInfo) {
9975                     
9976                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9977                     e.select('i', true).addClass(['fa-arrow-up']);
9978                 }
9979                 
9980                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9981                     e.select('i', true).addClass(['fa-arrow-down']);
9982                 }
9983             }
9984         });
9985         
9986         var tbody =  this.bodyEl;
9987               
9988         if(ds.getCount() > 0){
9989             ds.data.each(function(d,rowIndex){
9990                 var row =  this.renderRow(cm, ds, rowIndex);
9991                 
9992                 tbody.createChild(row);
9993                 
9994                 var _this = this;
9995                 
9996                 if(row.cellObjects.length){
9997                     Roo.each(row.cellObjects, function(r){
9998                         _this.renderCellObject(r);
9999                     })
10000                 }
10001                 
10002             }, this);
10003         }
10004         
10005         var tfoot = this.el.select('tfoot', true).first();
10006         
10007         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10008             
10009             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10010             
10011             var total = this.ds.getTotalCount();
10012             
10013             if(this.footer.pageSize < total){
10014                 this.mainFoot.show();
10015             }
10016         }
10017         
10018         Roo.each(this.el.select('tbody td', true).elements, function(e){
10019             e.on('mouseover', _this.onMouseover, _this);
10020         });
10021         
10022         Roo.each(this.el.select('tbody td', true).elements, function(e){
10023             e.on('mouseout', _this.onMouseout, _this);
10024         });
10025         this.fireEvent('rowsrendered', this);
10026         
10027         this.autoSize();
10028         
10029         this.initCSS(); /// resize cols
10030
10031         
10032     },
10033     
10034     
10035     onUpdate : function(ds,record)
10036     {
10037         this.refreshRow(record);
10038         this.autoSize();
10039     },
10040     
10041     onRemove : function(ds, record, index, isUpdate){
10042         if(isUpdate !== true){
10043             this.fireEvent("beforerowremoved", this, index, record);
10044         }
10045         var bt = this.bodyEl.dom;
10046         
10047         var rows = this.el.select('tbody > tr', true).elements;
10048         
10049         if(typeof(rows[index]) != 'undefined'){
10050             bt.removeChild(rows[index].dom);
10051         }
10052         
10053 //        if(bt.rows[index]){
10054 //            bt.removeChild(bt.rows[index]);
10055 //        }
10056         
10057         if(isUpdate !== true){
10058             //this.stripeRows(index);
10059             //this.syncRowHeights(index, index);
10060             //this.layout();
10061             this.fireEvent("rowremoved", this, index, record);
10062         }
10063     },
10064     
10065     onAdd : function(ds, records, rowIndex)
10066     {
10067         //Roo.log('on Add called');
10068         // - note this does not handle multiple adding very well..
10069         var bt = this.bodyEl.dom;
10070         for (var i =0 ; i < records.length;i++) {
10071             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10072             //Roo.log(records[i]);
10073             //Roo.log(this.store.getAt(rowIndex+i));
10074             this.insertRow(this.store, rowIndex + i, false);
10075             return;
10076         }
10077         
10078     },
10079     
10080     
10081     refreshRow : function(record){
10082         var ds = this.store, index;
10083         if(typeof record == 'number'){
10084             index = record;
10085             record = ds.getAt(index);
10086         }else{
10087             index = ds.indexOf(record);
10088             if (index < 0) {
10089                 return; // should not happen - but seems to 
10090             }
10091         }
10092         this.insertRow(ds, index, true);
10093         this.autoSize();
10094         this.onRemove(ds, record, index+1, true);
10095         this.autoSize();
10096         //this.syncRowHeights(index, index);
10097         //this.layout();
10098         this.fireEvent("rowupdated", this, index, record);
10099     },
10100     // private - called by RowSelection
10101     onRowSelect : function(rowIndex){
10102         var row = this.getRowDom(rowIndex);
10103         row.addClass(['bg-info','info']);
10104     },
10105     // private - called by RowSelection
10106     onRowDeselect : function(rowIndex)
10107     {
10108         if (rowIndex < 0) {
10109             return;
10110         }
10111         var row = this.getRowDom(rowIndex);
10112         row.removeClass(['bg-info','info']);
10113     },
10114       /**
10115      * Focuses the specified row.
10116      * @param {Number} row The row index
10117      */
10118     focusRow : function(row)
10119     {
10120         //Roo.log('GridView.focusRow');
10121         var x = this.bodyEl.dom.scrollLeft;
10122         this.focusCell(row, 0, false);
10123         this.bodyEl.dom.scrollLeft = x;
10124
10125     },
10126      /**
10127      * Focuses the specified cell.
10128      * @param {Number} row The row index
10129      * @param {Number} col The column index
10130      * @param {Boolean} hscroll false to disable horizontal scrolling
10131      */
10132     focusCell : function(row, col, hscroll)
10133     {
10134         //Roo.log('GridView.focusCell');
10135         var el = this.ensureVisible(row, col, hscroll);
10136         // not sure what focusEL achives = it's a <a> pos relative 
10137         //this.focusEl.alignTo(el, "tl-tl");
10138         //if(Roo.isGecko){
10139         //    this.focusEl.focus();
10140         //}else{
10141         //    this.focusEl.focus.defer(1, this.focusEl);
10142         //}
10143     },
10144     
10145      /**
10146      * Scrolls the specified cell into view
10147      * @param {Number} row The row index
10148      * @param {Number} col The column index
10149      * @param {Boolean} hscroll false to disable horizontal scrolling
10150      */
10151     ensureVisible : function(row, col, hscroll)
10152     {
10153         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10154         //return null; //disable for testing.
10155         if(typeof row != "number"){
10156             row = row.rowIndex;
10157         }
10158         if(row < 0 && row >= this.ds.getCount()){
10159             return  null;
10160         }
10161         col = (col !== undefined ? col : 0);
10162         var cm = this.cm;
10163         while(cm.isHidden(col)){
10164             col++;
10165         }
10166
10167         var el = this.getCellDom(row, col);
10168         if(!el){
10169             return null;
10170         }
10171         var c = this.bodyEl.dom;
10172
10173         var ctop = parseInt(el.offsetTop, 10);
10174         var cleft = parseInt(el.offsetLeft, 10);
10175         var cbot = ctop + el.offsetHeight;
10176         var cright = cleft + el.offsetWidth;
10177
10178         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10179         var ch = 0; //?? header is not withing the area?
10180         var stop = parseInt(c.scrollTop, 10);
10181         var sleft = parseInt(c.scrollLeft, 10);
10182         var sbot = stop + ch;
10183         var sright = sleft + c.clientWidth;
10184         /*
10185         Roo.log('GridView.ensureVisible:' +
10186                 ' ctop:' + ctop +
10187                 ' c.clientHeight:' + c.clientHeight +
10188                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10189                 ' stop:' + stop +
10190                 ' cbot:' + cbot +
10191                 ' sbot:' + sbot +
10192                 ' ch:' + ch  
10193                 );
10194         */
10195         if(ctop < stop){
10196             c.scrollTop = ctop;
10197             //Roo.log("set scrolltop to ctop DISABLE?");
10198         }else if(cbot > sbot){
10199             //Roo.log("set scrolltop to cbot-ch");
10200             c.scrollTop = cbot-ch;
10201         }
10202
10203         if(hscroll !== false){
10204             if(cleft < sleft){
10205                 c.scrollLeft = cleft;
10206             }else if(cright > sright){
10207                 c.scrollLeft = cright-c.clientWidth;
10208             }
10209         }
10210
10211         return el;
10212     },
10213     
10214     
10215     insertRow : function(dm, rowIndex, isUpdate){
10216         
10217         if(!isUpdate){
10218             this.fireEvent("beforerowsinserted", this, rowIndex);
10219         }
10220             //var s = this.getScrollState();
10221         var row = this.renderRow(this.cm, this.store, rowIndex);
10222         // insert before rowIndex..
10223         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10224         
10225         var _this = this;
10226                 
10227         if(row.cellObjects.length){
10228             Roo.each(row.cellObjects, function(r){
10229                 _this.renderCellObject(r);
10230             })
10231         }
10232             
10233         if(!isUpdate){
10234             this.fireEvent("rowsinserted", this, rowIndex);
10235             //this.syncRowHeights(firstRow, lastRow);
10236             //this.stripeRows(firstRow);
10237             //this.layout();
10238         }
10239         
10240     },
10241     
10242     
10243     getRowDom : function(rowIndex)
10244     {
10245         var rows = this.el.select('tbody > tr', true).elements;
10246         
10247         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10248         
10249     },
10250     getCellDom : function(rowIndex, colIndex)
10251     {
10252         var row = this.getRowDom(rowIndex);
10253         if (row === false) {
10254             return false;
10255         }
10256         var cols = row.select('td', true).elements;
10257         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10258         
10259     },
10260     
10261     // returns the object tree for a tr..
10262   
10263     
10264     renderRow : function(cm, ds, rowIndex) 
10265     {
10266         var d = ds.getAt(rowIndex);
10267         
10268         var row = {
10269             tag : 'tr',
10270             cls : 'x-row-' + rowIndex,
10271             cn : []
10272         };
10273             
10274         var cellObjects = [];
10275         
10276         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10277             var config = cm.config[i];
10278             
10279             var renderer = cm.getRenderer(i);
10280             var value = '';
10281             var id = false;
10282             
10283             if(typeof(renderer) !== 'undefined'){
10284                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10285             }
10286             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10287             // and are rendered into the cells after the row is rendered - using the id for the element.
10288             
10289             if(typeof(value) === 'object'){
10290                 id = Roo.id();
10291                 cellObjects.push({
10292                     container : id,
10293                     cfg : value 
10294                 })
10295             }
10296             
10297             var rowcfg = {
10298                 record: d,
10299                 rowIndex : rowIndex,
10300                 colIndex : i,
10301                 rowClass : ''
10302             };
10303
10304             this.fireEvent('rowclass', this, rowcfg);
10305             
10306             var td = {
10307                 tag: 'td',
10308                 // this might end up displaying HTML?
10309                 // this is too messy... - better to only do it on columsn you know are going to be too long
10310                 //tooltip : (typeof(value) === 'object') ? '' : value,
10311                 cls : rowcfg.rowClass + ' x-col-' + i,
10312                 style: '',
10313                 html: (typeof(value) === 'object') ? '' : value
10314             };
10315             
10316             if (id) {
10317                 td.id = id;
10318             }
10319             
10320             if(typeof(config.colspan) != 'undefined'){
10321                 td.colspan = config.colspan;
10322             }
10323             
10324             
10325             
10326             if(typeof(config.align) != 'undefined' && config.align.length){
10327                 td.style += ' text-align:' + config.align + ';';
10328             }
10329             if(typeof(config.valign) != 'undefined' && config.valign.length){
10330                 td.style += ' vertical-align:' + config.valign + ';';
10331             }
10332             /*
10333             if(typeof(config.width) != 'undefined'){
10334                 td.style += ' width:' +  config.width + 'px;';
10335             }
10336             */
10337             
10338             if(typeof(config.cursor) != 'undefined'){
10339                 td.style += ' cursor:' +  config.cursor + ';';
10340             }
10341             
10342             if(typeof(config.cls) != 'undefined'){
10343                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10344             }
10345             if (this.responsive) {
10346                 ['xs','sm','md','lg'].map(function(size){
10347                     
10348                     if(typeof(config[size]) == 'undefined'){
10349                         return;
10350                     }
10351                     
10352                     
10353                       
10354                     if (!config[size]) { // 0 = hidden
10355                         // BS 4 '0' is treated as hide that column and below.
10356                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10357                         return;
10358                     }
10359                     
10360                     td.cls += ' col-' + size + '-' + config[size] + (
10361                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10362                     );
10363                      
10364     
10365                 });
10366             }
10367             row.cn.push(td);
10368            
10369         }
10370         
10371         row.cellObjects = cellObjects;
10372         
10373         return row;
10374           
10375     },
10376     
10377     
10378     
10379     onBeforeLoad : function()
10380     {
10381         
10382     },
10383      /**
10384      * Remove all rows
10385      */
10386     clear : function()
10387     {
10388         this.el.select('tbody', true).first().dom.innerHTML = '';
10389     },
10390     /**
10391      * Show or hide a row.
10392      * @param {Number} rowIndex to show or hide
10393      * @param {Boolean} state hide
10394      */
10395     setRowVisibility : function(rowIndex, state)
10396     {
10397         var bt = this.bodyEl.dom;
10398         
10399         var rows = this.el.select('tbody > tr', true).elements;
10400         
10401         if(typeof(rows[rowIndex]) == 'undefined'){
10402             return;
10403         }
10404         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10405         
10406     },
10407     
10408     
10409     getSelectionModel : function(){
10410         if(!this.selModel){
10411             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10412         }
10413         return this.selModel;
10414     },
10415     /*
10416      * Render the Roo.bootstrap object from renderder
10417      */
10418     renderCellObject : function(r)
10419     {
10420         var _this = this;
10421         
10422         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10423         
10424         var t = r.cfg.render(r.container);
10425         
10426         if(r.cfg.cn){
10427             Roo.each(r.cfg.cn, function(c){
10428                 var child = {
10429                     container: t.getChildContainer(),
10430                     cfg: c
10431                 };
10432                 _this.renderCellObject(child);
10433             })
10434         }
10435     },
10436     /**
10437      * get the Row Index from a dom element.
10438      * @param {Roo.Element} row The row to look for
10439      * @returns {Number} the row
10440      */
10441     getRowIndex : function(row)
10442     {
10443         var rowIndex = -1;
10444         
10445         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10446             if(el != row){
10447                 return;
10448             }
10449             
10450             rowIndex = index;
10451         });
10452         
10453         return rowIndex;
10454     },
10455     /**
10456      * get the header TH element for columnIndex
10457      * @param {Number} columnIndex
10458      * @returns {Roo.Element}
10459      */
10460     getHeaderIndex: function(colIndex)
10461     {
10462         var cols = this.headEl.select('th', true).elements;
10463         return cols[colIndex]; 
10464     },
10465     /**
10466      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10467      * @param {domElement} cell to look for
10468      * @returns {Number} the column
10469      */
10470     getCellIndex : function(cell)
10471     {
10472         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10473         if(id){
10474             return parseInt(id[1], 10);
10475         }
10476         return 0;
10477     },
10478      /**
10479      * Returns the grid's underlying element = used by panel.Grid
10480      * @return {Element} The element
10481      */
10482     getGridEl : function(){
10483         return this.el;
10484     },
10485      /**
10486      * Forces a resize - used by panel.Grid
10487      * @return {Element} The element
10488      */
10489     autoSize : function()
10490     {
10491         //var ctr = Roo.get(this.container.dom.parentElement);
10492         var ctr = Roo.get(this.el.dom);
10493         
10494         var thd = this.getGridEl().select('thead',true).first();
10495         var tbd = this.getGridEl().select('tbody', true).first();
10496         var tfd = this.getGridEl().select('tfoot', true).first();
10497         
10498         var cw = ctr.getWidth();
10499         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10500         
10501         if (tbd) {
10502             
10503             tbd.setWidth(ctr.getWidth());
10504             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10505             // this needs fixing for various usage - currently only hydra job advers I think..
10506             //tdb.setHeight(
10507             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10508             //); 
10509             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10510             cw -= barsize;
10511         }
10512         cw = Math.max(cw, this.totalWidth);
10513         this.getGridEl().select('tbody tr',true).setWidth(cw);
10514         this.initCSS();
10515         
10516         // resize 'expandable coloumn?
10517         
10518         return; // we doe not have a view in this design..
10519         
10520     },
10521     onBodyScroll: function()
10522     {
10523         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10524         if(this.headEl){
10525             this.headEl.setStyle({
10526                 'position' : 'relative',
10527                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10528             });
10529         }
10530         
10531         if(this.lazyLoad){
10532             
10533             var scrollHeight = this.bodyEl.dom.scrollHeight;
10534             
10535             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10536             
10537             var height = this.bodyEl.getHeight();
10538             
10539             if(scrollHeight - height == scrollTop) {
10540                 
10541                 var total = this.ds.getTotalCount();
10542                 
10543                 if(this.footer.cursor + this.footer.pageSize < total){
10544                     
10545                     this.footer.ds.load({
10546                         params : {
10547                             start : this.footer.cursor + this.footer.pageSize,
10548                             limit : this.footer.pageSize
10549                         },
10550                         add : true
10551                     });
10552                 }
10553             }
10554             
10555         }
10556     },
10557     onColumnSplitterMoved : function(i, diff)
10558     {
10559         this.userResized = true;
10560         
10561         var cm = this.colModel;
10562         
10563         var w = this.getHeaderIndex(i).getWidth() + diff;
10564         
10565         
10566         cm.setColumnWidth(i, w, true);
10567         this.initCSS();
10568         //var cid = cm.getColumnId(i); << not used in this version?
10569        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10570         
10571         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10572         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10573         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10574 */
10575         //this.updateSplitters();
10576         //this.layout(); << ??
10577         this.fireEvent("columnresize", i, w);
10578     },
10579     onHeaderChange : function()
10580     {
10581         var header = this.renderHeader();
10582         var table = this.el.select('table', true).first();
10583         
10584         this.headEl.remove();
10585         this.headEl = table.createChild(header, this.bodyEl, false);
10586         
10587         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10588             e.on('click', this.sort, this);
10589         }, this);
10590         
10591         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10592             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10593         }
10594         
10595     },
10596     
10597     onHiddenChange : function(colModel, colIndex, hidden)
10598     {
10599         /*
10600         this.cm.setHidden()
10601         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10602         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10603         
10604         this.CSS.updateRule(thSelector, "display", "");
10605         this.CSS.updateRule(tdSelector, "display", "");
10606         
10607         if(hidden){
10608             this.CSS.updateRule(thSelector, "display", "none");
10609             this.CSS.updateRule(tdSelector, "display", "none");
10610         }
10611         */
10612         // onload calls initCSS()
10613         this.onHeaderChange();
10614         this.onLoad();
10615     },
10616     
10617     setColumnWidth: function(col_index, width)
10618     {
10619         // width = "md-2 xs-2..."
10620         if(!this.colModel.config[col_index]) {
10621             return;
10622         }
10623         
10624         var w = width.split(" ");
10625         
10626         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10627         
10628         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10629         
10630         
10631         for(var j = 0; j < w.length; j++) {
10632             
10633             if(!w[j]) {
10634                 continue;
10635             }
10636             
10637             var size_cls = w[j].split("-");
10638             
10639             if(!Number.isInteger(size_cls[1] * 1)) {
10640                 continue;
10641             }
10642             
10643             if(!this.colModel.config[col_index][size_cls[0]]) {
10644                 continue;
10645             }
10646             
10647             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10648                 continue;
10649             }
10650             
10651             h_row[0].classList.replace(
10652                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10653                 "col-"+size_cls[0]+"-"+size_cls[1]
10654             );
10655             
10656             for(var i = 0; i < rows.length; i++) {
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(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10669                     continue;
10670                 }
10671                 
10672                 rows[i].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             
10678             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10679         }
10680     }
10681 });
10682
10683 // currently only used to find the split on drag.. 
10684 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10685
10686 /**
10687  * @depricated
10688 */
10689 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10690 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10691 /*
10692  * - LGPL
10693  *
10694  * table cell
10695  * 
10696  */
10697
10698 /**
10699  * @class Roo.bootstrap.TableCell
10700  * @extends Roo.bootstrap.Component
10701  * @children Roo.bootstrap.Component
10702  * @parent Roo.bootstrap.TableRow
10703  * Bootstrap TableCell class
10704  * 
10705  * @cfg {String} html cell contain text
10706  * @cfg {String} cls cell class
10707  * @cfg {String} tag cell tag (td|th) default td
10708  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10709  * @cfg {String} align Aligns the content in a cell
10710  * @cfg {String} axis Categorizes cells
10711  * @cfg {String} bgcolor Specifies the background color of a cell
10712  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10713  * @cfg {Number} colspan Specifies the number of columns a cell should span
10714  * @cfg {String} headers Specifies one or more header cells a cell is related to
10715  * @cfg {Number} height Sets the height of a cell
10716  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10717  * @cfg {Number} rowspan Sets the number of rows a cell should span
10718  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10719  * @cfg {String} valign Vertical aligns the content in a cell
10720  * @cfg {Number} width Specifies the width of a cell
10721  * 
10722  * @constructor
10723  * Create a new TableCell
10724  * @param {Object} config The config object
10725  */
10726
10727 Roo.bootstrap.TableCell = function(config){
10728     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10729 };
10730
10731 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10732     
10733     html: false,
10734     cls: false,
10735     tag: false,
10736     abbr: false,
10737     align: false,
10738     axis: false,
10739     bgcolor: false,
10740     charoff: false,
10741     colspan: false,
10742     headers: false,
10743     height: false,
10744     nowrap: false,
10745     rowspan: false,
10746     scope: false,
10747     valign: false,
10748     width: false,
10749     
10750     
10751     getAutoCreate : function(){
10752         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10753         
10754         cfg = {
10755             tag: 'td'
10756         };
10757         
10758         if(this.tag){
10759             cfg.tag = this.tag;
10760         }
10761         
10762         if (this.html) {
10763             cfg.html=this.html
10764         }
10765         if (this.cls) {
10766             cfg.cls=this.cls
10767         }
10768         if (this.abbr) {
10769             cfg.abbr=this.abbr
10770         }
10771         if (this.align) {
10772             cfg.align=this.align
10773         }
10774         if (this.axis) {
10775             cfg.axis=this.axis
10776         }
10777         if (this.bgcolor) {
10778             cfg.bgcolor=this.bgcolor
10779         }
10780         if (this.charoff) {
10781             cfg.charoff=this.charoff
10782         }
10783         if (this.colspan) {
10784             cfg.colspan=this.colspan
10785         }
10786         if (this.headers) {
10787             cfg.headers=this.headers
10788         }
10789         if (this.height) {
10790             cfg.height=this.height
10791         }
10792         if (this.nowrap) {
10793             cfg.nowrap=this.nowrap
10794         }
10795         if (this.rowspan) {
10796             cfg.rowspan=this.rowspan
10797         }
10798         if (this.scope) {
10799             cfg.scope=this.scope
10800         }
10801         if (this.valign) {
10802             cfg.valign=this.valign
10803         }
10804         if (this.width) {
10805             cfg.width=this.width
10806         }
10807         
10808         
10809         return cfg;
10810     }
10811    
10812 });
10813
10814  
10815
10816  /*
10817  * - LGPL
10818  *
10819  * table row
10820  * 
10821  */
10822
10823 /**
10824  * @class Roo.bootstrap.TableRow
10825  * @extends Roo.bootstrap.Component
10826  * @children Roo.bootstrap.TableCell
10827  * @parent Roo.bootstrap.TableBody
10828  * Bootstrap TableRow class
10829  * @cfg {String} cls row class
10830  * @cfg {String} align Aligns the content in a table row
10831  * @cfg {String} bgcolor Specifies a background color for a table row
10832  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10833  * @cfg {String} valign Vertical aligns the content in a table row
10834  * 
10835  * @constructor
10836  * Create a new TableRow
10837  * @param {Object} config The config object
10838  */
10839
10840 Roo.bootstrap.TableRow = function(config){
10841     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10842 };
10843
10844 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10845     
10846     cls: false,
10847     align: false,
10848     bgcolor: false,
10849     charoff: false,
10850     valign: false,
10851     
10852     getAutoCreate : function(){
10853         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10854         
10855         cfg = {
10856             tag: 'tr'
10857         };
10858             
10859         if(this.cls){
10860             cfg.cls = this.cls;
10861         }
10862         if(this.align){
10863             cfg.align = this.align;
10864         }
10865         if(this.bgcolor){
10866             cfg.bgcolor = this.bgcolor;
10867         }
10868         if(this.charoff){
10869             cfg.charoff = this.charoff;
10870         }
10871         if(this.valign){
10872             cfg.valign = this.valign;
10873         }
10874         
10875         return cfg;
10876     }
10877    
10878 });
10879
10880  
10881
10882  /*
10883  * - LGPL
10884  *
10885  * table body
10886  * 
10887  */
10888
10889 /**
10890  * @class Roo.bootstrap.TableBody
10891  * @extends Roo.bootstrap.Component
10892  * @children Roo.bootstrap.TableRow
10893  * @parent Roo.bootstrap.Table
10894  * Bootstrap TableBody class
10895  * @cfg {String} cls element class
10896  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10897  * @cfg {String} align Aligns the content inside the element
10898  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10899  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10900  * 
10901  * @constructor
10902  * Create a new TableBody
10903  * @param {Object} config The config object
10904  */
10905
10906 Roo.bootstrap.TableBody = function(config){
10907     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10908 };
10909
10910 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10911     
10912     cls: false,
10913     tag: false,
10914     align: false,
10915     charoff: false,
10916     valign: false,
10917     
10918     getAutoCreate : function(){
10919         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10920         
10921         cfg = {
10922             tag: 'tbody'
10923         };
10924             
10925         if (this.cls) {
10926             cfg.cls=this.cls
10927         }
10928         if(this.tag){
10929             cfg.tag = this.tag;
10930         }
10931         
10932         if(this.align){
10933             cfg.align = this.align;
10934         }
10935         if(this.charoff){
10936             cfg.charoff = this.charoff;
10937         }
10938         if(this.valign){
10939             cfg.valign = this.valign;
10940         }
10941         
10942         return cfg;
10943     }
10944     
10945     
10946 //    initEvents : function()
10947 //    {
10948 //        
10949 //        if(!this.store){
10950 //            return;
10951 //        }
10952 //        
10953 //        this.store = Roo.factory(this.store, Roo.data);
10954 //        this.store.on('load', this.onLoad, this);
10955 //        
10956 //        this.store.load();
10957 //        
10958 //    },
10959 //    
10960 //    onLoad: function () 
10961 //    {   
10962 //        this.fireEvent('load', this);
10963 //    }
10964 //    
10965 //   
10966 });
10967
10968  
10969
10970  /*
10971  * Based on:
10972  * Ext JS Library 1.1.1
10973  * Copyright(c) 2006-2007, Ext JS, LLC.
10974  *
10975  * Originally Released Under LGPL - original licence link has changed is not relivant.
10976  *
10977  * Fork - LGPL
10978  * <script type="text/javascript">
10979  */
10980
10981 // as we use this in bootstrap.
10982 Roo.namespace('Roo.form');
10983  /**
10984  * @class Roo.form.Action
10985  * Internal Class used to handle form actions
10986  * @constructor
10987  * @param {Roo.form.BasicForm} el The form element or its id
10988  * @param {Object} config Configuration options
10989  */
10990
10991  
10992  
10993 // define the action interface
10994 Roo.form.Action = function(form, options){
10995     this.form = form;
10996     this.options = options || {};
10997 };
10998 /**
10999  * Client Validation Failed
11000  * @const 
11001  */
11002 Roo.form.Action.CLIENT_INVALID = 'client';
11003 /**
11004  * Server Validation Failed
11005  * @const 
11006  */
11007 Roo.form.Action.SERVER_INVALID = 'server';
11008  /**
11009  * Connect to Server Failed
11010  * @const 
11011  */
11012 Roo.form.Action.CONNECT_FAILURE = 'connect';
11013 /**
11014  * Reading Data from Server Failed
11015  * @const 
11016  */
11017 Roo.form.Action.LOAD_FAILURE = 'load';
11018
11019 Roo.form.Action.prototype = {
11020     type : 'default',
11021     failureType : undefined,
11022     response : undefined,
11023     result : undefined,
11024
11025     // interface method
11026     run : function(options){
11027
11028     },
11029
11030     // interface method
11031     success : function(response){
11032
11033     },
11034
11035     // interface method
11036     handleResponse : function(response){
11037
11038     },
11039
11040     // default connection failure
11041     failure : function(response){
11042         
11043         this.response = response;
11044         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11045         this.form.afterAction(this, false);
11046     },
11047
11048     processResponse : function(response){
11049         this.response = response;
11050         if(!response.responseText){
11051             return true;
11052         }
11053         this.result = this.handleResponse(response);
11054         return this.result;
11055     },
11056
11057     // utility functions used internally
11058     getUrl : function(appendParams){
11059         var url = this.options.url || this.form.url || this.form.el.dom.action;
11060         if(appendParams){
11061             var p = this.getParams();
11062             if(p){
11063                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11064             }
11065         }
11066         return url;
11067     },
11068
11069     getMethod : function(){
11070         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11071     },
11072
11073     getParams : function(){
11074         var bp = this.form.baseParams;
11075         var p = this.options.params;
11076         if(p){
11077             if(typeof p == "object"){
11078                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11079             }else if(typeof p == 'string' && bp){
11080                 p += '&' + Roo.urlEncode(bp);
11081             }
11082         }else if(bp){
11083             p = Roo.urlEncode(bp);
11084         }
11085         return p;
11086     },
11087
11088     createCallback : function(){
11089         return {
11090             success: this.success,
11091             failure: this.failure,
11092             scope: this,
11093             timeout: (this.form.timeout*1000),
11094             upload: this.form.fileUpload ? this.success : undefined
11095         };
11096     }
11097 };
11098
11099 Roo.form.Action.Submit = function(form, options){
11100     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11101 };
11102
11103 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11104     type : 'submit',
11105
11106     haveProgress : false,
11107     uploadComplete : false,
11108     
11109     // uploadProgress indicator.
11110     uploadProgress : function()
11111     {
11112         if (!this.form.progressUrl) {
11113             return;
11114         }
11115         
11116         if (!this.haveProgress) {
11117             Roo.MessageBox.progress("Uploading", "Uploading");
11118         }
11119         if (this.uploadComplete) {
11120            Roo.MessageBox.hide();
11121            return;
11122         }
11123         
11124         this.haveProgress = true;
11125    
11126         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11127         
11128         var c = new Roo.data.Connection();
11129         c.request({
11130             url : this.form.progressUrl,
11131             params: {
11132                 id : uid
11133             },
11134             method: 'GET',
11135             success : function(req){
11136                //console.log(data);
11137                 var rdata = false;
11138                 var edata;
11139                 try  {
11140                    rdata = Roo.decode(req.responseText)
11141                 } catch (e) {
11142                     Roo.log("Invalid data from server..");
11143                     Roo.log(edata);
11144                     return;
11145                 }
11146                 if (!rdata || !rdata.success) {
11147                     Roo.log(rdata);
11148                     Roo.MessageBox.alert(Roo.encode(rdata));
11149                     return;
11150                 }
11151                 var data = rdata.data;
11152                 
11153                 if (this.uploadComplete) {
11154                    Roo.MessageBox.hide();
11155                    return;
11156                 }
11157                    
11158                 if (data){
11159                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11160                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11161                     );
11162                 }
11163                 this.uploadProgress.defer(2000,this);
11164             },
11165        
11166             failure: function(data) {
11167                 Roo.log('progress url failed ');
11168                 Roo.log(data);
11169             },
11170             scope : this
11171         });
11172            
11173     },
11174     
11175     
11176     run : function()
11177     {
11178         // run get Values on the form, so it syncs any secondary forms.
11179         this.form.getValues();
11180         
11181         var o = this.options;
11182         var method = this.getMethod();
11183         var isPost = method == 'POST';
11184         if(o.clientValidation === false || this.form.isValid()){
11185             
11186             if (this.form.progressUrl) {
11187                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11188                     (new Date() * 1) + '' + Math.random());
11189                     
11190             } 
11191             
11192             
11193             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11194                 form:this.form.el.dom,
11195                 url:this.getUrl(!isPost),
11196                 method: method,
11197                 params:isPost ? this.getParams() : null,
11198                 isUpload: this.form.fileUpload,
11199                 formData : this.form.formData
11200             }));
11201             
11202             this.uploadProgress();
11203
11204         }else if (o.clientValidation !== false){ // client validation failed
11205             this.failureType = Roo.form.Action.CLIENT_INVALID;
11206             this.form.afterAction(this, false);
11207         }
11208     },
11209
11210     success : function(response)
11211     {
11212         this.uploadComplete= true;
11213         if (this.haveProgress) {
11214             Roo.MessageBox.hide();
11215         }
11216         
11217         
11218         var result = this.processResponse(response);
11219         if(result === true || result.success){
11220             this.form.afterAction(this, true);
11221             return;
11222         }
11223         if(result.errors){
11224             this.form.markInvalid(result.errors);
11225             this.failureType = Roo.form.Action.SERVER_INVALID;
11226         }
11227         this.form.afterAction(this, false);
11228     },
11229     failure : function(response)
11230     {
11231         this.uploadComplete= true;
11232         if (this.haveProgress) {
11233             Roo.MessageBox.hide();
11234         }
11235         
11236         this.response = response;
11237         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11238         this.form.afterAction(this, false);
11239     },
11240     
11241     handleResponse : function(response){
11242         if(this.form.errorReader){
11243             var rs = this.form.errorReader.read(response);
11244             var errors = [];
11245             if(rs.records){
11246                 for(var i = 0, len = rs.records.length; i < len; i++) {
11247                     var r = rs.records[i];
11248                     errors[i] = r.data;
11249                 }
11250             }
11251             if(errors.length < 1){
11252                 errors = null;
11253             }
11254             return {
11255                 success : rs.success,
11256                 errors : errors
11257             };
11258         }
11259         var ret = false;
11260         try {
11261             ret = Roo.decode(response.responseText);
11262         } catch (e) {
11263             ret = {
11264                 success: false,
11265                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11266                 errors : []
11267             };
11268         }
11269         return ret;
11270         
11271     }
11272 });
11273
11274
11275 Roo.form.Action.Load = function(form, options){
11276     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11277     this.reader = this.form.reader;
11278 };
11279
11280 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11281     type : 'load',
11282
11283     run : function(){
11284         
11285         Roo.Ajax.request(Roo.apply(
11286                 this.createCallback(), {
11287                     method:this.getMethod(),
11288                     url:this.getUrl(false),
11289                     params:this.getParams()
11290         }));
11291     },
11292
11293     success : function(response){
11294         
11295         var result = this.processResponse(response);
11296         if(result === true || !result.success || !result.data){
11297             this.failureType = Roo.form.Action.LOAD_FAILURE;
11298             this.form.afterAction(this, false);
11299             return;
11300         }
11301         this.form.clearInvalid();
11302         this.form.setValues(result.data);
11303         this.form.afterAction(this, true);
11304     },
11305
11306     handleResponse : function(response){
11307         if(this.form.reader){
11308             var rs = this.form.reader.read(response);
11309             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11310             return {
11311                 success : rs.success,
11312                 data : data
11313             };
11314         }
11315         return Roo.decode(response.responseText);
11316     }
11317 });
11318
11319 Roo.form.Action.ACTION_TYPES = {
11320     'load' : Roo.form.Action.Load,
11321     'submit' : Roo.form.Action.Submit
11322 };/*
11323  * - LGPL
11324  *
11325  * form
11326  *
11327  */
11328
11329 /**
11330  * @class Roo.bootstrap.form.Form
11331  * @extends Roo.bootstrap.Component
11332  * @children Roo.bootstrap.Component
11333  * Bootstrap Form class
11334  * @cfg {String} method  GET | POST (default POST)
11335  * @cfg {String} labelAlign top | left (default top)
11336  * @cfg {String} align left  | right - for navbars
11337  * @cfg {Boolean} loadMask load mask when submit (default true)
11338
11339  *
11340  * @constructor
11341  * Create a new Form
11342  * @param {Object} config The config object
11343  */
11344
11345
11346 Roo.bootstrap.form.Form = function(config){
11347     
11348     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11349     
11350     Roo.bootstrap.form.Form.popover.apply();
11351     
11352     this.addEvents({
11353         /**
11354          * @event clientvalidation
11355          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11356          * @param {Form} this
11357          * @param {Boolean} valid true if the form has passed client-side validation
11358          */
11359         clientvalidation: true,
11360         /**
11361          * @event beforeaction
11362          * Fires before any action is performed. Return false to cancel the action.
11363          * @param {Form} this
11364          * @param {Action} action The action to be performed
11365          */
11366         beforeaction: true,
11367         /**
11368          * @event actionfailed
11369          * Fires when an action fails.
11370          * @param {Form} this
11371          * @param {Action} action The action that failed
11372          */
11373         actionfailed : true,
11374         /**
11375          * @event actioncomplete
11376          * Fires when an action is completed.
11377          * @param {Form} this
11378          * @param {Action} action The action that completed
11379          */
11380         actioncomplete : true
11381     });
11382 };
11383
11384 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11385
11386      /**
11387      * @cfg {String} method
11388      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11389      */
11390     method : 'POST',
11391     /**
11392      * @cfg {String} url
11393      * The URL to use for form actions if one isn't supplied in the action options.
11394      */
11395     /**
11396      * @cfg {Boolean} fileUpload
11397      * Set to true if this form is a file upload.
11398      */
11399
11400     /**
11401      * @cfg {Object} baseParams
11402      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11403      */
11404
11405     /**
11406      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11407      */
11408     timeout: 30,
11409     /**
11410      * @cfg {Sting} align (left|right) for navbar forms
11411      */
11412     align : 'left',
11413
11414     // private
11415     activeAction : null,
11416
11417     /**
11418      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11419      * element by passing it or its id or mask the form itself by passing in true.
11420      * @type Mixed
11421      */
11422     waitMsgTarget : false,
11423
11424     loadMask : true,
11425     
11426     /**
11427      * @cfg {Boolean} errorMask (true|false) default false
11428      */
11429     errorMask : false,
11430     
11431     /**
11432      * @cfg {Number} maskOffset Default 100
11433      */
11434     maskOffset : 100,
11435     
11436     /**
11437      * @cfg {Boolean} maskBody
11438      */
11439     maskBody : false,
11440
11441     getAutoCreate : function(){
11442
11443         var cfg = {
11444             tag: 'form',
11445             method : this.method || 'POST',
11446             id : this.id || Roo.id(),
11447             cls : ''
11448         };
11449         if (this.parent().xtype.match(/^Nav/)) {
11450             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11451
11452         }
11453
11454         if (this.labelAlign == 'left' ) {
11455             cfg.cls += ' form-horizontal';
11456         }
11457
11458
11459         return cfg;
11460     },
11461     initEvents : function()
11462     {
11463         this.el.on('submit', this.onSubmit, this);
11464         // this was added as random key presses on the form where triggering form submit.
11465         this.el.on('keypress', function(e) {
11466             if (e.getCharCode() != 13) {
11467                 return true;
11468             }
11469             // we might need to allow it for textareas.. and some other items.
11470             // check e.getTarget().
11471
11472             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11473                 return true;
11474             }
11475
11476             Roo.log("keypress blocked");
11477
11478             e.preventDefault();
11479             return false;
11480         });
11481         
11482     },
11483     // private
11484     onSubmit : function(e){
11485         e.stopEvent();
11486     },
11487
11488      /**
11489      * Returns true if client-side validation on the form is successful.
11490      * @return Boolean
11491      */
11492     isValid : function(){
11493         var items = this.getItems();
11494         var valid = true;
11495         var target = false;
11496         
11497         items.each(function(f){
11498             
11499             if(f.validate()){
11500                 return;
11501             }
11502             
11503             Roo.log('invalid field: ' + f.name);
11504             
11505             valid = false;
11506
11507             if(!target && f.el.isVisible(true)){
11508                 target = f;
11509             }
11510            
11511         });
11512         
11513         if(this.errorMask && !valid){
11514             Roo.bootstrap.form.Form.popover.mask(this, target);
11515         }
11516         
11517         return valid;
11518     },
11519     
11520     /**
11521      * Returns true if any fields in this form have changed since their original load.
11522      * @return Boolean
11523      */
11524     isDirty : function(){
11525         var dirty = false;
11526         var items = this.getItems();
11527         items.each(function(f){
11528            if(f.isDirty()){
11529                dirty = true;
11530                return false;
11531            }
11532            return true;
11533         });
11534         return dirty;
11535     },
11536      /**
11537      * Performs a predefined action (submit or load) or custom actions you define on this form.
11538      * @param {String} actionName The name of the action type
11539      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11540      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11541      * accept other config options):
11542      * <pre>
11543 Property          Type             Description
11544 ----------------  ---------------  ----------------------------------------------------------------------------------
11545 url               String           The url for the action (defaults to the form's url)
11546 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11547 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11548 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11549                                    validate the form on the client (defaults to false)
11550      * </pre>
11551      * @return {BasicForm} this
11552      */
11553     doAction : function(action, options){
11554         if(typeof action == 'string'){
11555             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11556         }
11557         if(this.fireEvent('beforeaction', this, action) !== false){
11558             this.beforeAction(action);
11559             action.run.defer(100, action);
11560         }
11561         return this;
11562     },
11563
11564     // private
11565     beforeAction : function(action){
11566         var o = action.options;
11567         
11568         if(this.loadMask){
11569             
11570             if(this.maskBody){
11571                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11572             } else {
11573                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11574             }
11575         }
11576         // not really supported yet.. ??
11577
11578         //if(this.waitMsgTarget === true){
11579         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11580         //}else if(this.waitMsgTarget){
11581         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11582         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11583         //}else {
11584         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11585        // }
11586
11587     },
11588
11589     // private
11590     afterAction : function(action, success){
11591         this.activeAction = null;
11592         var o = action.options;
11593
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).unmask();
11598             } else {
11599                 this.el.unmask();
11600             }
11601         }
11602         
11603         //if(this.waitMsgTarget === true){
11604 //            this.el.unmask();
11605         //}else if(this.waitMsgTarget){
11606         //    this.waitMsgTarget.unmask();
11607         //}else{
11608         //    Roo.MessageBox.updateProgress(1);
11609         //    Roo.MessageBox.hide();
11610        // }
11611         //
11612         if(success){
11613             if(o.reset){
11614                 this.reset();
11615             }
11616             Roo.callback(o.success, o.scope, [this, action]);
11617             this.fireEvent('actioncomplete', this, action);
11618
11619         }else{
11620
11621             // failure condition..
11622             // we have a scenario where updates need confirming.
11623             // eg. if a locking scenario exists..
11624             // we look for { errors : { needs_confirm : true }} in the response.
11625             if (
11626                 (typeof(action.result) != 'undefined')  &&
11627                 (typeof(action.result.errors) != 'undefined')  &&
11628                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11629            ){
11630                 var _t = this;
11631                 Roo.log("not supported yet");
11632                  /*
11633
11634                 Roo.MessageBox.confirm(
11635                     "Change requires confirmation",
11636                     action.result.errorMsg,
11637                     function(r) {
11638                         if (r != 'yes') {
11639                             return;
11640                         }
11641                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11642                     }
11643
11644                 );
11645                 */
11646
11647
11648                 return;
11649             }
11650
11651             Roo.callback(o.failure, o.scope, [this, action]);
11652             // show an error message if no failed handler is set..
11653             if (!this.hasListener('actionfailed')) {
11654                 Roo.log("need to add dialog support");
11655                 /*
11656                 Roo.MessageBox.alert("Error",
11657                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11658                         action.result.errorMsg :
11659                         "Saving Failed, please check your entries or try again"
11660                 );
11661                 */
11662             }
11663
11664             this.fireEvent('actionfailed', this, action);
11665         }
11666
11667     },
11668     /**
11669      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11670      * @param {String} id The value to search for
11671      * @return Field
11672      */
11673     findField : function(id){
11674         var items = this.getItems();
11675         var field = items.get(id);
11676         if(!field){
11677              items.each(function(f){
11678                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11679                     field = f;
11680                     return false;
11681                 }
11682                 return true;
11683             });
11684         }
11685         return field || null;
11686     },
11687      /**
11688      * Mark fields in this form invalid in bulk.
11689      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11690      * @return {BasicForm} this
11691      */
11692     markInvalid : function(errors){
11693         if(errors instanceof Array){
11694             for(var i = 0, len = errors.length; i < len; i++){
11695                 var fieldError = errors[i];
11696                 var f = this.findField(fieldError.id);
11697                 if(f){
11698                     f.markInvalid(fieldError.msg);
11699                 }
11700             }
11701         }else{
11702             var field, id;
11703             for(id in errors){
11704                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11705                     field.markInvalid(errors[id]);
11706                 }
11707             }
11708         }
11709         //Roo.each(this.childForms || [], function (f) {
11710         //    f.markInvalid(errors);
11711         //});
11712
11713         return this;
11714     },
11715
11716     /**
11717      * Set values for fields in this form in bulk.
11718      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11719      * @return {BasicForm} this
11720      */
11721     setValues : function(values){
11722         if(values instanceof Array){ // array of objects
11723             for(var i = 0, len = values.length; i < len; i++){
11724                 var v = values[i];
11725                 var f = this.findField(v.id);
11726                 if(f){
11727                     f.setValue(v.value);
11728                     if(this.trackResetOnLoad){
11729                         f.originalValue = f.getValue();
11730                     }
11731                 }
11732             }
11733         }else{ // object hash
11734             var field, id;
11735             for(id in values){
11736                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11737
11738                     if (field.setFromData &&
11739                         field.valueField &&
11740                         field.displayField &&
11741                         // combos' with local stores can
11742                         // be queried via setValue()
11743                         // to set their value..
11744                         (field.store && !field.store.isLocal)
11745                         ) {
11746                         // it's a combo
11747                         var sd = { };
11748                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11749                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11750                         field.setFromData(sd);
11751
11752                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11753                         
11754                         field.setFromData(values);
11755                         
11756                     } else {
11757                         field.setValue(values[id]);
11758                     }
11759
11760
11761                     if(this.trackResetOnLoad){
11762                         field.originalValue = field.getValue();
11763                     }
11764                 }
11765             }
11766         }
11767
11768         //Roo.each(this.childForms || [], function (f) {
11769         //    f.setValues(values);
11770         //});
11771
11772         return this;
11773     },
11774
11775     /**
11776      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11777      * they are returned as an array.
11778      * @param {Boolean} asString
11779      * @return {Object}
11780      */
11781     getValues : function(asString){
11782         //if (this.childForms) {
11783             // copy values from the child forms
11784         //    Roo.each(this.childForms, function (f) {
11785         //        this.setValues(f.getValues());
11786         //    }, this);
11787         //}
11788
11789
11790
11791         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11792         if(asString === true){
11793             return fs;
11794         }
11795         return Roo.urlDecode(fs);
11796     },
11797
11798     /**
11799      * Returns the fields in this form as an object with key/value pairs.
11800      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11801      * @return {Object}
11802      */
11803     getFieldValues : function(with_hidden)
11804     {
11805         var items = this.getItems();
11806         var ret = {};
11807         items.each(function(f){
11808             
11809             if (!f.getName()) {
11810                 return;
11811             }
11812             
11813             var v = f.getValue();
11814             
11815             if (f.inputType =='radio') {
11816                 if (typeof(ret[f.getName()]) == 'undefined') {
11817                     ret[f.getName()] = ''; // empty..
11818                 }
11819
11820                 if (!f.el.dom.checked) {
11821                     return;
11822
11823                 }
11824                 v = f.el.dom.value;
11825
11826             }
11827             
11828             if(f.xtype == 'MoneyField'){
11829                 ret[f.currencyName] = f.getCurrency();
11830             }
11831
11832             // not sure if this supported any more..
11833             if ((typeof(v) == 'object') && f.getRawValue) {
11834                 v = f.getRawValue() ; // dates..
11835             }
11836             // combo boxes where name != hiddenName...
11837             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11838                 ret[f.name] = f.getRawValue();
11839             }
11840             ret[f.getName()] = v;
11841         });
11842
11843         return ret;
11844     },
11845
11846     /**
11847      * Clears all invalid messages in this form.
11848      * @return {BasicForm} this
11849      */
11850     clearInvalid : function(){
11851         var items = this.getItems();
11852
11853         items.each(function(f){
11854            f.clearInvalid();
11855         });
11856
11857         return this;
11858     },
11859
11860     /**
11861      * Resets this form.
11862      * @return {BasicForm} this
11863      */
11864     reset : function(){
11865         var items = this.getItems();
11866         items.each(function(f){
11867             f.reset();
11868         });
11869
11870         Roo.each(this.childForms || [], function (f) {
11871             f.reset();
11872         });
11873
11874
11875         return this;
11876     },
11877     
11878     getItems : function()
11879     {
11880         var r=new Roo.util.MixedCollection(false, function(o){
11881             return o.id || (o.id = Roo.id());
11882         });
11883         var iter = function(el) {
11884             if (el.inputEl) {
11885                 r.add(el);
11886             }
11887             if (!el.items) {
11888                 return;
11889             }
11890             Roo.each(el.items,function(e) {
11891                 iter(e);
11892             });
11893         };
11894
11895         iter(this);
11896         return r;
11897     },
11898     
11899     hideFields : function(items)
11900     {
11901         Roo.each(items, function(i){
11902             
11903             var f = this.findField(i);
11904             
11905             if(!f){
11906                 return;
11907             }
11908             
11909             f.hide();
11910             
11911         }, this);
11912     },
11913     
11914     showFields : function(items)
11915     {
11916         Roo.each(items, function(i){
11917             
11918             var f = this.findField(i);
11919             
11920             if(!f){
11921                 return;
11922             }
11923             
11924             f.show();
11925             
11926         }, this);
11927     }
11928
11929 });
11930
11931 Roo.apply(Roo.bootstrap.form.Form, {
11932     
11933     popover : {
11934         
11935         padding : 5,
11936         
11937         isApplied : false,
11938         
11939         isMasked : false,
11940         
11941         form : false,
11942         
11943         target : false,
11944         
11945         toolTip : false,
11946         
11947         intervalID : false,
11948         
11949         maskEl : false,
11950         
11951         apply : function()
11952         {
11953             if(this.isApplied){
11954                 return;
11955             }
11956             
11957             this.maskEl = {
11958                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11959                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11960                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11961                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11962             };
11963             
11964             this.maskEl.top.enableDisplayMode("block");
11965             this.maskEl.left.enableDisplayMode("block");
11966             this.maskEl.bottom.enableDisplayMode("block");
11967             this.maskEl.right.enableDisplayMode("block");
11968             
11969             this.toolTip = new Roo.bootstrap.Tooltip({
11970                 cls : 'roo-form-error-popover',
11971                 alignment : {
11972                     'left' : ['r-l', [-2,0], 'right'],
11973                     'right' : ['l-r', [2,0], 'left'],
11974                     'bottom' : ['tl-bl', [0,2], 'top'],
11975                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11976                 }
11977             });
11978             
11979             this.toolTip.render(Roo.get(document.body));
11980
11981             this.toolTip.el.enableDisplayMode("block");
11982             
11983             Roo.get(document.body).on('click', function(){
11984                 this.unmask();
11985             }, this);
11986             
11987             Roo.get(document.body).on('touchstart', function(){
11988                 this.unmask();
11989             }, this);
11990             
11991             this.isApplied = true
11992         },
11993         
11994         mask : function(form, target)
11995         {
11996             this.form = form;
11997             
11998             this.target = target;
11999             
12000             if(!this.form.errorMask || !target.el){
12001                 return;
12002             }
12003             
12004             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12005             
12006             Roo.log(scrollable);
12007             
12008             var ot = this.target.el.calcOffsetsTo(scrollable);
12009             
12010             var scrollTo = ot[1] - this.form.maskOffset;
12011             
12012             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12013             
12014             scrollable.scrollTo('top', scrollTo);
12015             
12016             var box = this.target.el.getBox();
12017             Roo.log(box);
12018             var zIndex = Roo.bootstrap.Modal.zIndex++;
12019
12020             
12021             this.maskEl.top.setStyle('position', 'absolute');
12022             this.maskEl.top.setStyle('z-index', zIndex);
12023             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12024             this.maskEl.top.setLeft(0);
12025             this.maskEl.top.setTop(0);
12026             this.maskEl.top.show();
12027             
12028             this.maskEl.left.setStyle('position', 'absolute');
12029             this.maskEl.left.setStyle('z-index', zIndex);
12030             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12031             this.maskEl.left.setLeft(0);
12032             this.maskEl.left.setTop(box.y - this.padding);
12033             this.maskEl.left.show();
12034
12035             this.maskEl.bottom.setStyle('position', 'absolute');
12036             this.maskEl.bottom.setStyle('z-index', zIndex);
12037             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12038             this.maskEl.bottom.setLeft(0);
12039             this.maskEl.bottom.setTop(box.bottom + this.padding);
12040             this.maskEl.bottom.show();
12041
12042             this.maskEl.right.setStyle('position', 'absolute');
12043             this.maskEl.right.setStyle('z-index', zIndex);
12044             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12045             this.maskEl.right.setLeft(box.right + this.padding);
12046             this.maskEl.right.setTop(box.y - this.padding);
12047             this.maskEl.right.show();
12048
12049             this.toolTip.bindEl = this.target.el;
12050
12051             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12052
12053             var tip = this.target.blankText;
12054
12055             if(this.target.getValue() !== '' ) {
12056                 
12057                 if (this.target.invalidText.length) {
12058                     tip = this.target.invalidText;
12059                 } else if (this.target.regexText.length){
12060                     tip = this.target.regexText;
12061                 }
12062             }
12063
12064             this.toolTip.show(tip);
12065
12066             this.intervalID = window.setInterval(function() {
12067                 Roo.bootstrap.form.Form.popover.unmask();
12068             }, 10000);
12069
12070             window.onwheel = function(){ return false;};
12071             
12072             (function(){ this.isMasked = true; }).defer(500, this);
12073             
12074         },
12075         
12076         unmask : function()
12077         {
12078             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12079                 return;
12080             }
12081             
12082             this.maskEl.top.setStyle('position', 'absolute');
12083             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12084             this.maskEl.top.hide();
12085
12086             this.maskEl.left.setStyle('position', 'absolute');
12087             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12088             this.maskEl.left.hide();
12089
12090             this.maskEl.bottom.setStyle('position', 'absolute');
12091             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12092             this.maskEl.bottom.hide();
12093
12094             this.maskEl.right.setStyle('position', 'absolute');
12095             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12096             this.maskEl.right.hide();
12097             
12098             this.toolTip.hide();
12099             
12100             this.toolTip.el.hide();
12101             
12102             window.onwheel = function(){ return true;};
12103             
12104             if(this.intervalID){
12105                 window.clearInterval(this.intervalID);
12106                 this.intervalID = false;
12107             }
12108             
12109             this.isMasked = false;
12110             
12111         }
12112         
12113     }
12114     
12115 });
12116
12117 /*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127 /**
12128  * @class Roo.form.VTypes
12129  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12130  * @static
12131  */
12132 Roo.form.VTypes = function(){
12133     // closure these in so they are only created once.
12134     var alpha = /^[a-zA-Z_]+$/;
12135     var alphanum = /^[a-zA-Z0-9_]+$/;
12136     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12137     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12138
12139     // All these messages and functions are configurable
12140     return {
12141         /**
12142          * The function used to validate email addresses
12143          * @param {String} value The email address
12144          */
12145         'email' : function(v){
12146             return email.test(v);
12147         },
12148         /**
12149          * The error text to display when the email validation function returns false
12150          * @type String
12151          */
12152         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12153         /**
12154          * The keystroke filter mask to be applied on email input
12155          * @type RegExp
12156          */
12157         'emailMask' : /[a-z0-9_\.\-@]/i,
12158
12159         /**
12160          * The function used to validate URLs
12161          * @param {String} value The URL
12162          */
12163         'url' : function(v){
12164             return url.test(v);
12165         },
12166         /**
12167          * The error text to display when the url validation function returns false
12168          * @type String
12169          */
12170         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12171         
12172         /**
12173          * The function used to validate alpha values
12174          * @param {String} value The value
12175          */
12176         'alpha' : function(v){
12177             return alpha.test(v);
12178         },
12179         /**
12180          * The error text to display when the alpha validation function returns false
12181          * @type String
12182          */
12183         'alphaText' : 'This field should only contain letters and _',
12184         /**
12185          * The keystroke filter mask to be applied on alpha input
12186          * @type RegExp
12187          */
12188         'alphaMask' : /[a-z_]/i,
12189
12190         /**
12191          * The function used to validate alphanumeric values
12192          * @param {String} value The value
12193          */
12194         'alphanum' : function(v){
12195             return alphanum.test(v);
12196         },
12197         /**
12198          * The error text to display when the alphanumeric validation function returns false
12199          * @type String
12200          */
12201         'alphanumText' : 'This field should only contain letters, numbers and _',
12202         /**
12203          * The keystroke filter mask to be applied on alphanumeric input
12204          * @type RegExp
12205          */
12206         'alphanumMask' : /[a-z0-9_]/i
12207     };
12208 }();/*
12209  * - LGPL
12210  *
12211  * Input
12212  * 
12213  */
12214
12215 /**
12216  * @class Roo.bootstrap.form.Input
12217  * @extends Roo.bootstrap.Component
12218  * Bootstrap Input class
12219  * @cfg {Boolean} disabled is it disabled
12220  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12221  * @cfg {String} name name of the input
12222  * @cfg {string} fieldLabel - the label associated
12223  * @cfg {string} placeholder - placeholder to put in text.
12224  * @cfg {string} before - input group add on before
12225  * @cfg {string} after - input group add on after
12226  * @cfg {string} size - (lg|sm) or leave empty..
12227  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12228  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12229  * @cfg {Number} md colspan out of 12 for computer-sized screens
12230  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12231  * @cfg {string} value default value of the input
12232  * @cfg {Number} labelWidth set the width of label 
12233  * @cfg {Number} labellg set the width of label (1-12)
12234  * @cfg {Number} labelmd set the width of label (1-12)
12235  * @cfg {Number} labelsm set the width of label (1-12)
12236  * @cfg {Number} labelxs set the width of label (1-12)
12237  * @cfg {String} labelAlign (top|left)
12238  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12239  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12240  * @cfg {String} indicatorpos (left|right) default left
12241  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12242  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12243  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12244  * @cfg {Roo.bootstrap.Button} before Button to show before
12245  * @cfg {Roo.bootstrap.Button} afterButton to show before
12246  * @cfg {String} align (left|center|right) Default left
12247  * @cfg {Boolean} forceFeedback (true|false) Default false
12248  * 
12249  * @constructor
12250  * Create a new Input
12251  * @param {Object} config The config object
12252  */
12253
12254 Roo.bootstrap.form.Input = function(config){
12255     
12256     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12257     
12258     this.addEvents({
12259         /**
12260          * @event focus
12261          * Fires when this field receives input focus.
12262          * @param {Roo.form.Field} this
12263          */
12264         focus : true,
12265         /**
12266          * @event blur
12267          * Fires when this field loses input focus.
12268          * @param {Roo.form.Field} this
12269          */
12270         blur : true,
12271         /**
12272          * @event specialkey
12273          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12274          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12275          * @param {Roo.form.Field} this
12276          * @param {Roo.EventObject} e The event object
12277          */
12278         specialkey : true,
12279         /**
12280          * @event change
12281          * Fires just before the field blurs if the field value has changed.
12282          * @param {Roo.form.Field} this
12283          * @param {Mixed} newValue The new value
12284          * @param {Mixed} oldValue The original value
12285          */
12286         change : true,
12287         /**
12288          * @event invalid
12289          * Fires after the field has been marked as invalid.
12290          * @param {Roo.form.Field} this
12291          * @param {String} msg The validation message
12292          */
12293         invalid : true,
12294         /**
12295          * @event valid
12296          * Fires after the field has been validated with no errors.
12297          * @param {Roo.form.Field} this
12298          */
12299         valid : true,
12300          /**
12301          * @event keyup
12302          * Fires after the key up
12303          * @param {Roo.form.Field} this
12304          * @param {Roo.EventObject}  e The event Object
12305          */
12306         keyup : true,
12307         /**
12308          * @event paste
12309          * Fires after the user pastes into input
12310          * @param {Roo.form.Field} this
12311          * @param {Roo.EventObject}  e The event Object
12312          */
12313         paste : true
12314     });
12315 };
12316
12317 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12318      /**
12319      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12320       automatic validation (defaults to "keyup").
12321      */
12322     validationEvent : "keyup",
12323      /**
12324      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12325      */
12326     validateOnBlur : true,
12327     /**
12328      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12329      */
12330     validationDelay : 250,
12331      /**
12332      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12333      */
12334     focusClass : "x-form-focus",  // not needed???
12335     
12336        
12337     /**
12338      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12339      */
12340     invalidClass : "has-warning",
12341     
12342     /**
12343      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12344      */
12345     validClass : "has-success",
12346     
12347     /**
12348      * @cfg {Boolean} hasFeedback (true|false) default true
12349      */
12350     hasFeedback : true,
12351     
12352     /**
12353      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12354      */
12355     invalidFeedbackClass : "glyphicon-warning-sign",
12356     
12357     /**
12358      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12359      */
12360     validFeedbackClass : "glyphicon-ok",
12361     
12362     /**
12363      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12364      */
12365     selectOnFocus : false,
12366     
12367      /**
12368      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12369      */
12370     maskRe : null,
12371        /**
12372      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12373      */
12374     vtype : null,
12375     
12376       /**
12377      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12378      */
12379     disableKeyFilter : false,
12380     
12381        /**
12382      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12383      */
12384     disabled : false,
12385      /**
12386      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12387      */
12388     allowBlank : true,
12389     /**
12390      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12391      */
12392     blankText : "Please complete this mandatory field",
12393     
12394      /**
12395      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12396      */
12397     minLength : 0,
12398     /**
12399      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12400      */
12401     maxLength : Number.MAX_VALUE,
12402     /**
12403      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12404      */
12405     minLengthText : "The minimum length for this field is {0}",
12406     /**
12407      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12408      */
12409     maxLengthText : "The maximum length for this field is {0}",
12410   
12411     
12412     /**
12413      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12414      * If available, this function will be called only after the basic validators all return true, and will be passed the
12415      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12416      */
12417     validator : null,
12418     /**
12419      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12420      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12421      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12422      */
12423     regex : null,
12424     /**
12425      * @cfg {String} regexText -- Depricated - use Invalid Text
12426      */
12427     regexText : "",
12428     
12429     /**
12430      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12431      */
12432     invalidText : "",
12433     
12434     
12435     
12436     autocomplete: false,
12437     
12438     
12439     fieldLabel : '',
12440     inputType : 'text',
12441     
12442     name : false,
12443     placeholder: false,
12444     before : false,
12445     after : false,
12446     size : false,
12447     hasFocus : false,
12448     preventMark: false,
12449     isFormField : true,
12450     value : '',
12451     labelWidth : 2,
12452     labelAlign : false,
12453     readOnly : false,
12454     align : false,
12455     formatedValue : false,
12456     forceFeedback : false,
12457     
12458     indicatorpos : 'left',
12459     
12460     labellg : 0,
12461     labelmd : 0,
12462     labelsm : 0,
12463     labelxs : 0,
12464     
12465     capture : '',
12466     accept : '',
12467     
12468     parentLabelAlign : function()
12469     {
12470         var parent = this;
12471         while (parent.parent()) {
12472             parent = parent.parent();
12473             if (typeof(parent.labelAlign) !='undefined') {
12474                 return parent.labelAlign;
12475             }
12476         }
12477         return 'left';
12478         
12479     },
12480     
12481     getAutoCreate : function()
12482     {
12483         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12484         
12485         var id = Roo.id();
12486         
12487         var cfg = {};
12488         
12489         if(this.inputType != 'hidden'){
12490             cfg.cls = 'form-group' //input-group
12491         }
12492         
12493         var input =  {
12494             tag: 'input',
12495             id : id,
12496             type : this.inputType,
12497             value : this.value,
12498             cls : 'form-control',
12499             placeholder : this.placeholder || '',
12500             autocomplete : this.autocomplete || 'new-password'
12501         };
12502         if (this.inputType == 'file') {
12503             input.style = 'overflow:hidden'; // why not in CSS?
12504         }
12505         
12506         if(this.capture.length){
12507             input.capture = this.capture;
12508         }
12509         
12510         if(this.accept.length){
12511             input.accept = this.accept + "/*";
12512         }
12513         
12514         if(this.align){
12515             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12516         }
12517         
12518         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12519             input.maxLength = this.maxLength;
12520         }
12521         
12522         if (this.disabled) {
12523             input.disabled=true;
12524         }
12525         
12526         if (this.readOnly) {
12527             input.readonly=true;
12528         }
12529         
12530         if (this.name) {
12531             input.name = this.name;
12532         }
12533         
12534         if (this.size) {
12535             input.cls += ' input-' + this.size;
12536         }
12537         
12538         var settings=this;
12539         ['xs','sm','md','lg'].map(function(size){
12540             if (settings[size]) {
12541                 cfg.cls += ' col-' + size + '-' + settings[size];
12542             }
12543         });
12544         
12545         var inputblock = input;
12546         
12547         var feedback = {
12548             tag: 'span',
12549             cls: 'glyphicon form-control-feedback'
12550         };
12551             
12552         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12553             
12554             inputblock = {
12555                 cls : 'has-feedback',
12556                 cn :  [
12557                     input,
12558                     feedback
12559                 ] 
12560             };  
12561         }
12562         
12563         if (this.before || this.after) {
12564             
12565             inputblock = {
12566                 cls : 'input-group',
12567                 cn :  [] 
12568             };
12569             
12570             if (this.before && typeof(this.before) == 'string') {
12571                 
12572                 inputblock.cn.push({
12573                     tag :'span',
12574                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12575                     html : this.before
12576                 });
12577             }
12578             if (this.before && typeof(this.before) == 'object') {
12579                 this.before = Roo.factory(this.before);
12580                 
12581                 inputblock.cn.push({
12582                     tag :'span',
12583                     cls : 'roo-input-before input-group-prepend   input-group-' +
12584                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12585                 });
12586             }
12587             
12588             inputblock.cn.push(input);
12589             
12590             if (this.after && typeof(this.after) == 'string') {
12591                 inputblock.cn.push({
12592                     tag :'span',
12593                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12594                     html : this.after
12595                 });
12596             }
12597             if (this.after && typeof(this.after) == 'object') {
12598                 this.after = Roo.factory(this.after);
12599                 
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-after input-group-append  input-group-' +
12603                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12604                 });
12605             }
12606             
12607             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12608                 inputblock.cls += ' has-feedback';
12609                 inputblock.cn.push(feedback);
12610             }
12611         };
12612         var indicator = {
12613             tag : 'i',
12614             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12615             tooltip : 'This field is required'
12616         };
12617         if (this.allowBlank ) {
12618             indicator.style = this.allowBlank ? ' display:none' : '';
12619         }
12620         if (align ==='left' && this.fieldLabel.length) {
12621             
12622             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12623             
12624             cfg.cn = [
12625                 indicator,
12626                 {
12627                     tag: 'label',
12628                     'for' :  id,
12629                     cls : 'control-label col-form-label',
12630                     html : this.fieldLabel
12631
12632                 },
12633                 {
12634                     cls : "", 
12635                     cn: [
12636                         inputblock
12637                     ]
12638                 }
12639             ];
12640             
12641             var labelCfg = cfg.cn[1];
12642             var contentCfg = cfg.cn[2];
12643             
12644             if(this.indicatorpos == 'right'){
12645                 cfg.cn = [
12646                     {
12647                         tag: 'label',
12648                         'for' :  id,
12649                         cls : 'control-label col-form-label',
12650                         cn : [
12651                             {
12652                                 tag : 'span',
12653                                 html : this.fieldLabel
12654                             },
12655                             indicator
12656                         ]
12657                     },
12658                     {
12659                         cls : "",
12660                         cn: [
12661                             inputblock
12662                         ]
12663                     }
12664
12665                 ];
12666                 
12667                 labelCfg = cfg.cn[0];
12668                 contentCfg = cfg.cn[1];
12669             
12670             }
12671             
12672             if(this.labelWidth > 12){
12673                 labelCfg.style = "width: " + this.labelWidth + 'px';
12674             }
12675             
12676             if(this.labelWidth < 13 && this.labelmd == 0){
12677                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12678             }
12679             
12680             if(this.labellg > 0){
12681                 labelCfg.cls += ' col-lg-' + this.labellg;
12682                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12683             }
12684             
12685             if(this.labelmd > 0){
12686                 labelCfg.cls += ' col-md-' + this.labelmd;
12687                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12688             }
12689             
12690             if(this.labelsm > 0){
12691                 labelCfg.cls += ' col-sm-' + this.labelsm;
12692                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12693             }
12694             
12695             if(this.labelxs > 0){
12696                 labelCfg.cls += ' col-xs-' + this.labelxs;
12697                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12698             }
12699             
12700             
12701         } else if ( this.fieldLabel.length) {
12702                 
12703             
12704             
12705             cfg.cn = [
12706                 {
12707                     tag : 'i',
12708                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12709                     tooltip : 'This field is required',
12710                     style : this.allowBlank ? ' display:none' : '' 
12711                 },
12712                 {
12713                     tag: 'label',
12714                    //cls : 'input-group-addon',
12715                     html : this.fieldLabel
12716
12717                 },
12718
12719                inputblock
12720
12721            ];
12722            
12723            if(this.indicatorpos == 'right'){
12724        
12725                 cfg.cn = [
12726                     {
12727                         tag: 'label',
12728                        //cls : 'input-group-addon',
12729                         html : this.fieldLabel
12730
12731                     },
12732                     {
12733                         tag : 'i',
12734                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12735                         tooltip : 'This field is required',
12736                         style : this.allowBlank ? ' display:none' : '' 
12737                     },
12738
12739                    inputblock
12740
12741                ];
12742
12743             }
12744
12745         } else {
12746             
12747             cfg.cn = [
12748
12749                     inputblock
12750
12751             ];
12752                 
12753                 
12754         };
12755         
12756         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12757            cfg.cls += ' navbar-form';
12758         }
12759         
12760         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12761             // on BS4 we do this only if not form 
12762             cfg.cls += ' navbar-form';
12763             cfg.tag = 'li';
12764         }
12765         
12766         return cfg;
12767         
12768     },
12769     /**
12770      * return the real input element.
12771      */
12772     inputEl: function ()
12773     {
12774         return this.el.select('input.form-control',true).first();
12775     },
12776     
12777     tooltipEl : function()
12778     {
12779         return this.inputEl();
12780     },
12781     
12782     indicatorEl : function()
12783     {
12784         if (Roo.bootstrap.version == 4) {
12785             return false; // not enabled in v4 yet.
12786         }
12787         
12788         var indicator = this.el.select('i.roo-required-indicator',true).first();
12789         
12790         if(!indicator){
12791             return false;
12792         }
12793         
12794         return indicator;
12795         
12796     },
12797     
12798     setDisabled : function(v)
12799     {
12800         var i  = this.inputEl().dom;
12801         if (!v) {
12802             i.removeAttribute('disabled');
12803             return;
12804             
12805         }
12806         i.setAttribute('disabled','true');
12807     },
12808     initEvents : function()
12809     {
12810           
12811         this.inputEl().on("keydown" , this.fireKey,  this);
12812         this.inputEl().on("focus", this.onFocus,  this);
12813         this.inputEl().on("blur", this.onBlur,  this);
12814         
12815         this.inputEl().relayEvent('keyup', this);
12816         this.inputEl().relayEvent('paste', this);
12817         
12818         this.indicator = this.indicatorEl();
12819         
12820         if(this.indicator){
12821             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12822         }
12823  
12824         // reference to original value for reset
12825         this.originalValue = this.getValue();
12826         //Roo.form.TextField.superclass.initEvents.call(this);
12827         if(this.validationEvent == 'keyup'){
12828             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12829             this.inputEl().on('keyup', this.filterValidation, this);
12830         }
12831         else if(this.validationEvent !== false){
12832             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12833         }
12834         
12835         if(this.selectOnFocus){
12836             this.on("focus", this.preFocus, this);
12837             
12838         }
12839         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12840             this.inputEl().on("keypress", this.filterKeys, this);
12841         } else {
12842             this.inputEl().relayEvent('keypress', this);
12843         }
12844        /* if(this.grow){
12845             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12846             this.el.on("click", this.autoSize,  this);
12847         }
12848         */
12849         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12850             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12851         }
12852         
12853         if (typeof(this.before) == 'object') {
12854             this.before.render(this.el.select('.roo-input-before',true).first());
12855         }
12856         if (typeof(this.after) == 'object') {
12857             this.after.render(this.el.select('.roo-input-after',true).first());
12858         }
12859         
12860         this.inputEl().on('change', this.onChange, this);
12861         
12862     },
12863     filterValidation : function(e){
12864         if(!e.isNavKeyPress()){
12865             this.validationTask.delay(this.validationDelay);
12866         }
12867     },
12868      /**
12869      * Validates the field value
12870      * @return {Boolean} True if the value is valid, else false
12871      */
12872     validate : function(){
12873         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12874         if(this.disabled || this.validateValue(this.getRawValue())){
12875             this.markValid();
12876             return true;
12877         }
12878         
12879         this.markInvalid();
12880         return false;
12881     },
12882     
12883     
12884     /**
12885      * Validates a value according to the field's validation rules and marks the field as invalid
12886      * if the validation fails
12887      * @param {Mixed} value The value to validate
12888      * @return {Boolean} True if the value is valid, else false
12889      */
12890     validateValue : function(value)
12891     {
12892         if(this.getVisibilityEl().hasClass('hidden')){
12893             return true;
12894         }
12895         
12896         if(value.length < 1)  { // if it's blank
12897             if(this.allowBlank){
12898                 return true;
12899             }
12900             return false;
12901         }
12902         
12903         if(value.length < this.minLength){
12904             return false;
12905         }
12906         if(value.length > this.maxLength){
12907             return false;
12908         }
12909         if(this.vtype){
12910             var vt = Roo.form.VTypes;
12911             if(!vt[this.vtype](value, this)){
12912                 return false;
12913             }
12914         }
12915         if(typeof this.validator == "function"){
12916             var msg = this.validator(value);
12917             if(msg !== true){
12918                 return false;
12919             }
12920             if (typeof(msg) == 'string') {
12921                 this.invalidText = msg;
12922             }
12923         }
12924         
12925         if(this.regex && !this.regex.test(value)){
12926             return false;
12927         }
12928         
12929         return true;
12930     },
12931     
12932      // private
12933     fireKey : function(e){
12934         //Roo.log('field ' + e.getKey());
12935         if(e.isNavKeyPress()){
12936             this.fireEvent("specialkey", this, e);
12937         }
12938     },
12939     focus : function (selectText){
12940         if(this.rendered){
12941             this.inputEl().focus();
12942             if(selectText === true){
12943                 this.inputEl().dom.select();
12944             }
12945         }
12946         return this;
12947     } ,
12948     
12949     onFocus : function(){
12950         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12951            // this.el.addClass(this.focusClass);
12952         }
12953         if(!this.hasFocus){
12954             this.hasFocus = true;
12955             this.startValue = this.getValue();
12956             this.fireEvent("focus", this);
12957         }
12958     },
12959     
12960     beforeBlur : Roo.emptyFn,
12961
12962     
12963     // private
12964     onBlur : function(){
12965         this.beforeBlur();
12966         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12967             //this.el.removeClass(this.focusClass);
12968         }
12969         this.hasFocus = false;
12970         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12971             this.validate();
12972         }
12973         var v = this.getValue();
12974         if(String(v) !== String(this.startValue)){
12975             this.fireEvent('change', this, v, this.startValue);
12976         }
12977         this.fireEvent("blur", this);
12978     },
12979     
12980     onChange : function(e)
12981     {
12982         var v = this.getValue();
12983         if(String(v) !== String(this.startValue)){
12984             this.fireEvent('change', this, v, this.startValue);
12985         }
12986         
12987     },
12988     
12989     /**
12990      * Resets the current field value to the originally loaded value and clears any validation messages
12991      */
12992     reset : function(){
12993         this.setValue(this.originalValue);
12994         this.validate();
12995     },
12996      /**
12997      * Returns the name of the field
12998      * @return {Mixed} name The name field
12999      */
13000     getName: function(){
13001         return this.name;
13002     },
13003      /**
13004      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13005      * @return {Mixed} value The field value
13006      */
13007     getValue : function(){
13008         
13009         var v = this.inputEl().getValue();
13010         
13011         return v;
13012     },
13013     /**
13014      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13015      * @return {Mixed} value The field value
13016      */
13017     getRawValue : function(){
13018         var v = this.inputEl().getValue();
13019         
13020         return v;
13021     },
13022     
13023     /**
13024      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13025      * @param {Mixed} value The value to set
13026      */
13027     setRawValue : function(v){
13028         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13029     },
13030     
13031     selectText : function(start, end){
13032         var v = this.getRawValue();
13033         if(v.length > 0){
13034             start = start === undefined ? 0 : start;
13035             end = end === undefined ? v.length : end;
13036             var d = this.inputEl().dom;
13037             if(d.setSelectionRange){
13038                 d.setSelectionRange(start, end);
13039             }else if(d.createTextRange){
13040                 var range = d.createTextRange();
13041                 range.moveStart("character", start);
13042                 range.moveEnd("character", v.length-end);
13043                 range.select();
13044             }
13045         }
13046     },
13047     
13048     /**
13049      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13050      * @param {Mixed} value The value to set
13051      */
13052     setValue : function(v){
13053         this.value = v;
13054         if(this.rendered){
13055             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13056             this.validate();
13057         }
13058     },
13059     
13060     /*
13061     processValue : function(value){
13062         if(this.stripCharsRe){
13063             var newValue = value.replace(this.stripCharsRe, '');
13064             if(newValue !== value){
13065                 this.setRawValue(newValue);
13066                 return newValue;
13067             }
13068         }
13069         return value;
13070     },
13071   */
13072     preFocus : function(){
13073         
13074         if(this.selectOnFocus){
13075             this.inputEl().dom.select();
13076         }
13077     },
13078     filterKeys : function(e){
13079         var k = e.getKey();
13080         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13081             return;
13082         }
13083         var c = e.getCharCode(), cc = String.fromCharCode(c);
13084         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13085             return;
13086         }
13087         if(!this.maskRe.test(cc)){
13088             e.stopEvent();
13089         }
13090     },
13091      /**
13092      * Clear any invalid styles/messages for this field
13093      */
13094     clearInvalid : function(){
13095         
13096         if(!this.el || this.preventMark){ // not rendered
13097             return;
13098         }
13099         
13100         
13101         this.el.removeClass([this.invalidClass, 'is-invalid']);
13102         
13103         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13104             
13105             var feedback = this.el.select('.form-control-feedback', true).first();
13106             
13107             if(feedback){
13108                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13109             }
13110             
13111         }
13112         
13113         if(this.indicator){
13114             this.indicator.removeClass('visible');
13115             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13116         }
13117         
13118         this.fireEvent('valid', this);
13119     },
13120     
13121      /**
13122      * Mark this field as valid
13123      */
13124     markValid : function()
13125     {
13126         if(!this.el  || this.preventMark){ // not rendered...
13127             return;
13128         }
13129         
13130         this.el.removeClass([this.invalidClass, this.validClass]);
13131         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13132
13133         var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135         if(feedback){
13136             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         if(this.disabled){
13145             return;
13146         }
13147         
13148            
13149         if(this.allowBlank && !this.getRawValue().length){
13150             return;
13151         }
13152         if (Roo.bootstrap.version == 3) {
13153             this.el.addClass(this.validClass);
13154         } else {
13155             this.inputEl().addClass('is-valid');
13156         }
13157
13158         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13159             
13160             var feedback = this.el.select('.form-control-feedback', true).first();
13161             
13162             if(feedback){
13163                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13164                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13165             }
13166             
13167         }
13168         
13169         this.fireEvent('valid', this);
13170     },
13171     
13172      /**
13173      * Mark this field as invalid
13174      * @param {String} msg The validation message
13175      */
13176     markInvalid : function(msg)
13177     {
13178         if(!this.el  || this.preventMark){ // not rendered
13179             return;
13180         }
13181         
13182         this.el.removeClass([this.invalidClass, this.validClass]);
13183         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13184         
13185         var feedback = this.el.select('.form-control-feedback', true).first();
13186             
13187         if(feedback){
13188             this.el.select('.form-control-feedback', true).first().removeClass(
13189                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13190         }
13191
13192         if(this.disabled){
13193             return;
13194         }
13195         
13196         if(this.allowBlank && !this.getRawValue().length){
13197             return;
13198         }
13199         
13200         if(this.indicator){
13201             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202             this.indicator.addClass('visible');
13203         }
13204         if (Roo.bootstrap.version == 3) {
13205             this.el.addClass(this.invalidClass);
13206         } else {
13207             this.inputEl().addClass('is-invalid');
13208         }
13209         
13210         
13211         
13212         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13213             
13214             var feedback = this.el.select('.form-control-feedback', true).first();
13215             
13216             if(feedback){
13217                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13218                 
13219                 if(this.getValue().length || this.forceFeedback){
13220                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13221                 }
13222                 
13223             }
13224             
13225         }
13226         
13227         this.fireEvent('invalid', this, msg);
13228     },
13229     // private
13230     SafariOnKeyDown : function(event)
13231     {
13232         // this is a workaround for a password hang bug on chrome/ webkit.
13233         if (this.inputEl().dom.type != 'password') {
13234             return;
13235         }
13236         
13237         var isSelectAll = false;
13238         
13239         if(this.inputEl().dom.selectionEnd > 0){
13240             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13241         }
13242         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13243             event.preventDefault();
13244             this.setValue('');
13245             return;
13246         }
13247         
13248         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13249             
13250             event.preventDefault();
13251             // this is very hacky as keydown always get's upper case.
13252             //
13253             var cc = String.fromCharCode(event.getCharCode());
13254             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13255             
13256         }
13257     },
13258     adjustWidth : function(tag, w){
13259         tag = tag.toLowerCase();
13260         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13261             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13262                 if(tag == 'input'){
13263                     return w + 2;
13264                 }
13265                 if(tag == 'textarea'){
13266                     return w-2;
13267                 }
13268             }else if(Roo.isOpera){
13269                 if(tag == 'input'){
13270                     return w + 2;
13271                 }
13272                 if(tag == 'textarea'){
13273                     return w-2;
13274                 }
13275             }
13276         }
13277         return w;
13278     },
13279     
13280     setFieldLabel : function(v)
13281     {
13282         if(!this.rendered){
13283             return;
13284         }
13285         
13286         if(this.indicatorEl()){
13287             var ar = this.el.select('label > span',true);
13288             
13289             if (ar.elements.length) {
13290                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13291                 this.fieldLabel = v;
13292                 return;
13293             }
13294             
13295             var br = this.el.select('label',true);
13296             
13297             if(br.elements.length) {
13298                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299                 this.fieldLabel = v;
13300                 return;
13301             }
13302             
13303             Roo.log('Cannot Found any of label > span || label in input');
13304             return;
13305         }
13306         
13307         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308         this.fieldLabel = v;
13309         
13310         
13311     }
13312 });
13313
13314  
13315 /*
13316  * - LGPL
13317  *
13318  * Input
13319  * 
13320  */
13321
13322 /**
13323  * @class Roo.bootstrap.form.TextArea
13324  * @extends Roo.bootstrap.form.Input
13325  * Bootstrap TextArea class
13326  * @cfg {Number} cols Specifies the visible width of a text area
13327  * @cfg {Number} rows Specifies the visible number of lines in a text area
13328  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13329  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13330  * @cfg {string} html text
13331  * 
13332  * @constructor
13333  * Create a new TextArea
13334  * @param {Object} config The config object
13335  */
13336
13337 Roo.bootstrap.form.TextArea = function(config){
13338     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13339    
13340 };
13341
13342 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13343      
13344     cols : false,
13345     rows : 5,
13346     readOnly : false,
13347     warp : 'soft',
13348     resize : false,
13349     value: false,
13350     html: false,
13351     
13352     getAutoCreate : function(){
13353         
13354         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13355         
13356         var id = Roo.id();
13357         
13358         var cfg = {};
13359         
13360         if(this.inputType != 'hidden'){
13361             cfg.cls = 'form-group' //input-group
13362         }
13363         
13364         var input =  {
13365             tag: 'textarea',
13366             id : id,
13367             warp : this.warp,
13368             rows : this.rows,
13369             value : this.value || '',
13370             html: this.html || '',
13371             cls : 'form-control',
13372             placeholder : this.placeholder || '' 
13373             
13374         };
13375         
13376         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13377             input.maxLength = this.maxLength;
13378         }
13379         
13380         if(this.resize){
13381             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13382         }
13383         
13384         if(this.cols){
13385             input.cols = this.cols;
13386         }
13387         
13388         if (this.readOnly) {
13389             input.readonly = true;
13390         }
13391         
13392         if (this.name) {
13393             input.name = this.name;
13394         }
13395         
13396         if (this.size) {
13397             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13398         }
13399         
13400         var settings=this;
13401         ['xs','sm','md','lg'].map(function(size){
13402             if (settings[size]) {
13403                 cfg.cls += ' col-' + size + '-' + settings[size];
13404             }
13405         });
13406         
13407         var inputblock = input;
13408         
13409         if(this.hasFeedback && !this.allowBlank){
13410             
13411             var feedback = {
13412                 tag: 'span',
13413                 cls: 'glyphicon form-control-feedback'
13414             };
13415
13416             inputblock = {
13417                 cls : 'has-feedback',
13418                 cn :  [
13419                     input,
13420                     feedback
13421                 ] 
13422             };  
13423         }
13424         
13425         
13426         if (this.before || this.after) {
13427             
13428             inputblock = {
13429                 cls : 'input-group',
13430                 cn :  [] 
13431             };
13432             if (this.before) {
13433                 inputblock.cn.push({
13434                     tag :'span',
13435                     cls : 'input-group-addon',
13436                     html : this.before
13437                 });
13438             }
13439             
13440             inputblock.cn.push(input);
13441             
13442             if(this.hasFeedback && !this.allowBlank){
13443                 inputblock.cls += ' has-feedback';
13444                 inputblock.cn.push(feedback);
13445             }
13446             
13447             if (this.after) {
13448                 inputblock.cn.push({
13449                     tag :'span',
13450                     cls : 'input-group-addon',
13451                     html : this.after
13452                 });
13453             }
13454             
13455         }
13456         
13457         if (align ==='left' && this.fieldLabel.length) {
13458             cfg.cn = [
13459                 {
13460                     tag: 'label',
13461                     'for' :  id,
13462                     cls : 'control-label',
13463                     html : this.fieldLabel
13464                 },
13465                 {
13466                     cls : "",
13467                     cn: [
13468                         inputblock
13469                     ]
13470                 }
13471
13472             ];
13473             
13474             if(this.labelWidth > 12){
13475                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13476             }
13477
13478             if(this.labelWidth < 13 && this.labelmd == 0){
13479                 this.labelmd = this.labelWidth;
13480             }
13481
13482             if(this.labellg > 0){
13483                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13484                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13485             }
13486
13487             if(this.labelmd > 0){
13488                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13489                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13490             }
13491
13492             if(this.labelsm > 0){
13493                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13494                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13495             }
13496
13497             if(this.labelxs > 0){
13498                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13499                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13500             }
13501             
13502         } else if ( this.fieldLabel.length) {
13503             cfg.cn = [
13504
13505                {
13506                    tag: 'label',
13507                    //cls : 'input-group-addon',
13508                    html : this.fieldLabel
13509
13510                },
13511
13512                inputblock
13513
13514            ];
13515
13516         } else {
13517
13518             cfg.cn = [
13519
13520                 inputblock
13521
13522             ];
13523                 
13524         }
13525         
13526         if (this.disabled) {
13527             input.disabled=true;
13528         }
13529         
13530         return cfg;
13531         
13532     },
13533     /**
13534      * return the real textarea element.
13535      */
13536     inputEl: function ()
13537     {
13538         return this.el.select('textarea.form-control',true).first();
13539     },
13540     
13541     /**
13542      * Clear any invalid styles/messages for this field
13543      */
13544     clearInvalid : function()
13545     {
13546         
13547         if(!this.el || this.preventMark){ // not rendered
13548             return;
13549         }
13550         
13551         var label = this.el.select('label', true).first();
13552         var icon = this.el.select('i.fa-star', true).first();
13553         
13554         if(label && icon){
13555             icon.remove();
13556         }
13557         this.el.removeClass( this.validClass);
13558         this.inputEl().removeClass('is-invalid');
13559          
13560         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13561             
13562             var feedback = this.el.select('.form-control-feedback', true).first();
13563             
13564             if(feedback){
13565                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13566             }
13567             
13568         }
13569         
13570         this.fireEvent('valid', this);
13571     },
13572     
13573      /**
13574      * Mark this field as valid
13575      */
13576     markValid : function()
13577     {
13578         if(!this.el  || this.preventMark){ // not rendered
13579             return;
13580         }
13581         
13582         this.el.removeClass([this.invalidClass, this.validClass]);
13583         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13584         
13585         var feedback = this.el.select('.form-control-feedback', true).first();
13586             
13587         if(feedback){
13588             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13589         }
13590
13591         if(this.disabled || this.allowBlank){
13592             return;
13593         }
13594         
13595         var label = this.el.select('label', true).first();
13596         var icon = this.el.select('i.fa-star', true).first();
13597         
13598         if(label && icon){
13599             icon.remove();
13600         }
13601         if (Roo.bootstrap.version == 3) {
13602             this.el.addClass(this.validClass);
13603         } else {
13604             this.inputEl().addClass('is-valid');
13605         }
13606         
13607         
13608         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13609             
13610             var feedback = this.el.select('.form-control-feedback', true).first();
13611             
13612             if(feedback){
13613                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13615             }
13616             
13617         }
13618         
13619         this.fireEvent('valid', this);
13620     },
13621     
13622      /**
13623      * Mark this field as invalid
13624      * @param {String} msg The validation message
13625      */
13626     markInvalid : function(msg)
13627     {
13628         if(!this.el  || this.preventMark){ // not rendered
13629             return;
13630         }
13631         
13632         this.el.removeClass([this.invalidClass, this.validClass]);
13633         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13634         
13635         var feedback = this.el.select('.form-control-feedback', true).first();
13636             
13637         if(feedback){
13638             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13639         }
13640
13641         if(this.disabled || this.allowBlank){
13642             return;
13643         }
13644         
13645         var label = this.el.select('label', true).first();
13646         var icon = this.el.select('i.fa-star', true).first();
13647         
13648         if(!this.getValue().length && label && !icon){
13649             this.el.createChild({
13650                 tag : 'i',
13651                 cls : 'text-danger fa fa-lg fa-star',
13652                 tooltip : 'This field is required',
13653                 style : 'margin-right:5px;'
13654             }, label, true);
13655         }
13656         
13657         if (Roo.bootstrap.version == 3) {
13658             this.el.addClass(this.invalidClass);
13659         } else {
13660             this.inputEl().addClass('is-invalid');
13661         }
13662         
13663         // fixme ... this may be depricated need to test..
13664         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13665             
13666             var feedback = this.el.select('.form-control-feedback', true).first();
13667             
13668             if(feedback){
13669                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13670                 
13671                 if(this.getValue().length || this.forceFeedback){
13672                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13673                 }
13674                 
13675             }
13676             
13677         }
13678         
13679         this.fireEvent('invalid', this, msg);
13680     }
13681 });
13682
13683  
13684 /*
13685  * - LGPL
13686  *
13687  * trigger field - base class for combo..
13688  * 
13689  */
13690  
13691 /**
13692  * @class Roo.bootstrap.form.TriggerField
13693  * @extends Roo.bootstrap.form.Input
13694  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13695  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13696  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13697  * for which you can provide a custom implementation.  For example:
13698  * <pre><code>
13699 var trigger = new Roo.bootstrap.form.TriggerField();
13700 trigger.onTriggerClick = myTriggerFn;
13701 trigger.applyTo('my-field');
13702 </code></pre>
13703  *
13704  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13705  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13706  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13707  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13708  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13709
13710  * @constructor
13711  * Create a new TriggerField.
13712  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13713  * to the base TextField)
13714  */
13715 Roo.bootstrap.form.TriggerField = function(config){
13716     this.mimicing = false;
13717     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13718 };
13719
13720 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13721     /**
13722      * @cfg {String} triggerClass A CSS class to apply to the trigger
13723      */
13724      /**
13725      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13726      */
13727     hideTrigger:false,
13728
13729     /**
13730      * @cfg {Boolean} removable (true|false) special filter default false
13731      */
13732     removable : false,
13733     
13734     /** @cfg {Boolean} grow @hide */
13735     /** @cfg {Number} growMin @hide */
13736     /** @cfg {Number} growMax @hide */
13737
13738     /**
13739      * @hide 
13740      * @method
13741      */
13742     autoSize: Roo.emptyFn,
13743     // private
13744     monitorTab : true,
13745     // private
13746     deferHeight : true,
13747
13748     
13749     actionMode : 'wrap',
13750     
13751     caret : false,
13752     
13753     
13754     getAutoCreate : function(){
13755        
13756         var align = this.labelAlign || this.parentLabelAlign();
13757         
13758         var id = Roo.id();
13759         
13760         var cfg = {
13761             cls: 'form-group' //input-group
13762         };
13763         
13764         
13765         var input =  {
13766             tag: 'input',
13767             id : id,
13768             type : this.inputType,
13769             cls : 'form-control',
13770             autocomplete: 'new-password',
13771             placeholder : this.placeholder || '' 
13772             
13773         };
13774         if (this.name) {
13775             input.name = this.name;
13776         }
13777         if (this.size) {
13778             input.cls += ' input-' + this.size;
13779         }
13780         
13781         if (this.disabled) {
13782             input.disabled=true;
13783         }
13784         
13785         var inputblock = input;
13786         
13787         if(this.hasFeedback && !this.allowBlank){
13788             
13789             var feedback = {
13790                 tag: 'span',
13791                 cls: 'glyphicon form-control-feedback'
13792             };
13793             
13794             if(this.removable && !this.editable  ){
13795                 inputblock = {
13796                     cls : 'has-feedback',
13797                     cn :  [
13798                         inputblock,
13799                         {
13800                             tag: 'button',
13801                             html : 'x',
13802                             cls : 'roo-combo-removable-btn close'
13803                         },
13804                         feedback
13805                     ] 
13806                 };
13807             } else {
13808                 inputblock = {
13809                     cls : 'has-feedback',
13810                     cn :  [
13811                         inputblock,
13812                         feedback
13813                     ] 
13814                 };
13815             }
13816
13817         } else {
13818             if(this.removable && !this.editable ){
13819                 inputblock = {
13820                     cls : 'roo-removable',
13821                     cn :  [
13822                         inputblock,
13823                         {
13824                             tag: 'button',
13825                             html : 'x',
13826                             cls : 'roo-combo-removable-btn close'
13827                         }
13828                     ] 
13829                 };
13830             }
13831         }
13832         
13833         if (this.before || this.after) {
13834             
13835             inputblock = {
13836                 cls : 'input-group',
13837                 cn :  [] 
13838             };
13839             if (this.before) {
13840                 inputblock.cn.push({
13841                     tag :'span',
13842                     cls : 'input-group-addon input-group-prepend input-group-text',
13843                     html : this.before
13844                 });
13845             }
13846             
13847             inputblock.cn.push(input);
13848             
13849             if(this.hasFeedback && !this.allowBlank){
13850                 inputblock.cls += ' has-feedback';
13851                 inputblock.cn.push(feedback);
13852             }
13853             
13854             if (this.after) {
13855                 inputblock.cn.push({
13856                     tag :'span',
13857                     cls : 'input-group-addon input-group-append input-group-text',
13858                     html : this.after
13859                 });
13860             }
13861             
13862         };
13863         
13864       
13865         
13866         var ibwrap = inputblock;
13867         
13868         if(this.multiple){
13869             ibwrap = {
13870                 tag: 'ul',
13871                 cls: 'roo-select2-choices',
13872                 cn:[
13873                     {
13874                         tag: 'li',
13875                         cls: 'roo-select2-search-field',
13876                         cn: [
13877
13878                             inputblock
13879                         ]
13880                     }
13881                 ]
13882             };
13883                 
13884         }
13885         
13886         var combobox = {
13887             cls: 'roo-select2-container input-group',
13888             cn: [
13889                  {
13890                     tag: 'input',
13891                     type : 'hidden',
13892                     cls: 'form-hidden-field'
13893                 },
13894                 ibwrap
13895             ]
13896         };
13897         
13898         if(!this.multiple && this.showToggleBtn){
13899             
13900             var caret = {
13901                         tag: 'span',
13902                         cls: 'caret'
13903              };
13904             if (this.caret != false) {
13905                 caret = {
13906                      tag: 'i',
13907                      cls: 'fa fa-' + this.caret
13908                 };
13909                 
13910             }
13911             
13912             combobox.cn.push({
13913                 tag :'span',
13914                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13915                 cn : [
13916                     Roo.bootstrap.version == 3 ? caret : '',
13917                     {
13918                         tag: 'span',
13919                         cls: 'combobox-clear',
13920                         cn  : [
13921                             {
13922                                 tag : 'i',
13923                                 cls: 'icon-remove'
13924                             }
13925                         ]
13926                     }
13927                 ]
13928
13929             })
13930         }
13931         
13932         if(this.multiple){
13933             combobox.cls += ' roo-select2-container-multi';
13934         }
13935          var indicator = {
13936             tag : 'i',
13937             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13938             tooltip : 'This field is required'
13939         };
13940         if (Roo.bootstrap.version == 4) {
13941             indicator = {
13942                 tag : 'i',
13943                 style : 'display:none'
13944             };
13945         }
13946         
13947         
13948         if (align ==='left' && this.fieldLabel.length) {
13949             
13950             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13951
13952             cfg.cn = [
13953                 indicator,
13954                 {
13955                     tag: 'label',
13956                     'for' :  id,
13957                     cls : 'control-label',
13958                     html : this.fieldLabel
13959
13960                 },
13961                 {
13962                     cls : "", 
13963                     cn: [
13964                         combobox
13965                     ]
13966                 }
13967
13968             ];
13969             
13970             var labelCfg = cfg.cn[1];
13971             var contentCfg = cfg.cn[2];
13972             
13973             if(this.indicatorpos == 'right'){
13974                 cfg.cn = [
13975                     {
13976                         tag: 'label',
13977                         'for' :  id,
13978                         cls : 'control-label',
13979                         cn : [
13980                             {
13981                                 tag : 'span',
13982                                 html : this.fieldLabel
13983                             },
13984                             indicator
13985                         ]
13986                     },
13987                     {
13988                         cls : "", 
13989                         cn: [
13990                             combobox
13991                         ]
13992                     }
13993
13994                 ];
13995                 
13996                 labelCfg = cfg.cn[0];
13997                 contentCfg = cfg.cn[1];
13998             }
13999             
14000             if(this.labelWidth > 12){
14001                 labelCfg.style = "width: " + this.labelWidth + 'px';
14002             }
14003             
14004             if(this.labelWidth < 13 && this.labelmd == 0){
14005                 this.labelmd = this.labelWidth;
14006             }
14007             
14008             if(this.labellg > 0){
14009                 labelCfg.cls += ' col-lg-' + this.labellg;
14010                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14011             }
14012             
14013             if(this.labelmd > 0){
14014                 labelCfg.cls += ' col-md-' + this.labelmd;
14015                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14016             }
14017             
14018             if(this.labelsm > 0){
14019                 labelCfg.cls += ' col-sm-' + this.labelsm;
14020                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14021             }
14022             
14023             if(this.labelxs > 0){
14024                 labelCfg.cls += ' col-xs-' + this.labelxs;
14025                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14026             }
14027             
14028         } else if ( this.fieldLabel.length) {
14029 //                Roo.log(" label");
14030             cfg.cn = [
14031                 indicator,
14032                {
14033                    tag: 'label',
14034                    //cls : 'input-group-addon',
14035                    html : this.fieldLabel
14036
14037                },
14038
14039                combobox
14040
14041             ];
14042             
14043             if(this.indicatorpos == 'right'){
14044                 
14045                 cfg.cn = [
14046                     {
14047                        tag: 'label',
14048                        cn : [
14049                            {
14050                                tag : 'span',
14051                                html : this.fieldLabel
14052                            },
14053                            indicator
14054                        ]
14055
14056                     },
14057                     combobox
14058
14059                 ];
14060
14061             }
14062
14063         } else {
14064             
14065 //                Roo.log(" no label && no align");
14066                 cfg = combobox
14067                      
14068                 
14069         }
14070         
14071         var settings=this;
14072         ['xs','sm','md','lg'].map(function(size){
14073             if (settings[size]) {
14074                 cfg.cls += ' col-' + size + '-' + settings[size];
14075             }
14076         });
14077         
14078         return cfg;
14079         
14080     },
14081     
14082     
14083     
14084     // private
14085     onResize : function(w, h){
14086 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14087 //        if(typeof w == 'number'){
14088 //            var x = w - this.trigger.getWidth();
14089 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14090 //            this.trigger.setStyle('left', x+'px');
14091 //        }
14092     },
14093
14094     // private
14095     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14096
14097     // private
14098     getResizeEl : function(){
14099         return this.inputEl();
14100     },
14101
14102     // private
14103     getPositionEl : function(){
14104         return this.inputEl();
14105     },
14106
14107     // private
14108     alignErrorIcon : function(){
14109         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14110     },
14111
14112     // private
14113     initEvents : function(){
14114         
14115         this.createList();
14116         
14117         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14118         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14119         if(!this.multiple && this.showToggleBtn){
14120             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14121             if(this.hideTrigger){
14122                 this.trigger.setDisplayed(false);
14123             }
14124             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14125         }
14126         
14127         if(this.multiple){
14128             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14129         }
14130         
14131         if(this.removable && !this.editable && !this.tickable){
14132             var close = this.closeTriggerEl();
14133             
14134             if(close){
14135                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14136                 close.on('click', this.removeBtnClick, this, close);
14137             }
14138         }
14139         
14140         //this.trigger.addClassOnOver('x-form-trigger-over');
14141         //this.trigger.addClassOnClick('x-form-trigger-click');
14142         
14143         //if(!this.width){
14144         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14145         //}
14146     },
14147     
14148     closeTriggerEl : function()
14149     {
14150         var close = this.el.select('.roo-combo-removable-btn', true).first();
14151         return close ? close : false;
14152     },
14153     
14154     removeBtnClick : function(e, h, el)
14155     {
14156         e.preventDefault();
14157         
14158         if(this.fireEvent("remove", this) !== false){
14159             this.reset();
14160             this.fireEvent("afterremove", this)
14161         }
14162     },
14163     
14164     createList : function()
14165     {
14166         this.list = Roo.get(document.body).createChild({
14167             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14168             cls: 'typeahead typeahead-long dropdown-menu shadow',
14169             style: 'display:none'
14170         });
14171         
14172         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14173         
14174     },
14175
14176     // private
14177     initTrigger : function(){
14178        
14179     },
14180
14181     // private
14182     onDestroy : function(){
14183         if(this.trigger){
14184             this.trigger.removeAllListeners();
14185           //  this.trigger.remove();
14186         }
14187         //if(this.wrap){
14188         //    this.wrap.remove();
14189         //}
14190         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14191     },
14192
14193     // private
14194     onFocus : function(){
14195         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14196         /*
14197         if(!this.mimicing){
14198             this.wrap.addClass('x-trigger-wrap-focus');
14199             this.mimicing = true;
14200             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14201             if(this.monitorTab){
14202                 this.el.on("keydown", this.checkTab, this);
14203             }
14204         }
14205         */
14206     },
14207
14208     // private
14209     checkTab : function(e){
14210         if(e.getKey() == e.TAB){
14211             this.triggerBlur();
14212         }
14213     },
14214
14215     // private
14216     onBlur : function(){
14217         // do nothing
14218     },
14219
14220     // private
14221     mimicBlur : function(e, t){
14222         /*
14223         if(!this.wrap.contains(t) && this.validateBlur()){
14224             this.triggerBlur();
14225         }
14226         */
14227     },
14228
14229     // private
14230     triggerBlur : function(){
14231         this.mimicing = false;
14232         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14233         if(this.monitorTab){
14234             this.el.un("keydown", this.checkTab, this);
14235         }
14236         //this.wrap.removeClass('x-trigger-wrap-focus');
14237         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14238     },
14239
14240     // private
14241     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14242     validateBlur : function(e, t){
14243         return true;
14244     },
14245
14246     // private
14247     onDisable : function(){
14248         this.inputEl().dom.disabled = true;
14249         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14250         //if(this.wrap){
14251         //    this.wrap.addClass('x-item-disabled');
14252         //}
14253     },
14254
14255     // private
14256     onEnable : function(){
14257         this.inputEl().dom.disabled = false;
14258         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14259         //if(this.wrap){
14260         //    this.el.removeClass('x-item-disabled');
14261         //}
14262     },
14263
14264     // private
14265     onShow : function(){
14266         var ae = this.getActionEl();
14267         
14268         if(ae){
14269             ae.dom.style.display = '';
14270             ae.dom.style.visibility = 'visible';
14271         }
14272     },
14273
14274     // private
14275     
14276     onHide : function(){
14277         var ae = this.getActionEl();
14278         ae.dom.style.display = 'none';
14279     },
14280
14281     /**
14282      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14283      * by an implementing function.
14284      * @method
14285      * @param {EventObject} e
14286      */
14287     onTriggerClick : Roo.emptyFn
14288 });
14289  
14290 /*
14291 * Licence: LGPL
14292 */
14293
14294 /**
14295  * @class Roo.bootstrap.form.CardUploader
14296  * @extends Roo.bootstrap.Button
14297  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14298  * @cfg {Number} errorTimeout default 3000
14299  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14300  * @cfg {Array}  html The button text.
14301
14302  *
14303  * @constructor
14304  * Create a new CardUploader
14305  * @param {Object} config The config object
14306  */
14307
14308 Roo.bootstrap.form.CardUploader = function(config){
14309     
14310  
14311     
14312     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14313     
14314     
14315     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14316         return r.data.id
14317      });
14318     
14319      this.addEvents({
14320          // raw events
14321         /**
14322          * @event preview
14323          * When a image is clicked on - and needs to display a slideshow or similar..
14324          * @param {Roo.bootstrap.Card} this
14325          * @param {Object} The image information data 
14326          *
14327          */
14328         'preview' : true,
14329          /**
14330          * @event download
14331          * When a the download link is clicked
14332          * @param {Roo.bootstrap.Card} this
14333          * @param {Object} The image information data  contains 
14334          */
14335         'download' : true
14336         
14337     });
14338 };
14339  
14340 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14341     
14342      
14343     errorTimeout : 3000,
14344      
14345     images : false,
14346    
14347     fileCollection : false,
14348     allowBlank : true,
14349     
14350     getAutoCreate : function()
14351     {
14352         
14353         var cfg =  {
14354             cls :'form-group' ,
14355             cn : [
14356                
14357                 {
14358                     tag: 'label',
14359                    //cls : 'input-group-addon',
14360                     html : this.fieldLabel
14361
14362                 },
14363
14364                 {
14365                     tag: 'input',
14366                     type : 'hidden',
14367                     name : this.name,
14368                     value : this.value,
14369                     cls : 'd-none  form-control'
14370                 },
14371                 
14372                 {
14373                     tag: 'input',
14374                     multiple : 'multiple',
14375                     type : 'file',
14376                     cls : 'd-none  roo-card-upload-selector'
14377                 },
14378                 
14379                 {
14380                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14381                 },
14382                 {
14383                     cls : 'card-columns roo-card-uploader-container'
14384                 }
14385
14386             ]
14387         };
14388            
14389          
14390         return cfg;
14391     },
14392     
14393     getChildContainer : function() /// what children are added to.
14394     {
14395         return this.containerEl;
14396     },
14397    
14398     getButtonContainer : function() /// what children are added to.
14399     {
14400         return this.el.select(".roo-card-uploader-button-container").first();
14401     },
14402    
14403     initEvents : function()
14404     {
14405         
14406         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14407         
14408         var t = this;
14409         this.addxtype({
14410             xns: Roo.bootstrap,
14411
14412             xtype : 'Button',
14413             container_method : 'getButtonContainer' ,            
14414             html :  this.html, // fix changable?
14415             cls : 'w-100 ',
14416             listeners : {
14417                 'click' : function(btn, e) {
14418                     t.onClick(e);
14419                 }
14420             }
14421         });
14422         
14423         
14424         
14425         
14426         this.urlAPI = (window.createObjectURL && window) || 
14427                                 (window.URL && URL.revokeObjectURL && URL) || 
14428                                 (window.webkitURL && webkitURL);
14429                         
14430          
14431          
14432          
14433         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14434         
14435         this.selectorEl.on('change', this.onFileSelected, this);
14436         if (this.images) {
14437             var t = this;
14438             this.images.forEach(function(img) {
14439                 t.addCard(img)
14440             });
14441             this.images = false;
14442         }
14443         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14444          
14445        
14446     },
14447     
14448    
14449     onClick : function(e)
14450     {
14451         e.preventDefault();
14452          
14453         this.selectorEl.dom.click();
14454          
14455     },
14456     
14457     onFileSelected : function(e)
14458     {
14459         e.preventDefault();
14460         
14461         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14462             return;
14463         }
14464         
14465         Roo.each(this.selectorEl.dom.files, function(file){    
14466             this.addFile(file);
14467         }, this);
14468          
14469     },
14470     
14471       
14472     
14473       
14474     
14475     addFile : function(file)
14476     {
14477            
14478         if(typeof(file) === 'string'){
14479             throw "Add file by name?"; // should not happen
14480             return;
14481         }
14482         
14483         if(!file || !this.urlAPI){
14484             return;
14485         }
14486         
14487         // file;
14488         // file.type;
14489         
14490         var _this = this;
14491         
14492         
14493         var url = _this.urlAPI.createObjectURL( file);
14494            
14495         this.addCard({
14496             id : Roo.bootstrap.form.CardUploader.ID--,
14497             is_uploaded : false,
14498             src : url,
14499             srcfile : file,
14500             title : file.name,
14501             mimetype : file.type,
14502             preview : false,
14503             is_deleted : 0
14504         });
14505         
14506     },
14507     
14508     /**
14509      * addCard - add an Attachment to the uploader
14510      * @param data - the data about the image to upload
14511      *
14512      * {
14513           id : 123
14514           title : "Title of file",
14515           is_uploaded : false,
14516           src : "http://.....",
14517           srcfile : { the File upload object },
14518           mimetype : file.type,
14519           preview : false,
14520           is_deleted : 0
14521           .. any other data...
14522         }
14523      *
14524      * 
14525     */
14526     
14527     addCard : function (data)
14528     {
14529         // hidden input element?
14530         // if the file is not an image...
14531         //then we need to use something other that and header_image
14532         var t = this;
14533         //   remove.....
14534         var footer = [
14535             {
14536                 xns : Roo.bootstrap,
14537                 xtype : 'CardFooter',
14538                  items: [
14539                     {
14540                         xns : Roo.bootstrap,
14541                         xtype : 'Element',
14542                         cls : 'd-flex',
14543                         items : [
14544                             
14545                             {
14546                                 xns : Roo.bootstrap,
14547                                 xtype : 'Button',
14548                                 html : String.format("<small>{0}</small>", data.title),
14549                                 cls : 'col-10 text-left',
14550                                 size: 'sm',
14551                                 weight: 'link',
14552                                 fa : 'download',
14553                                 listeners : {
14554                                     click : function() {
14555                                      
14556                                         t.fireEvent( "download", t, data );
14557                                     }
14558                                 }
14559                             },
14560                           
14561                             {
14562                                 xns : Roo.bootstrap,
14563                                 xtype : 'Button',
14564                                 style: 'max-height: 28px; ',
14565                                 size : 'sm',
14566                                 weight: 'danger',
14567                                 cls : 'col-2',
14568                                 fa : 'times',
14569                                 listeners : {
14570                                     click : function() {
14571                                         t.removeCard(data.id)
14572                                     }
14573                                 }
14574                             }
14575                         ]
14576                     }
14577                     
14578                 ] 
14579             }
14580             
14581         ];
14582         
14583         var cn = this.addxtype(
14584             {
14585                  
14586                 xns : Roo.bootstrap,
14587                 xtype : 'Card',
14588                 closeable : true,
14589                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14590                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14591                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14592                 data : data,
14593                 html : false,
14594                  
14595                 items : footer,
14596                 initEvents : function() {
14597                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14598                     var card = this;
14599                     this.imgEl = this.el.select('.card-img-top').first();
14600                     if (this.imgEl) {
14601                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14602                         this.imgEl.set({ 'pointer' : 'cursor' });
14603                                   
14604                     }
14605                     this.getCardFooter().addClass('p-1');
14606                     
14607                   
14608                 }
14609                 
14610             }
14611         );
14612         // dont' really need ot update items.
14613         // this.items.push(cn);
14614         this.fileCollection.add(cn);
14615         
14616         if (!data.srcfile) {
14617             this.updateInput();
14618             return;
14619         }
14620             
14621         var _t = this;
14622         var reader = new FileReader();
14623         reader.addEventListener("load", function() {  
14624             data.srcdata =  reader.result;
14625             _t.updateInput();
14626         });
14627         reader.readAsDataURL(data.srcfile);
14628         
14629         
14630         
14631     },
14632     removeCard : function(id)
14633     {
14634         
14635         var card  = this.fileCollection.get(id);
14636         card.data.is_deleted = 1;
14637         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14638         //this.fileCollection.remove(card);
14639         //this.items = this.items.filter(function(e) { return e != card });
14640         // dont' really need ot update items.
14641         card.el.dom.parentNode.removeChild(card.el.dom);
14642         this.updateInput();
14643
14644         
14645     },
14646     reset: function()
14647     {
14648         this.fileCollection.each(function(card) {
14649             if (card.el.dom && card.el.dom.parentNode) {
14650                 card.el.dom.parentNode.removeChild(card.el.dom);
14651             }
14652         });
14653         this.fileCollection.clear();
14654         this.updateInput();
14655     },
14656     
14657     updateInput : function()
14658     {
14659          var data = [];
14660         this.fileCollection.each(function(e) {
14661             data.push(e.data);
14662             
14663         });
14664         this.inputEl().dom.value = JSON.stringify(data);
14665         
14666         
14667         
14668     }
14669     
14670     
14671 });
14672
14673
14674 Roo.bootstrap.form.CardUploader.ID = -1;/*
14675  * Based on:
14676  * Ext JS Library 1.1.1
14677  * Copyright(c) 2006-2007, Ext JS, LLC.
14678  *
14679  * Originally Released Under LGPL - original licence link has changed is not relivant.
14680  *
14681  * Fork - LGPL
14682  * <script type="text/javascript">
14683  */
14684
14685
14686 /**
14687  * @class Roo.data.SortTypes
14688  * @static
14689  * Defines the default sorting (casting?) comparison functions used when sorting data.
14690  */
14691 Roo.data.SortTypes = {
14692     /**
14693      * Default sort that does nothing
14694      * @param {Mixed} s The value being converted
14695      * @return {Mixed} The comparison value
14696      */
14697     none : function(s){
14698         return s;
14699     },
14700     
14701     /**
14702      * The regular expression used to strip tags
14703      * @type {RegExp}
14704      * @property
14705      */
14706     stripTagsRE : /<\/?[^>]+>/gi,
14707     
14708     /**
14709      * Strips all HTML tags to sort on text only
14710      * @param {Mixed} s The value being converted
14711      * @return {String} The comparison value
14712      */
14713     asText : function(s){
14714         return String(s).replace(this.stripTagsRE, "");
14715     },
14716     
14717     /**
14718      * Strips all HTML tags to sort on text only - Case insensitive
14719      * @param {Mixed} s The value being converted
14720      * @return {String} The comparison value
14721      */
14722     asUCText : function(s){
14723         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14724     },
14725     
14726     /**
14727      * Case insensitive string
14728      * @param {Mixed} s The value being converted
14729      * @return {String} The comparison value
14730      */
14731     asUCString : function(s) {
14732         return String(s).toUpperCase();
14733     },
14734     
14735     /**
14736      * Date sorting
14737      * @param {Mixed} s The value being converted
14738      * @return {Number} The comparison value
14739      */
14740     asDate : function(s) {
14741         if(!s){
14742             return 0;
14743         }
14744         if(s instanceof Date){
14745             return s.getTime();
14746         }
14747         return Date.parse(String(s));
14748     },
14749     
14750     /**
14751      * Float sorting
14752      * @param {Mixed} s The value being converted
14753      * @return {Float} The comparison value
14754      */
14755     asFloat : function(s) {
14756         var val = parseFloat(String(s).replace(/,/g, ""));
14757         if(isNaN(val)) {
14758             val = 0;
14759         }
14760         return val;
14761     },
14762     
14763     /**
14764      * Integer sorting
14765      * @param {Mixed} s The value being converted
14766      * @return {Number} The comparison value
14767      */
14768     asInt : function(s) {
14769         var val = parseInt(String(s).replace(/,/g, ""));
14770         if(isNaN(val)) {
14771             val = 0;
14772         }
14773         return val;
14774     }
14775 };/*
14776  * Based on:
14777  * Ext JS Library 1.1.1
14778  * Copyright(c) 2006-2007, Ext JS, LLC.
14779  *
14780  * Originally Released Under LGPL - original licence link has changed is not relivant.
14781  *
14782  * Fork - LGPL
14783  * <script type="text/javascript">
14784  */
14785
14786 /**
14787 * @class Roo.data.Record
14788  * Instances of this class encapsulate both record <em>definition</em> information, and record
14789  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14790  * to access Records cached in an {@link Roo.data.Store} object.<br>
14791  * <p>
14792  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14793  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14794  * objects.<br>
14795  * <p>
14796  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14797  * @constructor
14798  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14799  * {@link #create}. The parameters are the same.
14800  * @param {Array} data An associative Array of data values keyed by the field name.
14801  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14802  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14803  * not specified an integer id is generated.
14804  */
14805 Roo.data.Record = function(data, id){
14806     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14807     this.data = data;
14808 };
14809
14810 /**
14811  * Generate a constructor for a specific record layout.
14812  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14813  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14814  * Each field definition object may contain the following properties: <ul>
14815  * <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,
14816  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14817  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14818  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14819  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14820  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14821  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14822  * this may be omitted.</p></li>
14823  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14824  * <ul><li>auto (Default, implies no conversion)</li>
14825  * <li>string</li>
14826  * <li>int</li>
14827  * <li>float</li>
14828  * <li>boolean</li>
14829  * <li>date</li></ul></p></li>
14830  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14831  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14832  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14833  * by the Reader into an object that will be stored in the Record. It is passed the
14834  * following parameters:<ul>
14835  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14836  * </ul></p></li>
14837  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14838  * </ul>
14839  * <br>usage:<br><pre><code>
14840 var TopicRecord = Roo.data.Record.create(
14841     {name: 'title', mapping: 'topic_title'},
14842     {name: 'author', mapping: 'username'},
14843     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14844     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14845     {name: 'lastPoster', mapping: 'user2'},
14846     {name: 'excerpt', mapping: 'post_text'}
14847 );
14848
14849 var myNewRecord = new TopicRecord({
14850     title: 'Do my job please',
14851     author: 'noobie',
14852     totalPosts: 1,
14853     lastPost: new Date(),
14854     lastPoster: 'Animal',
14855     excerpt: 'No way dude!'
14856 });
14857 myStore.add(myNewRecord);
14858 </code></pre>
14859  * @method create
14860  * @static
14861  */
14862 Roo.data.Record.create = function(o){
14863     var f = function(){
14864         f.superclass.constructor.apply(this, arguments);
14865     };
14866     Roo.extend(f, Roo.data.Record);
14867     var p = f.prototype;
14868     p.fields = new Roo.util.MixedCollection(false, function(field){
14869         return field.name;
14870     });
14871     for(var i = 0, len = o.length; i < len; i++){
14872         p.fields.add(new Roo.data.Field(o[i]));
14873     }
14874     f.getField = function(name){
14875         return p.fields.get(name);  
14876     };
14877     return f;
14878 };
14879
14880 Roo.data.Record.AUTO_ID = 1000;
14881 Roo.data.Record.EDIT = 'edit';
14882 Roo.data.Record.REJECT = 'reject';
14883 Roo.data.Record.COMMIT = 'commit';
14884
14885 Roo.data.Record.prototype = {
14886     /**
14887      * Readonly flag - true if this record has been modified.
14888      * @type Boolean
14889      */
14890     dirty : false,
14891     editing : false,
14892     error: null,
14893     modified: null,
14894
14895     // private
14896     join : function(store){
14897         this.store = store;
14898     },
14899
14900     /**
14901      * Set the named field to the specified value.
14902      * @param {String} name The name of the field to set.
14903      * @param {Object} value The value to set the field to.
14904      */
14905     set : function(name, value){
14906         if(this.data[name] == value){
14907             return;
14908         }
14909         this.dirty = true;
14910         if(!this.modified){
14911             this.modified = {};
14912         }
14913         if(typeof this.modified[name] == 'undefined'){
14914             this.modified[name] = this.data[name];
14915         }
14916         this.data[name] = value;
14917         if(!this.editing && this.store){
14918             this.store.afterEdit(this);
14919         }       
14920     },
14921
14922     /**
14923      * Get the value of the named field.
14924      * @param {String} name The name of the field to get the value of.
14925      * @return {Object} The value of the field.
14926      */
14927     get : function(name){
14928         return this.data[name]; 
14929     },
14930
14931     // private
14932     beginEdit : function(){
14933         this.editing = true;
14934         this.modified = {}; 
14935     },
14936
14937     // private
14938     cancelEdit : function(){
14939         this.editing = false;
14940         delete this.modified;
14941     },
14942
14943     // private
14944     endEdit : function(){
14945         this.editing = false;
14946         if(this.dirty && this.store){
14947             this.store.afterEdit(this);
14948         }
14949     },
14950
14951     /**
14952      * Usually called by the {@link Roo.data.Store} which owns the Record.
14953      * Rejects all changes made to the Record since either creation, or the last commit operation.
14954      * Modified fields are reverted to their original values.
14955      * <p>
14956      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14957      * of reject operations.
14958      */
14959     reject : function(){
14960         var m = this.modified;
14961         for(var n in m){
14962             if(typeof m[n] != "function"){
14963                 this.data[n] = m[n];
14964             }
14965         }
14966         this.dirty = false;
14967         delete this.modified;
14968         this.editing = false;
14969         if(this.store){
14970             this.store.afterReject(this);
14971         }
14972     },
14973
14974     /**
14975      * Usually called by the {@link Roo.data.Store} which owns the Record.
14976      * Commits all changes made to the Record since either creation, or the last commit operation.
14977      * <p>
14978      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14979      * of commit operations.
14980      */
14981     commit : function(){
14982         this.dirty = false;
14983         delete this.modified;
14984         this.editing = false;
14985         if(this.store){
14986             this.store.afterCommit(this);
14987         }
14988     },
14989
14990     // private
14991     hasError : function(){
14992         return this.error != null;
14993     },
14994
14995     // private
14996     clearError : function(){
14997         this.error = null;
14998     },
14999
15000     /**
15001      * Creates a copy of this record.
15002      * @param {String} id (optional) A new record id if you don't want to use this record's id
15003      * @return {Record}
15004      */
15005     copy : function(newId) {
15006         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15007     }
15008 };/*
15009  * Based on:
15010  * Ext JS Library 1.1.1
15011  * Copyright(c) 2006-2007, Ext JS, LLC.
15012  *
15013  * Originally Released Under LGPL - original licence link has changed is not relivant.
15014  *
15015  * Fork - LGPL
15016  * <script type="text/javascript">
15017  */
15018
15019
15020
15021 /**
15022  * @class Roo.data.Store
15023  * @extends Roo.util.Observable
15024  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15025  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15026  * <p>
15027  * 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
15028  * has no knowledge of the format of the data returned by the Proxy.<br>
15029  * <p>
15030  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15031  * instances from the data object. These records are cached and made available through accessor functions.
15032  * @constructor
15033  * Creates a new Store.
15034  * @param {Object} config A config object containing the objects needed for the Store to access data,
15035  * and read the data into Records.
15036  */
15037 Roo.data.Store = function(config){
15038     this.data = new Roo.util.MixedCollection(false);
15039     this.data.getKey = function(o){
15040         return o.id;
15041     };
15042     this.baseParams = {};
15043     // private
15044     this.paramNames = {
15045         "start" : "start",
15046         "limit" : "limit",
15047         "sort" : "sort",
15048         "dir" : "dir",
15049         "multisort" : "_multisort"
15050     };
15051
15052     if(config && config.data){
15053         this.inlineData = config.data;
15054         delete config.data;
15055     }
15056
15057     Roo.apply(this, config);
15058     
15059     if(this.reader){ // reader passed
15060         this.reader = Roo.factory(this.reader, Roo.data);
15061         this.reader.xmodule = this.xmodule || false;
15062         if(!this.recordType){
15063             this.recordType = this.reader.recordType;
15064         }
15065         if(this.reader.onMetaChange){
15066             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15067         }
15068     }
15069
15070     if(this.recordType){
15071         this.fields = this.recordType.prototype.fields;
15072     }
15073     this.modified = [];
15074
15075     this.addEvents({
15076         /**
15077          * @event datachanged
15078          * Fires when the data cache has changed, and a widget which is using this Store
15079          * as a Record cache should refresh its view.
15080          * @param {Store} this
15081          */
15082         datachanged : true,
15083         /**
15084          * @event metachange
15085          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15086          * @param {Store} this
15087          * @param {Object} meta The JSON metadata
15088          */
15089         metachange : true,
15090         /**
15091          * @event add
15092          * Fires when Records have been added to the Store
15093          * @param {Store} this
15094          * @param {Roo.data.Record[]} records The array of Records added
15095          * @param {Number} index The index at which the record(s) were added
15096          */
15097         add : true,
15098         /**
15099          * @event remove
15100          * Fires when a Record has been removed from the Store
15101          * @param {Store} this
15102          * @param {Roo.data.Record} record The Record that was removed
15103          * @param {Number} index The index at which the record was removed
15104          */
15105         remove : true,
15106         /**
15107          * @event update
15108          * Fires when a Record has been updated
15109          * @param {Store} this
15110          * @param {Roo.data.Record} record The Record that was updated
15111          * @param {String} operation The update operation being performed.  Value may be one of:
15112          * <pre><code>
15113  Roo.data.Record.EDIT
15114  Roo.data.Record.REJECT
15115  Roo.data.Record.COMMIT
15116          * </code></pre>
15117          */
15118         update : true,
15119         /**
15120          * @event clear
15121          * Fires when the data cache has been cleared.
15122          * @param {Store} this
15123          */
15124         clear : true,
15125         /**
15126          * @event beforeload
15127          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15128          * the load action will be canceled.
15129          * @param {Store} this
15130          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15131          */
15132         beforeload : true,
15133         /**
15134          * @event beforeloadadd
15135          * Fires after a new set of Records has been loaded.
15136          * @param {Store} this
15137          * @param {Roo.data.Record[]} records The Records that were loaded
15138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15139          */
15140         beforeloadadd : true,
15141         /**
15142          * @event load
15143          * Fires after a new set of Records has been loaded, before they are added to the store.
15144          * @param {Store} this
15145          * @param {Roo.data.Record[]} records The Records that were loaded
15146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147          * @params {Object} return from reader
15148          */
15149         load : true,
15150         /**
15151          * @event loadexception
15152          * Fires if an exception occurs in the Proxy during loading.
15153          * Called with the signature of the Proxy's "loadexception" event.
15154          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15155          * 
15156          * @param {Proxy} 
15157          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15158          * @param {Object} load options 
15159          * @param {Object} jsonData from your request (normally this contains the Exception)
15160          */
15161         loadexception : true
15162     });
15163     
15164     if(this.proxy){
15165         this.proxy = Roo.factory(this.proxy, Roo.data);
15166         this.proxy.xmodule = this.xmodule || false;
15167         this.relayEvents(this.proxy,  ["loadexception"]);
15168     }
15169     this.sortToggle = {};
15170     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15171
15172     Roo.data.Store.superclass.constructor.call(this);
15173
15174     if(this.inlineData){
15175         this.loadData(this.inlineData);
15176         delete this.inlineData;
15177     }
15178 };
15179
15180 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15181      /**
15182     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15183     * without a remote query - used by combo/forms at present.
15184     */
15185     
15186     /**
15187     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15188     */
15189     /**
15190     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15191     */
15192     /**
15193     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15194     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15195     */
15196     /**
15197     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15198     * on any HTTP request
15199     */
15200     /**
15201     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15202     */
15203     /**
15204     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15205     */
15206     multiSort: false,
15207     /**
15208     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15209     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15210     */
15211     remoteSort : false,
15212
15213     /**
15214     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15215      * loaded or when a record is removed. (defaults to false).
15216     */
15217     pruneModifiedRecords : false,
15218
15219     // private
15220     lastOptions : null,
15221
15222     /**
15223      * Add Records to the Store and fires the add event.
15224      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15225      */
15226     add : function(records){
15227         records = [].concat(records);
15228         for(var i = 0, len = records.length; i < len; i++){
15229             records[i].join(this);
15230         }
15231         var index = this.data.length;
15232         this.data.addAll(records);
15233         this.fireEvent("add", this, records, index);
15234     },
15235
15236     /**
15237      * Remove a Record from the Store and fires the remove event.
15238      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15239      */
15240     remove : function(record){
15241         var index = this.data.indexOf(record);
15242         this.data.removeAt(index);
15243  
15244         if(this.pruneModifiedRecords){
15245             this.modified.remove(record);
15246         }
15247         this.fireEvent("remove", this, record, index);
15248     },
15249
15250     /**
15251      * Remove all Records from the Store and fires the clear event.
15252      */
15253     removeAll : function(){
15254         this.data.clear();
15255         if(this.pruneModifiedRecords){
15256             this.modified = [];
15257         }
15258         this.fireEvent("clear", this);
15259     },
15260
15261     /**
15262      * Inserts Records to the Store at the given index and fires the add event.
15263      * @param {Number} index The start index at which to insert the passed Records.
15264      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15265      */
15266     insert : function(index, records){
15267         records = [].concat(records);
15268         for(var i = 0, len = records.length; i < len; i++){
15269             this.data.insert(index, records[i]);
15270             records[i].join(this);
15271         }
15272         this.fireEvent("add", this, records, index);
15273     },
15274
15275     /**
15276      * Get the index within the cache of the passed Record.
15277      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15278      * @return {Number} The index of the passed Record. Returns -1 if not found.
15279      */
15280     indexOf : function(record){
15281         return this.data.indexOf(record);
15282     },
15283
15284     /**
15285      * Get the index within the cache of the Record with the passed id.
15286      * @param {String} id The id of the Record to find.
15287      * @return {Number} The index of the Record. Returns -1 if not found.
15288      */
15289     indexOfId : function(id){
15290         return this.data.indexOfKey(id);
15291     },
15292
15293     /**
15294      * Get the Record with the specified id.
15295      * @param {String} id The id of the Record to find.
15296      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15297      */
15298     getById : function(id){
15299         return this.data.key(id);
15300     },
15301
15302     /**
15303      * Get the Record at the specified index.
15304      * @param {Number} index The index of the Record to find.
15305      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15306      */
15307     getAt : function(index){
15308         return this.data.itemAt(index);
15309     },
15310
15311     /**
15312      * Returns a range of Records between specified indices.
15313      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15314      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15315      * @return {Roo.data.Record[]} An array of Records
15316      */
15317     getRange : function(start, end){
15318         return this.data.getRange(start, end);
15319     },
15320
15321     // private
15322     storeOptions : function(o){
15323         o = Roo.apply({}, o);
15324         delete o.callback;
15325         delete o.scope;
15326         this.lastOptions = o;
15327     },
15328
15329     /**
15330      * Loads the Record cache from the configured Proxy using the configured Reader.
15331      * <p>
15332      * If using remote paging, then the first load call must specify the <em>start</em>
15333      * and <em>limit</em> properties in the options.params property to establish the initial
15334      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15335      * <p>
15336      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15337      * and this call will return before the new data has been loaded. Perform any post-processing
15338      * in a callback function, or in a "load" event handler.</strong>
15339      * <p>
15340      * @param {Object} options An object containing properties which control loading options:<ul>
15341      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15342      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15343      * passed the following arguments:<ul>
15344      * <li>r : Roo.data.Record[]</li>
15345      * <li>options: Options object from the load call</li>
15346      * <li>success: Boolean success indicator</li></ul></li>
15347      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15348      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15349      * </ul>
15350      */
15351     load : function(options){
15352         options = options || {};
15353         if(this.fireEvent("beforeload", this, options) !== false){
15354             this.storeOptions(options);
15355             var p = Roo.apply(options.params || {}, this.baseParams);
15356             // if meta was not loaded from remote source.. try requesting it.
15357             if (!this.reader.metaFromRemote) {
15358                 p._requestMeta = 1;
15359             }
15360             if(this.sortInfo && this.remoteSort){
15361                 var pn = this.paramNames;
15362                 p[pn["sort"]] = this.sortInfo.field;
15363                 p[pn["dir"]] = this.sortInfo.direction;
15364             }
15365             if (this.multiSort) {
15366                 var pn = this.paramNames;
15367                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15368             }
15369             
15370             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15371         }
15372     },
15373
15374     /**
15375      * Reloads the Record cache from the configured Proxy using the configured Reader and
15376      * the options from the last load operation performed.
15377      * @param {Object} options (optional) An object containing properties which may override the options
15378      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15379      * the most recently used options are reused).
15380      */
15381     reload : function(options){
15382         this.load(Roo.applyIf(options||{}, this.lastOptions));
15383     },
15384
15385     // private
15386     // Called as a callback by the Reader during a load operation.
15387     loadRecords : function(o, options, success){
15388         if(!o || success === false){
15389             if(success !== false){
15390                 this.fireEvent("load", this, [], options, o);
15391             }
15392             if(options.callback){
15393                 options.callback.call(options.scope || this, [], options, false);
15394             }
15395             return;
15396         }
15397         // if data returned failure - throw an exception.
15398         if (o.success === false) {
15399             // show a message if no listener is registered.
15400             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15401                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15402             }
15403             // loadmask wil be hooked into this..
15404             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15405             return;
15406         }
15407         var r = o.records, t = o.totalRecords || r.length;
15408         
15409         this.fireEvent("beforeloadadd", this, r, options, o);
15410         
15411         if(!options || options.add !== true){
15412             if(this.pruneModifiedRecords){
15413                 this.modified = [];
15414             }
15415             for(var i = 0, len = r.length; i < len; i++){
15416                 r[i].join(this);
15417             }
15418             if(this.snapshot){
15419                 this.data = this.snapshot;
15420                 delete this.snapshot;
15421             }
15422             this.data.clear();
15423             this.data.addAll(r);
15424             this.totalLength = t;
15425             this.applySort();
15426             this.fireEvent("datachanged", this);
15427         }else{
15428             this.totalLength = Math.max(t, this.data.length+r.length);
15429             this.add(r);
15430         }
15431         
15432         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15433                 
15434             var e = new Roo.data.Record({});
15435
15436             e.set(this.parent.displayField, this.parent.emptyTitle);
15437             e.set(this.parent.valueField, '');
15438
15439             this.insert(0, e);
15440         }
15441             
15442         this.fireEvent("load", this, r, options, o);
15443         if(options.callback){
15444             options.callback.call(options.scope || this, r, options, true);
15445         }
15446     },
15447
15448
15449     /**
15450      * Loads data from a passed data block. A Reader which understands the format of the data
15451      * must have been configured in the constructor.
15452      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15453      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15454      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15455      */
15456     loadData : function(o, append){
15457         var r = this.reader.readRecords(o);
15458         this.loadRecords(r, {add: append}, true);
15459     },
15460     
15461      /**
15462      * using 'cn' the nested child reader read the child array into it's child stores.
15463      * @param {Object} rec The record with a 'children array
15464      */
15465     loadDataFromChildren : function(rec)
15466     {
15467         this.loadData(this.reader.toLoadData(rec));
15468     },
15469     
15470
15471     /**
15472      * Gets the number of cached records.
15473      * <p>
15474      * <em>If using paging, this may not be the total size of the dataset. If the data object
15475      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15476      * the data set size</em>
15477      */
15478     getCount : function(){
15479         return this.data.length || 0;
15480     },
15481
15482     /**
15483      * Gets the total number of records in the dataset as returned by the server.
15484      * <p>
15485      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15486      * the dataset size</em>
15487      */
15488     getTotalCount : function(){
15489         return this.totalLength || 0;
15490     },
15491
15492     /**
15493      * Returns the sort state of the Store as an object with two properties:
15494      * <pre><code>
15495  field {String} The name of the field by which the Records are sorted
15496  direction {String} The sort order, "ASC" or "DESC"
15497      * </code></pre>
15498      */
15499     getSortState : function(){
15500         return this.sortInfo;
15501     },
15502
15503     // private
15504     applySort : function(){
15505         if(this.sortInfo && !this.remoteSort){
15506             var s = this.sortInfo, f = s.field;
15507             var st = this.fields.get(f).sortType;
15508             var fn = function(r1, r2){
15509                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15510                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15511             };
15512             this.data.sort(s.direction, fn);
15513             if(this.snapshot && this.snapshot != this.data){
15514                 this.snapshot.sort(s.direction, fn);
15515             }
15516         }
15517     },
15518
15519     /**
15520      * Sets the default sort column and order to be used by the next load operation.
15521      * @param {String} fieldName The name of the field to sort by.
15522      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15523      */
15524     setDefaultSort : function(field, dir){
15525         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15526     },
15527
15528     /**
15529      * Sort the Records.
15530      * If remote sorting is used, the sort is performed on the server, and the cache is
15531      * reloaded. If local sorting is used, the cache is sorted internally.
15532      * @param {String} fieldName The name of the field to sort by.
15533      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15534      */
15535     sort : function(fieldName, dir){
15536         var f = this.fields.get(fieldName);
15537         if(!dir){
15538             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15539             
15540             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15541                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15542             }else{
15543                 dir = f.sortDir;
15544             }
15545         }
15546         this.sortToggle[f.name] = dir;
15547         this.sortInfo = {field: f.name, direction: dir};
15548         if(!this.remoteSort){
15549             this.applySort();
15550             this.fireEvent("datachanged", this);
15551         }else{
15552             this.load(this.lastOptions);
15553         }
15554     },
15555
15556     /**
15557      * Calls the specified function for each of the Records in the cache.
15558      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15559      * Returning <em>false</em> aborts and exits the iteration.
15560      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15561      */
15562     each : function(fn, scope){
15563         this.data.each(fn, scope);
15564     },
15565
15566     /**
15567      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15568      * (e.g., during paging).
15569      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15570      */
15571     getModifiedRecords : function(){
15572         return this.modified;
15573     },
15574
15575     // private
15576     createFilterFn : function(property, value, anyMatch){
15577         if(!value.exec){ // not a regex
15578             value = String(value);
15579             if(value.length == 0){
15580                 return false;
15581             }
15582             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15583         }
15584         return function(r){
15585             return value.test(r.data[property]);
15586         };
15587     },
15588
15589     /**
15590      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15591      * @param {String} property A field on your records
15592      * @param {Number} start The record index to start at (defaults to 0)
15593      * @param {Number} end The last record index to include (defaults to length - 1)
15594      * @return {Number} The sum
15595      */
15596     sum : function(property, start, end){
15597         var rs = this.data.items, v = 0;
15598         start = start || 0;
15599         end = (end || end === 0) ? end : rs.length-1;
15600
15601         for(var i = start; i <= end; i++){
15602             v += (rs[i].data[property] || 0);
15603         }
15604         return v;
15605     },
15606
15607     /**
15608      * Filter the records by a specified property.
15609      * @param {String} field A field on your records
15610      * @param {String/RegExp} value Either a string that the field
15611      * should start with or a RegExp to test against the field
15612      * @param {Boolean} anyMatch True to match any part not just the beginning
15613      */
15614     filter : function(property, value, anyMatch){
15615         var fn = this.createFilterFn(property, value, anyMatch);
15616         return fn ? this.filterBy(fn) : this.clearFilter();
15617     },
15618
15619     /**
15620      * Filter by a function. The specified function will be called with each
15621      * record in this data source. If the function returns true the record is included,
15622      * otherwise it is filtered.
15623      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15624      * @param {Object} scope (optional) The scope of the function (defaults to this)
15625      */
15626     filterBy : function(fn, scope){
15627         this.snapshot = this.snapshot || this.data;
15628         this.data = this.queryBy(fn, scope||this);
15629         this.fireEvent("datachanged", this);
15630     },
15631
15632     /**
15633      * Query the records by a specified property.
15634      * @param {String} field A field on your records
15635      * @param {String/RegExp} value Either a string that the field
15636      * should start with or a RegExp to test against the field
15637      * @param {Boolean} anyMatch True to match any part not just the beginning
15638      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15639      */
15640     query : function(property, value, anyMatch){
15641         var fn = this.createFilterFn(property, value, anyMatch);
15642         return fn ? this.queryBy(fn) : this.data.clone();
15643     },
15644
15645     /**
15646      * Query by a function. The specified function will be called with each
15647      * record in this data source. If the function returns true the record is included
15648      * in the results.
15649      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15650      * @param {Object} scope (optional) The scope of the function (defaults to this)
15651       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15652      **/
15653     queryBy : function(fn, scope){
15654         var data = this.snapshot || this.data;
15655         return data.filterBy(fn, scope||this);
15656     },
15657
15658     /**
15659      * Collects unique values for a particular dataIndex from this store.
15660      * @param {String} dataIndex The property to collect
15661      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15662      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15663      * @return {Array} An array of the unique values
15664      **/
15665     collect : function(dataIndex, allowNull, bypassFilter){
15666         var d = (bypassFilter === true && this.snapshot) ?
15667                 this.snapshot.items : this.data.items;
15668         var v, sv, r = [], l = {};
15669         for(var i = 0, len = d.length; i < len; i++){
15670             v = d[i].data[dataIndex];
15671             sv = String(v);
15672             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15673                 l[sv] = true;
15674                 r[r.length] = v;
15675             }
15676         }
15677         return r;
15678     },
15679
15680     /**
15681      * Revert to a view of the Record cache with no filtering applied.
15682      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15683      */
15684     clearFilter : function(suppressEvent){
15685         if(this.snapshot && this.snapshot != this.data){
15686             this.data = this.snapshot;
15687             delete this.snapshot;
15688             if(suppressEvent !== true){
15689                 this.fireEvent("datachanged", this);
15690             }
15691         }
15692     },
15693
15694     // private
15695     afterEdit : function(record){
15696         if(this.modified.indexOf(record) == -1){
15697             this.modified.push(record);
15698         }
15699         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15700     },
15701     
15702     // private
15703     afterReject : function(record){
15704         this.modified.remove(record);
15705         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15706     },
15707
15708     // private
15709     afterCommit : function(record){
15710         this.modified.remove(record);
15711         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15712     },
15713
15714     /**
15715      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15716      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15717      */
15718     commitChanges : function(){
15719         var m = this.modified.slice(0);
15720         this.modified = [];
15721         for(var i = 0, len = m.length; i < len; i++){
15722             m[i].commit();
15723         }
15724     },
15725
15726     /**
15727      * Cancel outstanding changes on all changed records.
15728      */
15729     rejectChanges : function(){
15730         var m = this.modified.slice(0);
15731         this.modified = [];
15732         for(var i = 0, len = m.length; i < len; i++){
15733             m[i].reject();
15734         }
15735     },
15736
15737     onMetaChange : function(meta, rtype, o){
15738         this.recordType = rtype;
15739         this.fields = rtype.prototype.fields;
15740         delete this.snapshot;
15741         this.sortInfo = meta.sortInfo || this.sortInfo;
15742         this.modified = [];
15743         this.fireEvent('metachange', this, this.reader.meta);
15744     },
15745     
15746     moveIndex : function(data, type)
15747     {
15748         var index = this.indexOf(data);
15749         
15750         var newIndex = index + type;
15751         
15752         this.remove(data);
15753         
15754         this.insert(newIndex, data);
15755         
15756     }
15757 });/*
15758  * Based on:
15759  * Ext JS Library 1.1.1
15760  * Copyright(c) 2006-2007, Ext JS, LLC.
15761  *
15762  * Originally Released Under LGPL - original licence link has changed is not relivant.
15763  *
15764  * Fork - LGPL
15765  * <script type="text/javascript">
15766  */
15767
15768 /**
15769  * @class Roo.data.SimpleStore
15770  * @extends Roo.data.Store
15771  * Small helper class to make creating Stores from Array data easier.
15772  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15773  * @cfg {Array} fields An array of field definition objects, or field name strings.
15774  * @cfg {Object} an existing reader (eg. copied from another store)
15775  * @cfg {Array} data The multi-dimensional array of data
15776  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15777  * @cfg {Roo.data.Reader} reader  [not-required] 
15778  * @constructor
15779  * @param {Object} config
15780  */
15781 Roo.data.SimpleStore = function(config)
15782 {
15783     Roo.data.SimpleStore.superclass.constructor.call(this, {
15784         isLocal : true,
15785         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15786                 id: config.id
15787             },
15788             Roo.data.Record.create(config.fields)
15789         ),
15790         proxy : new Roo.data.MemoryProxy(config.data)
15791     });
15792     this.load();
15793 };
15794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806 /**
15807  * @extends Roo.data.Store
15808  * @class Roo.data.JsonStore
15809  * Small helper class to make creating Stores for JSON data easier. <br/>
15810 <pre><code>
15811 var store = new Roo.data.JsonStore({
15812     url: 'get-images.php',
15813     root: 'images',
15814     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15815 });
15816 </code></pre>
15817  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15818  * JsonReader and HttpProxy (unless inline data is provided).</b>
15819  * @cfg {Array} fields An array of field definition objects, or field name strings.
15820  * @constructor
15821  * @param {Object} config
15822  */
15823 Roo.data.JsonStore = function(c){
15824     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15825         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15826         reader: new Roo.data.JsonReader(c, c.fields)
15827     }));
15828 };
15829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15830  * Based on:
15831  * Ext JS Library 1.1.1
15832  * Copyright(c) 2006-2007, Ext JS, LLC.
15833  *
15834  * Originally Released Under LGPL - original licence link has changed is not relivant.
15835  *
15836  * Fork - LGPL
15837  * <script type="text/javascript">
15838  */
15839
15840  
15841 Roo.data.Field = function(config){
15842     if(typeof config == "string"){
15843         config = {name: config};
15844     }
15845     Roo.apply(this, config);
15846     
15847     if(!this.type){
15848         this.type = "auto";
15849     }
15850     
15851     var st = Roo.data.SortTypes;
15852     // named sortTypes are supported, here we look them up
15853     if(typeof this.sortType == "string"){
15854         this.sortType = st[this.sortType];
15855     }
15856     
15857     // set default sortType for strings and dates
15858     if(!this.sortType){
15859         switch(this.type){
15860             case "string":
15861                 this.sortType = st.asUCString;
15862                 break;
15863             case "date":
15864                 this.sortType = st.asDate;
15865                 break;
15866             default:
15867                 this.sortType = st.none;
15868         }
15869     }
15870
15871     // define once
15872     var stripRe = /[\$,%]/g;
15873
15874     // prebuilt conversion function for this field, instead of
15875     // switching every time we're reading a value
15876     if(!this.convert){
15877         var cv, dateFormat = this.dateFormat;
15878         switch(this.type){
15879             case "":
15880             case "auto":
15881             case undefined:
15882                 cv = function(v){ return v; };
15883                 break;
15884             case "string":
15885                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15886                 break;
15887             case "int":
15888                 cv = function(v){
15889                     return v !== undefined && v !== null && v !== '' ?
15890                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15891                     };
15892                 break;
15893             case "float":
15894                 cv = function(v){
15895                     return v !== undefined && v !== null && v !== '' ?
15896                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15897                     };
15898                 break;
15899             case "bool":
15900             case "boolean":
15901                 cv = function(v){ return v === true || v === "true" || v == 1; };
15902                 break;
15903             case "date":
15904                 cv = function(v){
15905                     if(!v){
15906                         return '';
15907                     }
15908                     if(v instanceof Date){
15909                         return v;
15910                     }
15911                     if(dateFormat){
15912                         if(dateFormat == "timestamp"){
15913                             return new Date(v*1000);
15914                         }
15915                         return Date.parseDate(v, dateFormat);
15916                     }
15917                     var parsed = Date.parse(v);
15918                     return parsed ? new Date(parsed) : null;
15919                 };
15920              break;
15921             
15922         }
15923         this.convert = cv;
15924     }
15925 };
15926
15927 Roo.data.Field.prototype = {
15928     dateFormat: null,
15929     defaultValue: "",
15930     mapping: null,
15931     sortType : null,
15932     sortDir : "ASC"
15933 };/*
15934  * Based on:
15935  * Ext JS Library 1.1.1
15936  * Copyright(c) 2006-2007, Ext JS, LLC.
15937  *
15938  * Originally Released Under LGPL - original licence link has changed is not relivant.
15939  *
15940  * Fork - LGPL
15941  * <script type="text/javascript">
15942  */
15943  
15944 // Base class for reading structured data from a data source.  This class is intended to be
15945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15946
15947 /**
15948  * @class Roo.data.DataReader
15949  * @abstract
15950  * Base class for reading structured data from a data source.  This class is intended to be
15951  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15952  */
15953
15954 Roo.data.DataReader = function(meta, recordType){
15955     
15956     this.meta = meta;
15957     
15958     this.recordType = recordType instanceof Array ? 
15959         Roo.data.Record.create(recordType) : recordType;
15960 };
15961
15962 Roo.data.DataReader.prototype = {
15963     
15964     
15965     readerType : 'Data',
15966      /**
15967      * Create an empty record
15968      * @param {Object} data (optional) - overlay some values
15969      * @return {Roo.data.Record} record created.
15970      */
15971     newRow :  function(d) {
15972         var da =  {};
15973         this.recordType.prototype.fields.each(function(c) {
15974             switch( c.type) {
15975                 case 'int' : da[c.name] = 0; break;
15976                 case 'date' : da[c.name] = new Date(); break;
15977                 case 'float' : da[c.name] = 0.0; break;
15978                 case 'boolean' : da[c.name] = false; break;
15979                 default : da[c.name] = ""; break;
15980             }
15981             
15982         });
15983         return new this.recordType(Roo.apply(da, d));
15984     }
15985     
15986     
15987 };/*
15988  * Based on:
15989  * Ext JS Library 1.1.1
15990  * Copyright(c) 2006-2007, Ext JS, LLC.
15991  *
15992  * Originally Released Under LGPL - original licence link has changed is not relivant.
15993  *
15994  * Fork - LGPL
15995  * <script type="text/javascript">
15996  */
15997
15998 /**
15999  * @class Roo.data.DataProxy
16000  * @extends Roo.util.Observable
16001  * @abstract
16002  * This class is an abstract base class for implementations which provide retrieval of
16003  * unformatted data objects.<br>
16004  * <p>
16005  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16006  * (of the appropriate type which knows how to parse the data object) to provide a block of
16007  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16008  * <p>
16009  * Custom implementations must implement the load method as described in
16010  * {@link Roo.data.HttpProxy#load}.
16011  */
16012 Roo.data.DataProxy = function(){
16013     this.addEvents({
16014         /**
16015          * @event beforeload
16016          * Fires before a network request is made to retrieve a data object.
16017          * @param {Object} This DataProxy object.
16018          * @param {Object} params The params parameter to the load function.
16019          */
16020         beforeload : true,
16021         /**
16022          * @event load
16023          * Fires before the load method's callback is called.
16024          * @param {Object} This DataProxy object.
16025          * @param {Object} o The data object.
16026          * @param {Object} arg The callback argument object passed to the load function.
16027          */
16028         load : true,
16029         /**
16030          * @event loadexception
16031          * Fires if an Exception occurs during data retrieval.
16032          * @param {Object} This DataProxy object.
16033          * @param {Object} o The data object.
16034          * @param {Object} arg The callback argument object passed to the load function.
16035          * @param {Object} e The Exception.
16036          */
16037         loadexception : true
16038     });
16039     Roo.data.DataProxy.superclass.constructor.call(this);
16040 };
16041
16042 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16043
16044     /**
16045      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16046      */
16047 /*
16048  * Based on:
16049  * Ext JS Library 1.1.1
16050  * Copyright(c) 2006-2007, Ext JS, LLC.
16051  *
16052  * Originally Released Under LGPL - original licence link has changed is not relivant.
16053  *
16054  * Fork - LGPL
16055  * <script type="text/javascript">
16056  */
16057 /**
16058  * @class Roo.data.MemoryProxy
16059  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16060  * to the Reader when its load method is called.
16061  * @constructor
16062  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16063  */
16064 Roo.data.MemoryProxy = function(data){
16065     if (data.data) {
16066         data = data.data;
16067     }
16068     Roo.data.MemoryProxy.superclass.constructor.call(this);
16069     this.data = data;
16070 };
16071
16072 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16073     
16074     /**
16075      * Load data from the requested source (in this case an in-memory
16076      * data object passed to the constructor), read the data object into
16077      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16078      * process that block using the passed callback.
16079      * @param {Object} params This parameter is not used by the MemoryProxy class.
16080      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16081      * object into a block of Roo.data.Records.
16082      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16083      * The function must be passed <ul>
16084      * <li>The Record block object</li>
16085      * <li>The "arg" argument from the load function</li>
16086      * <li>A boolean success indicator</li>
16087      * </ul>
16088      * @param {Object} scope The scope in which to call the callback
16089      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16090      */
16091     load : function(params, reader, callback, scope, arg){
16092         params = params || {};
16093         var result;
16094         try {
16095             result = reader.readRecords(params.data ? params.data :this.data);
16096         }catch(e){
16097             this.fireEvent("loadexception", this, arg, null, e);
16098             callback.call(scope, null, arg, false);
16099             return;
16100         }
16101         callback.call(scope, result, arg, true);
16102     },
16103     
16104     // private
16105     update : function(params, records){
16106         
16107     }
16108 });/*
16109  * Based on:
16110  * Ext JS Library 1.1.1
16111  * Copyright(c) 2006-2007, Ext JS, LLC.
16112  *
16113  * Originally Released Under LGPL - original licence link has changed is not relivant.
16114  *
16115  * Fork - LGPL
16116  * <script type="text/javascript">
16117  */
16118 /**
16119  * @class Roo.data.HttpProxy
16120  * @extends Roo.data.DataProxy
16121  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16122  * configured to reference a certain URL.<br><br>
16123  * <p>
16124  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16125  * from which the running page was served.<br><br>
16126  * <p>
16127  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16128  * <p>
16129  * Be aware that to enable the browser to parse an XML document, the server must set
16130  * the Content-Type header in the HTTP response to "text/xml".
16131  * @constructor
16132  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16133  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16134  * will be used to make the request.
16135  */
16136 Roo.data.HttpProxy = function(conn){
16137     Roo.data.HttpProxy.superclass.constructor.call(this);
16138     // is conn a conn config or a real conn?
16139     this.conn = conn;
16140     this.useAjax = !conn || !conn.events;
16141   
16142 };
16143
16144 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16145     // thse are take from connection...
16146     
16147     /**
16148      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16149      */
16150     /**
16151      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16152      * extra parameters to each request made by this object. (defaults to undefined)
16153      */
16154     /**
16155      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16156      *  to each request made by this object. (defaults to undefined)
16157      */
16158     /**
16159      * @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)
16160      */
16161     /**
16162      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16163      */
16164      /**
16165      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16166      * @type Boolean
16167      */
16168   
16169
16170     /**
16171      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16172      * @type Boolean
16173      */
16174     /**
16175      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16176      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16177      * a finer-grained basis than the DataProxy events.
16178      */
16179     getConnection : function(){
16180         return this.useAjax ? Roo.Ajax : this.conn;
16181     },
16182
16183     /**
16184      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16185      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16186      * process that block using the passed callback.
16187      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16188      * for the request to the remote server.
16189      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16190      * object into a block of Roo.data.Records.
16191      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16192      * The function must be passed <ul>
16193      * <li>The Record block object</li>
16194      * <li>The "arg" argument from the load function</li>
16195      * <li>A boolean success indicator</li>
16196      * </ul>
16197      * @param {Object} scope The scope in which to call the callback
16198      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16199      */
16200     load : function(params, reader, callback, scope, arg){
16201         if(this.fireEvent("beforeload", this, params) !== false){
16202             var  o = {
16203                 params : params || {},
16204                 request: {
16205                     callback : callback,
16206                     scope : scope,
16207                     arg : arg
16208                 },
16209                 reader: reader,
16210                 callback : this.loadResponse,
16211                 scope: this
16212             };
16213             if(this.useAjax){
16214                 Roo.applyIf(o, this.conn);
16215                 if(this.activeRequest){
16216                     Roo.Ajax.abort(this.activeRequest);
16217                 }
16218                 this.activeRequest = Roo.Ajax.request(o);
16219             }else{
16220                 this.conn.request(o);
16221             }
16222         }else{
16223             callback.call(scope||this, null, arg, false);
16224         }
16225     },
16226
16227     // private
16228     loadResponse : function(o, success, response){
16229         delete this.activeRequest;
16230         if(!success){
16231             this.fireEvent("loadexception", this, o, response);
16232             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16233             return;
16234         }
16235         var result;
16236         try {
16237             result = o.reader.read(response);
16238         }catch(e){
16239             this.fireEvent("loadexception", this, o, response, e);
16240             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16241             return;
16242         }
16243         
16244         this.fireEvent("load", this, o, o.request.arg);
16245         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16246     },
16247
16248     // private
16249     update : function(dataSet){
16250
16251     },
16252
16253     // private
16254     updateResponse : function(dataSet){
16255
16256     }
16257 });/*
16258  * Based on:
16259  * Ext JS Library 1.1.1
16260  * Copyright(c) 2006-2007, Ext JS, LLC.
16261  *
16262  * Originally Released Under LGPL - original licence link has changed is not relivant.
16263  *
16264  * Fork - LGPL
16265  * <script type="text/javascript">
16266  */
16267
16268 /**
16269  * @class Roo.data.ScriptTagProxy
16270  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16271  * other than the originating domain of the running page.<br><br>
16272  * <p>
16273  * <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
16274  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16275  * <p>
16276  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16277  * source code that is used as the source inside a &lt;script> tag.<br><br>
16278  * <p>
16279  * In order for the browser to process the returned data, the server must wrap the data object
16280  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16281  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16282  * depending on whether the callback name was passed:
16283  * <p>
16284  * <pre><code>
16285 boolean scriptTag = false;
16286 String cb = request.getParameter("callback");
16287 if (cb != null) {
16288     scriptTag = true;
16289     response.setContentType("text/javascript");
16290 } else {
16291     response.setContentType("application/x-json");
16292 }
16293 Writer out = response.getWriter();
16294 if (scriptTag) {
16295     out.write(cb + "(");
16296 }
16297 out.print(dataBlock.toJsonString());
16298 if (scriptTag) {
16299     out.write(");");
16300 }
16301 </pre></code>
16302  *
16303  * @constructor
16304  * @param {Object} config A configuration object.
16305  */
16306 Roo.data.ScriptTagProxy = function(config){
16307     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16308     Roo.apply(this, config);
16309     this.head = document.getElementsByTagName("head")[0];
16310 };
16311
16312 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16313
16314 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16315     /**
16316      * @cfg {String} url The URL from which to request the data object.
16317      */
16318     /**
16319      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16320      */
16321     timeout : 30000,
16322     /**
16323      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16324      * the server the name of the callback function set up by the load call to process the returned data object.
16325      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16326      * javascript output which calls this named function passing the data object as its only parameter.
16327      */
16328     callbackParam : "callback",
16329     /**
16330      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16331      * name to the request.
16332      */
16333     nocache : true,
16334
16335     /**
16336      * Load data from the configured URL, read the data object into
16337      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16338      * process that block using the passed callback.
16339      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16340      * for the request to the remote server.
16341      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16342      * object into a block of Roo.data.Records.
16343      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16344      * The function must be passed <ul>
16345      * <li>The Record block object</li>
16346      * <li>The "arg" argument from the load function</li>
16347      * <li>A boolean success indicator</li>
16348      * </ul>
16349      * @param {Object} scope The scope in which to call the callback
16350      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16351      */
16352     load : function(params, reader, callback, scope, arg){
16353         if(this.fireEvent("beforeload", this, params) !== false){
16354
16355             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16356
16357             var url = this.url;
16358             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16359             if(this.nocache){
16360                 url += "&_dc=" + (new Date().getTime());
16361             }
16362             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16363             var trans = {
16364                 id : transId,
16365                 cb : "stcCallback"+transId,
16366                 scriptId : "stcScript"+transId,
16367                 params : params,
16368                 arg : arg,
16369                 url : url,
16370                 callback : callback,
16371                 scope : scope,
16372                 reader : reader
16373             };
16374             var conn = this;
16375
16376             window[trans.cb] = function(o){
16377                 conn.handleResponse(o, trans);
16378             };
16379
16380             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16381
16382             if(this.autoAbort !== false){
16383                 this.abort();
16384             }
16385
16386             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16387
16388             var script = document.createElement("script");
16389             script.setAttribute("src", url);
16390             script.setAttribute("type", "text/javascript");
16391             script.setAttribute("id", trans.scriptId);
16392             this.head.appendChild(script);
16393
16394             this.trans = trans;
16395         }else{
16396             callback.call(scope||this, null, arg, false);
16397         }
16398     },
16399
16400     // private
16401     isLoading : function(){
16402         return this.trans ? true : false;
16403     },
16404
16405     /**
16406      * Abort the current server request.
16407      */
16408     abort : function(){
16409         if(this.isLoading()){
16410             this.destroyTrans(this.trans);
16411         }
16412     },
16413
16414     // private
16415     destroyTrans : function(trans, isLoaded){
16416         this.head.removeChild(document.getElementById(trans.scriptId));
16417         clearTimeout(trans.timeoutId);
16418         if(isLoaded){
16419             window[trans.cb] = undefined;
16420             try{
16421                 delete window[trans.cb];
16422             }catch(e){}
16423         }else{
16424             // if hasn't been loaded, wait for load to remove it to prevent script error
16425             window[trans.cb] = function(){
16426                 window[trans.cb] = undefined;
16427                 try{
16428                     delete window[trans.cb];
16429                 }catch(e){}
16430             };
16431         }
16432     },
16433
16434     // private
16435     handleResponse : function(o, trans){
16436         this.trans = false;
16437         this.destroyTrans(trans, true);
16438         var result;
16439         try {
16440             result = trans.reader.readRecords(o);
16441         }catch(e){
16442             this.fireEvent("loadexception", this, o, trans.arg, e);
16443             trans.callback.call(trans.scope||window, null, trans.arg, false);
16444             return;
16445         }
16446         this.fireEvent("load", this, o, trans.arg);
16447         trans.callback.call(trans.scope||window, result, trans.arg, true);
16448     },
16449
16450     // private
16451     handleFailure : function(trans){
16452         this.trans = false;
16453         this.destroyTrans(trans, false);
16454         this.fireEvent("loadexception", this, null, trans.arg);
16455         trans.callback.call(trans.scope||window, null, trans.arg, false);
16456     }
16457 });/*
16458  * Based on:
16459  * Ext JS Library 1.1.1
16460  * Copyright(c) 2006-2007, Ext JS, LLC.
16461  *
16462  * Originally Released Under LGPL - original licence link has changed is not relivant.
16463  *
16464  * Fork - LGPL
16465  * <script type="text/javascript">
16466  */
16467
16468 /**
16469  * @class Roo.data.JsonReader
16470  * @extends Roo.data.DataReader
16471  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16472  * based on mappings in a provided Roo.data.Record constructor.
16473  * 
16474  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16475  * in the reply previously. 
16476  * 
16477  * <p>
16478  * Example code:
16479  * <pre><code>
16480 var RecordDef = Roo.data.Record.create([
16481     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16482     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16483 ]);
16484 var myReader = new Roo.data.JsonReader({
16485     totalProperty: "results",    // The property which contains the total dataset size (optional)
16486     root: "rows",                // The property which contains an Array of row objects
16487     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16488 }, RecordDef);
16489 </code></pre>
16490  * <p>
16491  * This would consume a JSON file like this:
16492  * <pre><code>
16493 { 'results': 2, 'rows': [
16494     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16495     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16496 }
16497 </code></pre>
16498  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16499  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16500  * paged from the remote server.
16501  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16502  * @cfg {String} root name of the property which contains the Array of row objects.
16503  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16504  * @cfg {Array} fields Array of field definition objects
16505  * @constructor
16506  * Create a new JsonReader
16507  * @param {Object} meta Metadata configuration options
16508  * @param {Object} recordType Either an Array of field definition objects,
16509  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16510  */
16511 Roo.data.JsonReader = function(meta, recordType){
16512     
16513     meta = meta || {};
16514     // set some defaults:
16515     Roo.applyIf(meta, {
16516         totalProperty: 'total',
16517         successProperty : 'success',
16518         root : 'data',
16519         id : 'id'
16520     });
16521     
16522     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16523 };
16524 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16525     
16526     readerType : 'Json',
16527     
16528     /**
16529      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16530      * Used by Store query builder to append _requestMeta to params.
16531      * 
16532      */
16533     metaFromRemote : false,
16534     /**
16535      * This method is only used by a DataProxy which has retrieved data from a remote server.
16536      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16537      * @return {Object} data A data block which is used by an Roo.data.Store object as
16538      * a cache of Roo.data.Records.
16539      */
16540     read : function(response){
16541         var json = response.responseText;
16542        
16543         var o = /* eval:var:o */ eval("("+json+")");
16544         if(!o) {
16545             throw {message: "JsonReader.read: Json object not found"};
16546         }
16547         
16548         if(o.metaData){
16549             
16550             delete this.ef;
16551             this.metaFromRemote = true;
16552             this.meta = o.metaData;
16553             this.recordType = Roo.data.Record.create(o.metaData.fields);
16554             this.onMetaChange(this.meta, this.recordType, o);
16555         }
16556         return this.readRecords(o);
16557     },
16558
16559     // private function a store will implement
16560     onMetaChange : function(meta, recordType, o){
16561
16562     },
16563
16564     /**
16565          * @ignore
16566          */
16567     simpleAccess: function(obj, subsc) {
16568         return obj[subsc];
16569     },
16570
16571         /**
16572          * @ignore
16573          */
16574     getJsonAccessor: function(){
16575         var re = /[\[\.]/;
16576         return function(expr) {
16577             try {
16578                 return(re.test(expr))
16579                     ? new Function("obj", "return obj." + expr)
16580                     : function(obj){
16581                         return obj[expr];
16582                     };
16583             } catch(e){}
16584             return Roo.emptyFn;
16585         };
16586     }(),
16587
16588     /**
16589      * Create a data block containing Roo.data.Records from an XML document.
16590      * @param {Object} o An object which contains an Array of row objects in the property specified
16591      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16592      * which contains the total size of the dataset.
16593      * @return {Object} data A data block which is used by an Roo.data.Store object as
16594      * a cache of Roo.data.Records.
16595      */
16596     readRecords : function(o){
16597         /**
16598          * After any data loads, the raw JSON data is available for further custom processing.
16599          * @type Object
16600          */
16601         this.o = o;
16602         var s = this.meta, Record = this.recordType,
16603             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16604
16605 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16606         if (!this.ef) {
16607             if(s.totalProperty) {
16608                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16609                 }
16610                 if(s.successProperty) {
16611                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16612                 }
16613                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16614                 if (s.id) {
16615                         var g = this.getJsonAccessor(s.id);
16616                         this.getId = function(rec) {
16617                                 var r = g(rec);  
16618                                 return (r === undefined || r === "") ? null : r;
16619                         };
16620                 } else {
16621                         this.getId = function(){return null;};
16622                 }
16623             this.ef = [];
16624             for(var jj = 0; jj < fl; jj++){
16625                 f = fi[jj];
16626                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16627                 this.ef[jj] = this.getJsonAccessor(map);
16628             }
16629         }
16630
16631         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16632         if(s.totalProperty){
16633             var vt = parseInt(this.getTotal(o), 10);
16634             if(!isNaN(vt)){
16635                 totalRecords = vt;
16636             }
16637         }
16638         if(s.successProperty){
16639             var vs = this.getSuccess(o);
16640             if(vs === false || vs === 'false'){
16641                 success = false;
16642             }
16643         }
16644         var records = [];
16645         for(var i = 0; i < c; i++){
16646                 var n = root[i];
16647             var values = {};
16648             var id = this.getId(n);
16649             for(var j = 0; j < fl; j++){
16650                 f = fi[j];
16651             var v = this.ef[j](n);
16652             if (!f.convert) {
16653                 Roo.log('missing convert for ' + f.name);
16654                 Roo.log(f);
16655                 continue;
16656             }
16657             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16658             }
16659             var record = new Record(values, id);
16660             record.json = n;
16661             records[i] = record;
16662         }
16663         return {
16664             raw : o,
16665             success : success,
16666             records : records,
16667             totalRecords : totalRecords
16668         };
16669     },
16670     // used when loading children.. @see loadDataFromChildren
16671     toLoadData: function(rec)
16672     {
16673         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16674         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16675         return { data : data, total : data.length };
16676         
16677     }
16678 });/*
16679  * Based on:
16680  * Ext JS Library 1.1.1
16681  * Copyright(c) 2006-2007, Ext JS, LLC.
16682  *
16683  * Originally Released Under LGPL - original licence link has changed is not relivant.
16684  *
16685  * Fork - LGPL
16686  * <script type="text/javascript">
16687  */
16688
16689 /**
16690  * @class Roo.data.ArrayReader
16691  * @extends Roo.data.DataReader
16692  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16693  * Each element of that Array represents a row of data fields. The
16694  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16695  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16696  * <p>
16697  * Example code:.
16698  * <pre><code>
16699 var RecordDef = Roo.data.Record.create([
16700     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16701     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16702 ]);
16703 var myReader = new Roo.data.ArrayReader({
16704     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16705 }, RecordDef);
16706 </code></pre>
16707  * <p>
16708  * This would consume an Array like this:
16709  * <pre><code>
16710 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16711   </code></pre>
16712  
16713  * @constructor
16714  * Create a new JsonReader
16715  * @param {Object} meta Metadata configuration options.
16716  * @param {Object|Array} recordType Either an Array of field definition objects
16717  * 
16718  * @cfg {Array} fields Array of field definition objects
16719  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16720  * as specified to {@link Roo.data.Record#create},
16721  * or an {@link Roo.data.Record} object
16722  *
16723  * 
16724  * created using {@link Roo.data.Record#create}.
16725  */
16726 Roo.data.ArrayReader = function(meta, recordType)
16727 {    
16728     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16729 };
16730
16731 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16732     
16733       /**
16734      * Create a data block containing Roo.data.Records from an XML document.
16735      * @param {Object} o An Array of row objects which represents the dataset.
16736      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16737      * a cache of Roo.data.Records.
16738      */
16739     readRecords : function(o)
16740     {
16741         var sid = this.meta ? this.meta.id : null;
16742         var recordType = this.recordType, fields = recordType.prototype.fields;
16743         var records = [];
16744         var root = o;
16745         for(var i = 0; i < root.length; i++){
16746             var n = root[i];
16747             var values = {};
16748             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16749             for(var j = 0, jlen = fields.length; j < jlen; j++){
16750                 var f = fields.items[j];
16751                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16752                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16753                 v = f.convert(v);
16754                 values[f.name] = v;
16755             }
16756             var record = new recordType(values, id);
16757             record.json = n;
16758             records[records.length] = record;
16759         }
16760         return {
16761             records : records,
16762             totalRecords : records.length
16763         };
16764     },
16765     // used when loading children.. @see loadDataFromChildren
16766     toLoadData: function(rec)
16767     {
16768         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16769         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16770         
16771     }
16772     
16773     
16774 });/*
16775  * - LGPL
16776  * * 
16777  */
16778
16779 /**
16780  * @class Roo.bootstrap.form.ComboBox
16781  * @extends Roo.bootstrap.form.TriggerField
16782  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16783  * @cfg {Boolean} append (true|false) default false
16784  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16785  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16786  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16787  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16788  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16789  * @cfg {Boolean} animate default true
16790  * @cfg {Boolean} emptyResultText only for touch device
16791  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16792  * @cfg {String} emptyTitle default ''
16793  * @cfg {Number} width fixed with? experimental
16794  * @constructor
16795  * Create a new ComboBox.
16796  * @param {Object} config Configuration options
16797  */
16798 Roo.bootstrap.form.ComboBox = function(config){
16799     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16800     this.addEvents({
16801         /**
16802          * @event expand
16803          * Fires when the dropdown list is expanded
16804         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16805         */
16806         'expand' : true,
16807         /**
16808          * @event collapse
16809          * Fires when the dropdown list is collapsed
16810         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16811         */
16812         'collapse' : true,
16813         /**
16814          * @event beforeselect
16815          * Fires before a list item is selected. Return false to cancel the selection.
16816         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16817         * @param {Roo.data.Record} record The data record returned from the underlying store
16818         * @param {Number} index The index of the selected item in the dropdown list
16819         */
16820         'beforeselect' : true,
16821         /**
16822          * @event select
16823          * Fires when a list item is selected
16824         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16825         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16826         * @param {Number} index The index of the selected item in the dropdown list
16827         */
16828         'select' : true,
16829         /**
16830          * @event beforequery
16831          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16832          * The event object passed has these properties:
16833         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16834         * @param {String} query The query
16835         * @param {Boolean} forceAll true to force "all" query
16836         * @param {Boolean} cancel true to cancel the query
16837         * @param {Object} e The query event object
16838         */
16839         'beforequery': true,
16840          /**
16841          * @event add
16842          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16843         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16844         */
16845         'add' : true,
16846         /**
16847          * @event edit
16848          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16849         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16850         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16851         */
16852         'edit' : true,
16853         /**
16854          * @event remove
16855          * Fires when the remove value from the combobox array
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'remove' : true,
16859         /**
16860          * @event afterremove
16861          * Fires when the remove value from the combobox array
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'afterremove' : true,
16865         /**
16866          * @event specialfilter
16867          * Fires when specialfilter
16868             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869             */
16870         'specialfilter' : true,
16871         /**
16872          * @event tick
16873          * Fires when tick the element
16874             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875             */
16876         'tick' : true,
16877         /**
16878          * @event touchviewdisplay
16879          * Fires when touch view require special display (default is using displayField)
16880             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881             * @param {Object} cfg set html .
16882             */
16883         'touchviewdisplay' : true
16884         
16885     });
16886     
16887     this.item = [];
16888     this.tickItems = [];
16889     
16890     this.selectedIndex = -1;
16891     if(this.mode == 'local'){
16892         if(config.queryDelay === undefined){
16893             this.queryDelay = 10;
16894         }
16895         if(config.minChars === undefined){
16896             this.minChars = 0;
16897         }
16898     }
16899 };
16900
16901 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16902      
16903     /**
16904      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16905      * rendering into an Roo.Editor, defaults to false)
16906      */
16907     /**
16908      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16909      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16910      */
16911     /**
16912      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16913      */
16914     /**
16915      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16916      * the dropdown list (defaults to undefined, with no header element)
16917      */
16918
16919      /**
16920      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16921      */
16922      
16923      /**
16924      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16925      */
16926     listWidth: undefined,
16927     /**
16928      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16929      * mode = 'remote' or 'text' if mode = 'local')
16930      */
16931     displayField: undefined,
16932     
16933     /**
16934      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16935      * mode = 'remote' or 'value' if mode = 'local'). 
16936      * Note: use of a valueField requires the user make a selection
16937      * in order for a value to be mapped.
16938      */
16939     valueField: undefined,
16940     /**
16941      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16942      */
16943     modalTitle : '',
16944     
16945     /**
16946      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16947      * field's data value (defaults to the underlying DOM element's name)
16948      */
16949     hiddenName: undefined,
16950     /**
16951      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16952      */
16953     listClass: '',
16954     /**
16955      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16956      */
16957     selectedClass: 'active',
16958     
16959     /**
16960      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16961      */
16962     shadow:'sides',
16963     /**
16964      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16965      * anchor positions (defaults to 'tl-bl')
16966      */
16967     listAlign: 'tl-bl?',
16968     /**
16969      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16970      */
16971     maxHeight: 300,
16972     /**
16973      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16974      * query specified by the allQuery config option (defaults to 'query')
16975      */
16976     triggerAction: 'query',
16977     /**
16978      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16979      * (defaults to 4, does not apply if editable = false)
16980      */
16981     minChars : 4,
16982     /**
16983      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16984      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16985      */
16986     typeAhead: false,
16987     /**
16988      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16989      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16990      */
16991     queryDelay: 500,
16992     /**
16993      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16994      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16995      */
16996     pageSize: 0,
16997     /**
16998      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16999      * when editable = true (defaults to false)
17000      */
17001     selectOnFocus:false,
17002     /**
17003      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17004      */
17005     queryParam: 'query',
17006     /**
17007      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17008      * when mode = 'remote' (defaults to 'Loading...')
17009      */
17010     loadingText: 'Loading...',
17011     /**
17012      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17013      */
17014     resizable: false,
17015     /**
17016      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17017      */
17018     handleHeight : 8,
17019     /**
17020      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17021      * traditional select (defaults to true)
17022      */
17023     editable: true,
17024     /**
17025      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17026      */
17027     allQuery: '',
17028     /**
17029      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17030      */
17031     mode: 'remote',
17032     /**
17033      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17034      * listWidth has a higher value)
17035      */
17036     minListWidth : 70,
17037     /**
17038      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17039      * allow the user to set arbitrary text into the field (defaults to false)
17040      */
17041     forceSelection:false,
17042     /**
17043      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17044      * if typeAhead = true (defaults to 250)
17045      */
17046     typeAheadDelay : 250,
17047     /**
17048      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17049      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17050      */
17051     valueNotFoundText : undefined,
17052     /**
17053      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17054      */
17055     blockFocus : false,
17056     
17057     /**
17058      * @cfg {Boolean} disableClear Disable showing of clear button.
17059      */
17060     disableClear : false,
17061     /**
17062      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17063      */
17064     alwaysQuery : false,
17065     
17066     /**
17067      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17068      */
17069     multiple : false,
17070     
17071     /**
17072      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17073      */
17074     invalidClass : "has-warning",
17075     
17076     /**
17077      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17078      */
17079     validClass : "has-success",
17080     
17081     /**
17082      * @cfg {Boolean} specialFilter (true|false) special filter default false
17083      */
17084     specialFilter : false,
17085     
17086     /**
17087      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17088      */
17089     mobileTouchView : true,
17090     
17091     /**
17092      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17093      */
17094     useNativeIOS : false,
17095     
17096     /**
17097      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17098      */
17099     mobile_restrict_height : false,
17100     
17101     ios_options : false,
17102     
17103     //private
17104     addicon : false,
17105     editicon: false,
17106     
17107     page: 0,
17108     hasQuery: false,
17109     append: false,
17110     loadNext: false,
17111     autoFocus : true,
17112     tickable : false,
17113     btnPosition : 'right',
17114     triggerList : true,
17115     showToggleBtn : true,
17116     animate : true,
17117     emptyResultText: 'Empty',
17118     triggerText : 'Select',
17119     emptyTitle : '',
17120     width : false,
17121     
17122     // element that contains real text value.. (when hidden is used..)
17123     
17124     getAutoCreate : function()
17125     {   
17126         var cfg = false;
17127         //render
17128         /*
17129          * Render classic select for iso
17130          */
17131         
17132         if(Roo.isIOS && this.useNativeIOS){
17133             cfg = this.getAutoCreateNativeIOS();
17134             return cfg;
17135         }
17136         
17137         /*
17138          * Touch Devices
17139          */
17140         
17141         if(Roo.isTouch && this.mobileTouchView){
17142             cfg = this.getAutoCreateTouchView();
17143             return cfg;;
17144         }
17145         
17146         /*
17147          *  Normal ComboBox
17148          */
17149         if(!this.tickable){
17150             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17151             return cfg;
17152         }
17153         
17154         /*
17155          *  ComboBox with tickable selections
17156          */
17157              
17158         var align = this.labelAlign || this.parentLabelAlign();
17159         
17160         cfg = {
17161             cls : 'form-group roo-combobox-tickable' //input-group
17162         };
17163         
17164         var btn_text_select = '';
17165         var btn_text_done = '';
17166         var btn_text_cancel = '';
17167         
17168         if (this.btn_text_show) {
17169             btn_text_select = 'Select';
17170             btn_text_done = 'Done';
17171             btn_text_cancel = 'Cancel'; 
17172         }
17173         
17174         var buttons = {
17175             tag : 'div',
17176             cls : 'tickable-buttons',
17177             cn : [
17178                 {
17179                     tag : 'button',
17180                     type : 'button',
17181                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17182                     //html : this.triggerText
17183                     html: btn_text_select
17184                 },
17185                 {
17186                     tag : 'button',
17187                     type : 'button',
17188                     name : 'ok',
17189                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17190                     //html : 'Done'
17191                     html: btn_text_done
17192                 },
17193                 {
17194                     tag : 'button',
17195                     type : 'button',
17196                     name : 'cancel',
17197                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17198                     //html : 'Cancel'
17199                     html: btn_text_cancel
17200                 }
17201             ]
17202         };
17203         
17204         if(this.editable){
17205             buttons.cn.unshift({
17206                 tag: 'input',
17207                 cls: 'roo-select2-search-field-input'
17208             });
17209         }
17210         
17211         var _this = this;
17212         
17213         Roo.each(buttons.cn, function(c){
17214             if (_this.size) {
17215                 c.cls += ' btn-' + _this.size;
17216             }
17217
17218             if (_this.disabled) {
17219                 c.disabled = true;
17220             }
17221         });
17222         
17223         var box = {
17224             tag: 'div',
17225             style : 'display: contents',
17226             cn: [
17227                 {
17228                     tag: 'input',
17229                     type : 'hidden',
17230                     cls: 'form-hidden-field'
17231                 },
17232                 {
17233                     tag: 'ul',
17234                     cls: 'roo-select2-choices',
17235                     cn:[
17236                         {
17237                             tag: 'li',
17238                             cls: 'roo-select2-search-field',
17239                             cn: [
17240                                 buttons
17241                             ]
17242                         }
17243                     ]
17244                 }
17245             ]
17246         };
17247         
17248         var combobox = {
17249             cls: 'roo-select2-container input-group roo-select2-container-multi',
17250             cn: [
17251                 
17252                 box
17253 //                {
17254 //                    tag: 'ul',
17255 //                    cls: 'typeahead typeahead-long dropdown-menu',
17256 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17257 //                }
17258             ]
17259         };
17260         
17261         if(this.hasFeedback && !this.allowBlank){
17262             
17263             var feedback = {
17264                 tag: 'span',
17265                 cls: 'glyphicon form-control-feedback'
17266             };
17267
17268             combobox.cn.push(feedback);
17269         }
17270         
17271         
17272         
17273         var indicator = {
17274             tag : 'i',
17275             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17276             tooltip : 'This field is required'
17277         };
17278         if (Roo.bootstrap.version == 4) {
17279             indicator = {
17280                 tag : 'i',
17281                 style : 'display:none'
17282             };
17283         }
17284         if (align ==='left' && this.fieldLabel.length) {
17285             
17286             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17287             
17288             cfg.cn = [
17289                 indicator,
17290                 {
17291                     tag: 'label',
17292                     'for' :  id,
17293                     cls : 'control-label col-form-label',
17294                     html : this.fieldLabel
17295
17296                 },
17297                 {
17298                     cls : "", 
17299                     cn: [
17300                         combobox
17301                     ]
17302                 }
17303
17304             ];
17305             
17306             var labelCfg = cfg.cn[1];
17307             var contentCfg = cfg.cn[2];
17308             
17309
17310             if(this.indicatorpos == 'right'){
17311                 
17312                 cfg.cn = [
17313                     {
17314                         tag: 'label',
17315                         'for' :  id,
17316                         cls : 'control-label col-form-label',
17317                         cn : [
17318                             {
17319                                 tag : 'span',
17320                                 html : this.fieldLabel
17321                             },
17322                             indicator
17323                         ]
17324                     },
17325                     {
17326                         cls : "",
17327                         cn: [
17328                             combobox
17329                         ]
17330                     }
17331
17332                 ];
17333                 
17334                 
17335                 
17336                 labelCfg = cfg.cn[0];
17337                 contentCfg = cfg.cn[1];
17338             
17339             }
17340             
17341             if(this.labelWidth > 12){
17342                 labelCfg.style = "width: " + this.labelWidth + 'px';
17343             }
17344             if(this.width * 1 > 0){
17345                 contentCfg.style = "width: " + this.width + 'px';
17346             }
17347             if(this.labelWidth < 13 && this.labelmd == 0){
17348                 this.labelmd = this.labelWidth;
17349             }
17350             
17351             if(this.labellg > 0){
17352                 labelCfg.cls += ' col-lg-' + this.labellg;
17353                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17354             }
17355             
17356             if(this.labelmd > 0){
17357                 labelCfg.cls += ' col-md-' + this.labelmd;
17358                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17359             }
17360             
17361             if(this.labelsm > 0){
17362                 labelCfg.cls += ' col-sm-' + this.labelsm;
17363                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17364             }
17365             
17366             if(this.labelxs > 0){
17367                 labelCfg.cls += ' col-xs-' + this.labelxs;
17368                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17369             }
17370                 
17371                 
17372         } else if ( this.fieldLabel.length) {
17373 //                Roo.log(" label");
17374                  cfg.cn = [
17375                    indicator,
17376                     {
17377                         tag: 'label',
17378                         //cls : 'input-group-addon',
17379                         html : this.fieldLabel
17380                     },
17381                     combobox
17382                 ];
17383                 
17384                 if(this.indicatorpos == 'right'){
17385                     cfg.cn = [
17386                         {
17387                             tag: 'label',
17388                             //cls : 'input-group-addon',
17389                             html : this.fieldLabel
17390                         },
17391                         indicator,
17392                         combobox
17393                     ];
17394                     
17395                 }
17396
17397         } else {
17398             
17399 //                Roo.log(" no label && no align");
17400                 cfg = combobox
17401                      
17402                 
17403         }
17404          
17405         var settings=this;
17406         ['xs','sm','md','lg'].map(function(size){
17407             if (settings[size]) {
17408                 cfg.cls += ' col-' + size + '-' + settings[size];
17409             }
17410         });
17411         
17412         return cfg;
17413         
17414     },
17415     
17416     _initEventsCalled : false,
17417     
17418     // private
17419     initEvents: function()
17420     {   
17421         if (this._initEventsCalled) { // as we call render... prevent looping...
17422             return;
17423         }
17424         this._initEventsCalled = true;
17425         
17426         if (!this.store) {
17427             throw "can not find store for combo";
17428         }
17429         
17430         this.indicator = this.indicatorEl();
17431         
17432         this.store = Roo.factory(this.store, Roo.data);
17433         this.store.parent = this;
17434         
17435         // if we are building from html. then this element is so complex, that we can not really
17436         // use the rendered HTML.
17437         // so we have to trash and replace the previous code.
17438         if (Roo.XComponent.build_from_html) {
17439             // remove this element....
17440             var e = this.el.dom, k=0;
17441             while (e ) { e = e.previousSibling;  ++k;}
17442
17443             this.el.remove();
17444             
17445             this.el=false;
17446             this.rendered = false;
17447             
17448             this.render(this.parent().getChildContainer(true), k);
17449         }
17450         
17451         if(Roo.isIOS && this.useNativeIOS){
17452             this.initIOSView();
17453             return;
17454         }
17455         
17456         /*
17457          * Touch Devices
17458          */
17459         
17460         if(Roo.isTouch && this.mobileTouchView){
17461             this.initTouchView();
17462             return;
17463         }
17464         
17465         if(this.tickable){
17466             this.initTickableEvents();
17467             return;
17468         }
17469         
17470         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17471         
17472         if(this.hiddenName){
17473             
17474             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17475             
17476             this.hiddenField.dom.value =
17477                 this.hiddenValue !== undefined ? this.hiddenValue :
17478                 this.value !== undefined ? this.value : '';
17479
17480             // prevent input submission
17481             this.el.dom.removeAttribute('name');
17482             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17483              
17484              
17485         }
17486         //if(Roo.isGecko){
17487         //    this.el.dom.setAttribute('autocomplete', 'off');
17488         //}
17489         
17490         var cls = 'x-combo-list';
17491         
17492         //this.list = new Roo.Layer({
17493         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17494         //});
17495         
17496         var _this = this;
17497         
17498         (function(){
17499             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17500             _this.list.setWidth(lw);
17501         }).defer(100);
17502         
17503         this.list.on('mouseover', this.onViewOver, this);
17504         this.list.on('mousemove', this.onViewMove, this);
17505         this.list.on('scroll', this.onViewScroll, this);
17506         
17507         /*
17508         this.list.swallowEvent('mousewheel');
17509         this.assetHeight = 0;
17510
17511         if(this.title){
17512             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17513             this.assetHeight += this.header.getHeight();
17514         }
17515
17516         this.innerList = this.list.createChild({cls:cls+'-inner'});
17517         this.innerList.on('mouseover', this.onViewOver, this);
17518         this.innerList.on('mousemove', this.onViewMove, this);
17519         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17520         
17521         if(this.allowBlank && !this.pageSize && !this.disableClear){
17522             this.footer = this.list.createChild({cls:cls+'-ft'});
17523             this.pageTb = new Roo.Toolbar(this.footer);
17524            
17525         }
17526         if(this.pageSize){
17527             this.footer = this.list.createChild({cls:cls+'-ft'});
17528             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17529                     {pageSize: this.pageSize});
17530             
17531         }
17532         
17533         if (this.pageTb && this.allowBlank && !this.disableClear) {
17534             var _this = this;
17535             this.pageTb.add(new Roo.Toolbar.Fill(), {
17536                 cls: 'x-btn-icon x-btn-clear',
17537                 text: '&#160;',
17538                 handler: function()
17539                 {
17540                     _this.collapse();
17541                     _this.clearValue();
17542                     _this.onSelect(false, -1);
17543                 }
17544             });
17545         }
17546         if (this.footer) {
17547             this.assetHeight += this.footer.getHeight();
17548         }
17549         */
17550             
17551         if(!this.tpl){
17552             this.tpl = Roo.bootstrap.version == 4 ?
17553                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17554                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17555         }
17556
17557         this.view = new Roo.View(this.list, this.tpl, {
17558             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17559         });
17560         //this.view.wrapEl.setDisplayed(false);
17561         this.view.on('click', this.onViewClick, this);
17562         
17563         
17564         this.store.on('beforeload', this.onBeforeLoad, this);
17565         this.store.on('load', this.onLoad, this);
17566         this.store.on('loadexception', this.onLoadException, this);
17567         /*
17568         if(this.resizable){
17569             this.resizer = new Roo.Resizable(this.list,  {
17570                pinned:true, handles:'se'
17571             });
17572             this.resizer.on('resize', function(r, w, h){
17573                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17574                 this.listWidth = w;
17575                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17576                 this.restrictHeight();
17577             }, this);
17578             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17579         }
17580         */
17581         if(!this.editable){
17582             this.editable = true;
17583             this.setEditable(false);
17584         }
17585         
17586         /*
17587         
17588         if (typeof(this.events.add.listeners) != 'undefined') {
17589             
17590             this.addicon = this.wrap.createChild(
17591                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17592        
17593             this.addicon.on('click', function(e) {
17594                 this.fireEvent('add', this);
17595             }, this);
17596         }
17597         if (typeof(this.events.edit.listeners) != 'undefined') {
17598             
17599             this.editicon = this.wrap.createChild(
17600                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17601             if (this.addicon) {
17602                 this.editicon.setStyle('margin-left', '40px');
17603             }
17604             this.editicon.on('click', function(e) {
17605                 
17606                 // we fire even  if inothing is selected..
17607                 this.fireEvent('edit', this, this.lastData );
17608                 
17609             }, this);
17610         }
17611         */
17612         
17613         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17614             "up" : function(e){
17615                 this.inKeyMode = true;
17616                 this.selectPrev();
17617             },
17618
17619             "down" : function(e){
17620                 if(!this.isExpanded()){
17621                     this.onTriggerClick();
17622                 }else{
17623                     this.inKeyMode = true;
17624                     this.selectNext();
17625                 }
17626             },
17627
17628             "enter" : function(e){
17629 //                this.onViewClick();
17630                 //return true;
17631                 this.collapse();
17632                 
17633                 if(this.fireEvent("specialkey", this, e)){
17634                     this.onViewClick(false);
17635                 }
17636                 
17637                 return true;
17638             },
17639
17640             "esc" : function(e){
17641                 this.collapse();
17642             },
17643
17644             "tab" : function(e){
17645                 this.collapse();
17646                 
17647                 if(this.fireEvent("specialkey", this, e)){
17648                     this.onViewClick(false);
17649                 }
17650                 
17651                 return true;
17652             },
17653
17654             scope : this,
17655
17656             doRelay : function(foo, bar, hname){
17657                 if(hname == 'down' || this.scope.isExpanded()){
17658                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17659                 }
17660                 return true;
17661             },
17662
17663             forceKeyDown: true
17664         });
17665         
17666         
17667         this.queryDelay = Math.max(this.queryDelay || 10,
17668                 this.mode == 'local' ? 10 : 250);
17669         
17670         
17671         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17672         
17673         if(this.typeAhead){
17674             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17675         }
17676         if(this.editable !== false){
17677             this.inputEl().on("keyup", this.onKeyUp, this);
17678         }
17679         if(this.forceSelection){
17680             this.inputEl().on('blur', this.doForce, this);
17681         }
17682         
17683         if(this.multiple){
17684             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17685             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17686         }
17687     },
17688     
17689     initTickableEvents: function()
17690     {   
17691         this.createList();
17692         
17693         if(this.hiddenName){
17694             
17695             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17696             
17697             this.hiddenField.dom.value =
17698                 this.hiddenValue !== undefined ? this.hiddenValue :
17699                 this.value !== undefined ? this.value : '';
17700
17701             // prevent input submission
17702             this.el.dom.removeAttribute('name');
17703             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17704              
17705              
17706         }
17707         
17708 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17709         
17710         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17711         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17712         if(this.triggerList){
17713             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17714         }
17715          
17716         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17717         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17718         
17719         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17720         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17721         
17722         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17723         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17724         
17725         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17726         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17727         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17728         
17729         this.okBtn.hide();
17730         this.cancelBtn.hide();
17731         
17732         var _this = this;
17733         
17734         (function(){
17735             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17736             _this.list.setWidth(lw);
17737         }).defer(100);
17738         
17739         this.list.on('mouseover', this.onViewOver, this);
17740         this.list.on('mousemove', this.onViewMove, this);
17741         
17742         this.list.on('scroll', this.onViewScroll, this);
17743         
17744         if(!this.tpl){
17745             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17746                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17747         }
17748
17749         this.view = new Roo.View(this.list, this.tpl, {
17750             singleSelect:true,
17751             tickable:true,
17752             parent:this,
17753             store: this.store,
17754             selectedClass: this.selectedClass
17755         });
17756         
17757         //this.view.wrapEl.setDisplayed(false);
17758         this.view.on('click', this.onViewClick, this);
17759         
17760         
17761         
17762         this.store.on('beforeload', this.onBeforeLoad, this);
17763         this.store.on('load', this.onLoad, this);
17764         this.store.on('loadexception', this.onLoadException, this);
17765         
17766         if(this.editable){
17767             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17768                 "up" : function(e){
17769                     this.inKeyMode = true;
17770                     this.selectPrev();
17771                 },
17772
17773                 "down" : function(e){
17774                     this.inKeyMode = true;
17775                     this.selectNext();
17776                 },
17777
17778                 "enter" : function(e){
17779                     if(this.fireEvent("specialkey", this, e)){
17780                         this.onViewClick(false);
17781                     }
17782                     
17783                     return true;
17784                 },
17785
17786                 "esc" : function(e){
17787                     this.onTickableFooterButtonClick(e, false, false);
17788                 },
17789
17790                 "tab" : function(e){
17791                     this.fireEvent("specialkey", this, e);
17792                     
17793                     this.onTickableFooterButtonClick(e, false, false);
17794                     
17795                     return true;
17796                 },
17797
17798                 scope : this,
17799
17800                 doRelay : function(e, fn, key){
17801                     if(this.scope.isExpanded()){
17802                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17803                     }
17804                     return true;
17805                 },
17806
17807                 forceKeyDown: true
17808             });
17809         }
17810         
17811         this.queryDelay = Math.max(this.queryDelay || 10,
17812                 this.mode == 'local' ? 10 : 250);
17813         
17814         
17815         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17816         
17817         if(this.typeAhead){
17818             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17819         }
17820         
17821         if(this.editable !== false){
17822             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17823         }
17824         
17825         this.indicator = this.indicatorEl();
17826         
17827         if(this.indicator){
17828             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17829             this.indicator.hide();
17830         }
17831         
17832     },
17833
17834     onDestroy : function(){
17835         if(this.view){
17836             this.view.setStore(null);
17837             this.view.el.removeAllListeners();
17838             this.view.el.remove();
17839             this.view.purgeListeners();
17840         }
17841         if(this.list){
17842             this.list.dom.innerHTML  = '';
17843         }
17844         
17845         if(this.store){
17846             this.store.un('beforeload', this.onBeforeLoad, this);
17847             this.store.un('load', this.onLoad, this);
17848             this.store.un('loadexception', this.onLoadException, this);
17849         }
17850         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17851     },
17852
17853     // private
17854     fireKey : function(e){
17855         if(e.isNavKeyPress() && !this.list.isVisible()){
17856             this.fireEvent("specialkey", this, e);
17857         }
17858     },
17859
17860     // private
17861     onResize: function(w, h)
17862     {
17863         
17864         
17865 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17866 //        
17867 //        if(typeof w != 'number'){
17868 //            // we do not handle it!?!?
17869 //            return;
17870 //        }
17871 //        var tw = this.trigger.getWidth();
17872 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17873 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17874 //        var x = w - tw;
17875 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17876 //            
17877 //        //this.trigger.setStyle('left', x+'px');
17878 //        
17879 //        if(this.list && this.listWidth === undefined){
17880 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17881 //            this.list.setWidth(lw);
17882 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17883 //        }
17884         
17885     
17886         
17887     },
17888
17889     /**
17890      * Allow or prevent the user from directly editing the field text.  If false is passed,
17891      * the user will only be able to select from the items defined in the dropdown list.  This method
17892      * is the runtime equivalent of setting the 'editable' config option at config time.
17893      * @param {Boolean} value True to allow the user to directly edit the field text
17894      */
17895     setEditable : function(value){
17896         if(value == this.editable){
17897             return;
17898         }
17899         this.editable = value;
17900         if(!value){
17901             this.inputEl().dom.setAttribute('readOnly', true);
17902             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17903             this.inputEl().addClass('x-combo-noedit');
17904         }else{
17905             this.inputEl().dom.removeAttribute('readOnly');
17906             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17907             this.inputEl().removeClass('x-combo-noedit');
17908         }
17909     },
17910
17911     // private
17912     
17913     onBeforeLoad : function(combo,opts){
17914         if(!this.hasFocus){
17915             return;
17916         }
17917          if (!opts.add) {
17918             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17919          }
17920         this.restrictHeight();
17921         this.selectedIndex = -1;
17922     },
17923
17924     // private
17925     onLoad : function(){
17926         
17927         this.hasQuery = false;
17928         
17929         if(!this.hasFocus){
17930             return;
17931         }
17932         
17933         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17934             this.loading.hide();
17935         }
17936         
17937         if(this.store.getCount() > 0){
17938             
17939             this.expand();
17940             this.restrictHeight();
17941             if(this.lastQuery == this.allQuery){
17942                 if(this.editable && !this.tickable){
17943                     this.inputEl().dom.select();
17944                 }
17945                 
17946                 if(
17947                     !this.selectByValue(this.value, true) &&
17948                     this.autoFocus && 
17949                     (
17950                         !this.store.lastOptions ||
17951                         typeof(this.store.lastOptions.add) == 'undefined' || 
17952                         this.store.lastOptions.add != true
17953                     )
17954                 ){
17955                     this.select(0, true);
17956                 }
17957             }else{
17958                 if(this.autoFocus){
17959                     this.selectNext();
17960                 }
17961                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17962                     this.taTask.delay(this.typeAheadDelay);
17963                 }
17964             }
17965         }else{
17966             this.onEmptyResults();
17967         }
17968         
17969         //this.el.focus();
17970     },
17971     // private
17972     onLoadException : function()
17973     {
17974         this.hasQuery = false;
17975         
17976         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17977             this.loading.hide();
17978         }
17979         
17980         if(this.tickable && this.editable){
17981             return;
17982         }
17983         
17984         this.collapse();
17985         // only causes errors at present
17986         //Roo.log(this.store.reader.jsonData);
17987         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17988             // fixme
17989             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17990         //}
17991         
17992         
17993     },
17994     // private
17995     onTypeAhead : function(){
17996         if(this.store.getCount() > 0){
17997             var r = this.store.getAt(0);
17998             var newValue = r.data[this.displayField];
17999             var len = newValue.length;
18000             var selStart = this.getRawValue().length;
18001             
18002             if(selStart != len){
18003                 this.setRawValue(newValue);
18004                 this.selectText(selStart, newValue.length);
18005             }
18006         }
18007     },
18008
18009     // private
18010     onSelect : function(record, index){
18011         
18012         if(this.fireEvent('beforeselect', this, record, index) !== false){
18013         
18014             this.setFromData(index > -1 ? record.data : false);
18015             
18016             this.collapse();
18017             this.fireEvent('select', this, record, index);
18018         }
18019     },
18020
18021     /**
18022      * Returns the currently selected field value or empty string if no value is set.
18023      * @return {String} value The selected value
18024      */
18025     getValue : function()
18026     {
18027         if(Roo.isIOS && this.useNativeIOS){
18028             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18029         }
18030         
18031         if(this.multiple){
18032             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18033         }
18034         
18035         if(this.valueField){
18036             return typeof this.value != 'undefined' ? this.value : '';
18037         }else{
18038             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18039         }
18040     },
18041     
18042     getRawValue : function()
18043     {
18044         if(Roo.isIOS && this.useNativeIOS){
18045             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18046         }
18047         
18048         var v = this.inputEl().getValue();
18049         
18050         return v;
18051     },
18052
18053     /**
18054      * Clears any text/value currently set in the field
18055      */
18056     clearValue : function(){
18057         
18058         if(this.hiddenField){
18059             this.hiddenField.dom.value = '';
18060         }
18061         this.value = '';
18062         this.setRawValue('');
18063         this.lastSelectionText = '';
18064         this.lastData = false;
18065         
18066         var close = this.closeTriggerEl();
18067         
18068         if(close){
18069             close.hide();
18070         }
18071         
18072         this.validate();
18073         
18074     },
18075
18076     /**
18077      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18078      * will be displayed in the field.  If the value does not match the data value of an existing item,
18079      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18080      * Otherwise the field will be blank (although the value will still be set).
18081      * @param {String} value The value to match
18082      */
18083     setValue : function(v)
18084     {
18085         if(Roo.isIOS && this.useNativeIOS){
18086             this.setIOSValue(v);
18087             return;
18088         }
18089         
18090         if(this.multiple){
18091             this.syncValue();
18092             return;
18093         }
18094         
18095         var text = v;
18096         if(this.valueField){
18097             var r = this.findRecord(this.valueField, v);
18098             if(r){
18099                 text = r.data[this.displayField];
18100             }else if(this.valueNotFoundText !== undefined){
18101                 text = this.valueNotFoundText;
18102             }
18103         }
18104         this.lastSelectionText = text;
18105         if(this.hiddenField){
18106             this.hiddenField.dom.value = v;
18107         }
18108         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18109         this.value = v;
18110         
18111         var close = this.closeTriggerEl();
18112         
18113         if(close){
18114             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18115         }
18116         
18117         this.validate();
18118     },
18119     /**
18120      * @property {Object} the last set data for the element
18121      */
18122     
18123     lastData : false,
18124     /**
18125      * Sets the value of the field based on a object which is related to the record format for the store.
18126      * @param {Object} value the value to set as. or false on reset?
18127      */
18128     setFromData : function(o){
18129         
18130         if(this.multiple){
18131             this.addItem(o);
18132             return;
18133         }
18134             
18135         var dv = ''; // display value
18136         var vv = ''; // value value..
18137         this.lastData = o;
18138         if (this.displayField) {
18139             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18140         } else {
18141             // this is an error condition!!!
18142             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18143         }
18144         
18145         if(this.valueField){
18146             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18147         }
18148         
18149         var close = this.closeTriggerEl();
18150         
18151         if(close){
18152             if(dv.length || vv * 1 > 0){
18153                 close.show() ;
18154                 this.blockFocus=true;
18155             } else {
18156                 close.hide();
18157             }             
18158         }
18159         
18160         if(this.hiddenField){
18161             this.hiddenField.dom.value = vv;
18162             
18163             this.lastSelectionText = dv;
18164             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18165             this.value = vv;
18166             return;
18167         }
18168         // no hidden field.. - we store the value in 'value', but still display
18169         // display field!!!!
18170         this.lastSelectionText = dv;
18171         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18172         this.value = vv;
18173         
18174         
18175         
18176     },
18177     // private
18178     reset : function(){
18179         // overridden so that last data is reset..
18180         
18181         if(this.multiple){
18182             this.clearItem();
18183             return;
18184         }
18185         
18186         this.setValue(this.originalValue);
18187         //this.clearInvalid();
18188         this.lastData = false;
18189         if (this.view) {
18190             this.view.clearSelections();
18191         }
18192         
18193         this.validate();
18194     },
18195     // private
18196     findRecord : function(prop, value){
18197         var record;
18198         if(this.store.getCount() > 0){
18199             this.store.each(function(r){
18200                 if(r.data[prop] == value){
18201                     record = r;
18202                     return false;
18203                 }
18204                 return true;
18205             });
18206         }
18207         return record;
18208     },
18209     
18210     getName: function()
18211     {
18212         // returns hidden if it's set..
18213         if (!this.rendered) {return ''};
18214         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18215         
18216     },
18217     // private
18218     onViewMove : function(e, t){
18219         this.inKeyMode = false;
18220     },
18221
18222     // private
18223     onViewOver : function(e, t){
18224         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18225             return;
18226         }
18227         var item = this.view.findItemFromChild(t);
18228         
18229         if(item){
18230             var index = this.view.indexOf(item);
18231             this.select(index, false);
18232         }
18233     },
18234
18235     // private
18236     onViewClick : function(view, doFocus, el, e)
18237     {
18238         var index = this.view.getSelectedIndexes()[0];
18239         
18240         var r = this.store.getAt(index);
18241         
18242         if(this.tickable){
18243             
18244             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18245                 return;
18246             }
18247             
18248             var rm = false;
18249             var _this = this;
18250             
18251             Roo.each(this.tickItems, function(v,k){
18252                 
18253                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18254                     Roo.log(v);
18255                     _this.tickItems.splice(k, 1);
18256                     
18257                     if(typeof(e) == 'undefined' && view == false){
18258                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18259                     }
18260                     
18261                     rm = true;
18262                     return;
18263                 }
18264             });
18265             
18266             if(rm){
18267                 return;
18268             }
18269             
18270             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18271                 this.tickItems.push(r.data);
18272             }
18273             
18274             if(typeof(e) == 'undefined' && view == false){
18275                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18276             }
18277                     
18278             return;
18279         }
18280         
18281         if(r){
18282             this.onSelect(r, index);
18283         }
18284         if(doFocus !== false && !this.blockFocus){
18285             this.inputEl().focus();
18286         }
18287     },
18288
18289     // private
18290     restrictHeight : function(){
18291         //this.innerList.dom.style.height = '';
18292         //var inner = this.innerList.dom;
18293         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18294         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18295         //this.list.beginUpdate();
18296         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18297         this.list.alignTo(this.inputEl(), this.listAlign);
18298         this.list.alignTo(this.inputEl(), this.listAlign);
18299         //this.list.endUpdate();
18300     },
18301
18302     // private
18303     onEmptyResults : function(){
18304         
18305         if(this.tickable && this.editable){
18306             this.hasFocus = false;
18307             this.restrictHeight();
18308             return;
18309         }
18310         
18311         this.collapse();
18312     },
18313
18314     /**
18315      * Returns true if the dropdown list is expanded, else false.
18316      */
18317     isExpanded : function(){
18318         return this.list.isVisible();
18319     },
18320
18321     /**
18322      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18323      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18324      * @param {String} value The data value of the item to select
18325      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18326      * selected item if it is not currently in view (defaults to true)
18327      * @return {Boolean} True if the value matched an item in the list, else false
18328      */
18329     selectByValue : function(v, scrollIntoView){
18330         if(v !== undefined && v !== null){
18331             var r = this.findRecord(this.valueField || this.displayField, v);
18332             if(r){
18333                 this.select(this.store.indexOf(r), scrollIntoView);
18334                 return true;
18335             }
18336         }
18337         return false;
18338     },
18339
18340     /**
18341      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18342      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18343      * @param {Number} index The zero-based index of the list item to select
18344      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18345      * selected item if it is not currently in view (defaults to true)
18346      */
18347     select : function(index, scrollIntoView){
18348         this.selectedIndex = index;
18349         this.view.select(index);
18350         if(scrollIntoView !== false){
18351             var el = this.view.getNode(index);
18352             /*
18353              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18354              */
18355             if(el){
18356                 this.list.scrollChildIntoView(el, false);
18357             }
18358         }
18359     },
18360
18361     // private
18362     selectNext : function(){
18363         var ct = this.store.getCount();
18364         if(ct > 0){
18365             if(this.selectedIndex == -1){
18366                 this.select(0);
18367             }else if(this.selectedIndex < ct-1){
18368                 this.select(this.selectedIndex+1);
18369             }
18370         }
18371     },
18372
18373     // private
18374     selectPrev : function(){
18375         var ct = this.store.getCount();
18376         if(ct > 0){
18377             if(this.selectedIndex == -1){
18378                 this.select(0);
18379             }else if(this.selectedIndex != 0){
18380                 this.select(this.selectedIndex-1);
18381             }
18382         }
18383     },
18384
18385     // private
18386     onKeyUp : function(e){
18387         if(this.editable !== false && !e.isSpecialKey()){
18388             this.lastKey = e.getKey();
18389             this.dqTask.delay(this.queryDelay);
18390         }
18391     },
18392
18393     // private
18394     validateBlur : function(){
18395         return !this.list || !this.list.isVisible();   
18396     },
18397
18398     // private
18399     initQuery : function(){
18400         
18401         var v = this.getRawValue();
18402         
18403         if(this.tickable && this.editable){
18404             v = this.tickableInputEl().getValue();
18405         }
18406         
18407         this.doQuery(v);
18408     },
18409
18410     // private
18411     doForce : function(){
18412         if(this.inputEl().dom.value.length > 0){
18413             this.inputEl().dom.value =
18414                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18415              
18416         }
18417     },
18418
18419     /**
18420      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18421      * query allowing the query action to be canceled if needed.
18422      * @param {String} query The SQL query to execute
18423      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18424      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18425      * saved in the current store (defaults to false)
18426      */
18427     doQuery : function(q, forceAll){
18428         
18429         if(q === undefined || q === null){
18430             q = '';
18431         }
18432         var qe = {
18433             query: q,
18434             forceAll: forceAll,
18435             combo: this,
18436             cancel:false
18437         };
18438         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18439             return false;
18440         }
18441         q = qe.query;
18442         
18443         forceAll = qe.forceAll;
18444         if(forceAll === true || (q.length >= this.minChars)){
18445             
18446             this.hasQuery = true;
18447             
18448             if(this.lastQuery != q || this.alwaysQuery){
18449                 this.lastQuery = q;
18450                 if(this.mode == 'local'){
18451                     this.selectedIndex = -1;
18452                     if(forceAll){
18453                         this.store.clearFilter();
18454                     }else{
18455                         
18456                         if(this.specialFilter){
18457                             this.fireEvent('specialfilter', this);
18458                             this.onLoad();
18459                             return;
18460                         }
18461                         
18462                         this.store.filter(this.displayField, q);
18463                     }
18464                     
18465                     this.store.fireEvent("datachanged", this.store);
18466                     
18467                     this.onLoad();
18468                     
18469                     
18470                 }else{
18471                     
18472                     this.store.baseParams[this.queryParam] = q;
18473                     
18474                     var options = {params : this.getParams(q)};
18475                     
18476                     if(this.loadNext){
18477                         options.add = true;
18478                         options.params.start = this.page * this.pageSize;
18479                     }
18480                     
18481                     this.store.load(options);
18482                     
18483                     /*
18484                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18485                      *  we should expand the list on onLoad
18486                      *  so command out it
18487                      */
18488 //                    this.expand();
18489                 }
18490             }else{
18491                 this.selectedIndex = -1;
18492                 this.onLoad();   
18493             }
18494         }
18495         
18496         this.loadNext = false;
18497     },
18498     
18499     // private
18500     getParams : function(q){
18501         var p = {};
18502         //p[this.queryParam] = q;
18503         
18504         if(this.pageSize){
18505             p.start = 0;
18506             p.limit = this.pageSize;
18507         }
18508         return p;
18509     },
18510
18511     /**
18512      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18513      */
18514     collapse : function(){
18515         if(!this.isExpanded()){
18516             return;
18517         }
18518         
18519         this.list.hide();
18520         
18521         this.hasFocus = false;
18522         
18523         if(this.tickable){
18524             this.okBtn.hide();
18525             this.cancelBtn.hide();
18526             this.trigger.show();
18527             
18528             if(this.editable){
18529                 this.tickableInputEl().dom.value = '';
18530                 this.tickableInputEl().blur();
18531             }
18532             
18533         }
18534         
18535         Roo.get(document).un('mousedown', this.collapseIf, this);
18536         Roo.get(document).un('mousewheel', this.collapseIf, this);
18537         if (!this.editable) {
18538             Roo.get(document).un('keydown', this.listKeyPress, this);
18539         }
18540         this.fireEvent('collapse', this);
18541         
18542         this.validate();
18543     },
18544
18545     // private
18546     collapseIf : function(e){
18547         var in_combo  = e.within(this.el);
18548         var in_list =  e.within(this.list);
18549         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18550         
18551         if (in_combo || in_list || is_list) {
18552             //e.stopPropagation();
18553             return;
18554         }
18555         
18556         if(this.tickable){
18557             this.onTickableFooterButtonClick(e, false, false);
18558         }
18559
18560         this.collapse();
18561         
18562     },
18563
18564     /**
18565      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18566      */
18567     expand : function(){
18568        
18569         if(this.isExpanded() || !this.hasFocus){
18570             return;
18571         }
18572         
18573         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18574         this.list.setWidth(lw);
18575         
18576         Roo.log('expand');
18577         
18578         this.list.show();
18579         
18580         this.restrictHeight();
18581         
18582         if(this.tickable){
18583             
18584             this.tickItems = Roo.apply([], this.item);
18585             
18586             this.okBtn.show();
18587             this.cancelBtn.show();
18588             this.trigger.hide();
18589             
18590             if(this.editable){
18591                 this.tickableInputEl().focus();
18592             }
18593             
18594         }
18595         
18596         Roo.get(document).on('mousedown', this.collapseIf, this);
18597         Roo.get(document).on('mousewheel', this.collapseIf, this);
18598         if (!this.editable) {
18599             Roo.get(document).on('keydown', this.listKeyPress, this);
18600         }
18601         
18602         this.fireEvent('expand', this);
18603     },
18604
18605     // private
18606     // Implements the default empty TriggerField.onTriggerClick function
18607     onTriggerClick : function(e)
18608     {
18609         Roo.log('trigger click');
18610         
18611         if(this.disabled || !this.triggerList){
18612             return;
18613         }
18614         
18615         this.page = 0;
18616         this.loadNext = false;
18617         
18618         if(this.isExpanded()){
18619             this.collapse();
18620             if (!this.blockFocus) {
18621                 this.inputEl().focus();
18622             }
18623             
18624         }else {
18625             this.hasFocus = true;
18626             if(this.triggerAction == 'all') {
18627                 this.doQuery(this.allQuery, true);
18628             } else {
18629                 this.doQuery(this.getRawValue());
18630             }
18631             if (!this.blockFocus) {
18632                 this.inputEl().focus();
18633             }
18634         }
18635     },
18636     
18637     onTickableTriggerClick : function(e)
18638     {
18639         if(this.disabled){
18640             return;
18641         }
18642         
18643         this.page = 0;
18644         this.loadNext = false;
18645         this.hasFocus = true;
18646         
18647         if(this.triggerAction == 'all') {
18648             this.doQuery(this.allQuery, true);
18649         } else {
18650             this.doQuery(this.getRawValue());
18651         }
18652     },
18653     
18654     onSearchFieldClick : function(e)
18655     {
18656         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18657             this.onTickableFooterButtonClick(e, false, false);
18658             return;
18659         }
18660         
18661         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18662             return;
18663         }
18664         
18665         this.page = 0;
18666         this.loadNext = false;
18667         this.hasFocus = true;
18668         
18669         if(this.triggerAction == 'all') {
18670             this.doQuery(this.allQuery, true);
18671         } else {
18672             this.doQuery(this.getRawValue());
18673         }
18674     },
18675     
18676     listKeyPress : function(e)
18677     {
18678         //Roo.log('listkeypress');
18679         // scroll to first matching element based on key pres..
18680         if (e.isSpecialKey()) {
18681             return false;
18682         }
18683         var k = String.fromCharCode(e.getKey()).toUpperCase();
18684         //Roo.log(k);
18685         var match  = false;
18686         var csel = this.view.getSelectedNodes();
18687         var cselitem = false;
18688         if (csel.length) {
18689             var ix = this.view.indexOf(csel[0]);
18690             cselitem  = this.store.getAt(ix);
18691             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18692                 cselitem = false;
18693             }
18694             
18695         }
18696         
18697         this.store.each(function(v) { 
18698             if (cselitem) {
18699                 // start at existing selection.
18700                 if (cselitem.id == v.id) {
18701                     cselitem = false;
18702                 }
18703                 return true;
18704             }
18705                 
18706             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18707                 match = this.store.indexOf(v);
18708                 return false;
18709             }
18710             return true;
18711         }, this);
18712         
18713         if (match === false) {
18714             return true; // no more action?
18715         }
18716         // scroll to?
18717         this.view.select(match);
18718         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18719         sn.scrollIntoView(sn.dom.parentNode, false);
18720     },
18721     
18722     onViewScroll : function(e, t){
18723         
18724         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){
18725             return;
18726         }
18727         
18728         this.hasQuery = true;
18729         
18730         this.loading = this.list.select('.loading', true).first();
18731         
18732         if(this.loading === null){
18733             this.list.createChild({
18734                 tag: 'div',
18735                 cls: 'loading roo-select2-more-results roo-select2-active',
18736                 html: 'Loading more results...'
18737             });
18738             
18739             this.loading = this.list.select('.loading', true).first();
18740             
18741             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18742             
18743             this.loading.hide();
18744         }
18745         
18746         this.loading.show();
18747         
18748         var _combo = this;
18749         
18750         this.page++;
18751         this.loadNext = true;
18752         
18753         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18754         
18755         return;
18756     },
18757     
18758     addItem : function(o)
18759     {   
18760         var dv = ''; // display value
18761         
18762         if (this.displayField) {
18763             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18764         } else {
18765             // this is an error condition!!!
18766             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18767         }
18768         
18769         if(!dv.length){
18770             return;
18771         }
18772         
18773         var choice = this.choices.createChild({
18774             tag: 'li',
18775             cls: 'roo-select2-search-choice',
18776             cn: [
18777                 {
18778                     tag: 'div',
18779                     html: dv
18780                 },
18781                 {
18782                     tag: 'a',
18783                     href: '#',
18784                     cls: 'roo-select2-search-choice-close fa fa-times',
18785                     tabindex: '-1'
18786                 }
18787             ]
18788             
18789         }, this.searchField);
18790         
18791         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18792         
18793         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18794         
18795         this.item.push(o);
18796         
18797         this.lastData = o;
18798         
18799         this.syncValue();
18800         
18801         this.inputEl().dom.value = '';
18802         
18803         this.validate();
18804     },
18805     
18806     onRemoveItem : function(e, _self, o)
18807     {
18808         e.preventDefault();
18809         
18810         this.lastItem = Roo.apply([], this.item);
18811         
18812         var index = this.item.indexOf(o.data) * 1;
18813         
18814         if( index < 0){
18815             Roo.log('not this item?!');
18816             return;
18817         }
18818         
18819         this.item.splice(index, 1);
18820         o.item.remove();
18821         
18822         this.syncValue();
18823         
18824         this.fireEvent('remove', this, e);
18825         
18826         this.validate();
18827         
18828     },
18829     
18830     syncValue : function()
18831     {
18832         if(!this.item.length){
18833             this.clearValue();
18834             return;
18835         }
18836             
18837         var value = [];
18838         var _this = this;
18839         Roo.each(this.item, function(i){
18840             if(_this.valueField){
18841                 value.push(i[_this.valueField]);
18842                 return;
18843             }
18844
18845             value.push(i);
18846         });
18847
18848         this.value = value.join(',');
18849
18850         if(this.hiddenField){
18851             this.hiddenField.dom.value = this.value;
18852         }
18853         
18854         this.store.fireEvent("datachanged", this.store);
18855         
18856         this.validate();
18857     },
18858     
18859     clearItem : function()
18860     {
18861         if(!this.multiple){
18862             return;
18863         }
18864         
18865         this.item = [];
18866         
18867         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18868            c.remove();
18869         });
18870         
18871         this.syncValue();
18872         
18873         this.validate();
18874         
18875         if(this.tickable && !Roo.isTouch){
18876             this.view.refresh();
18877         }
18878     },
18879     
18880     inputEl: function ()
18881     {
18882         if(Roo.isIOS && this.useNativeIOS){
18883             return this.el.select('select.roo-ios-select', true).first();
18884         }
18885         
18886         if(Roo.isTouch && this.mobileTouchView){
18887             return this.el.select('input.form-control',true).first();
18888         }
18889         
18890         if(this.tickable){
18891             return this.searchField;
18892         }
18893         
18894         return this.el.select('input.form-control',true).first();
18895     },
18896     
18897     onTickableFooterButtonClick : function(e, btn, el)
18898     {
18899         e.preventDefault();
18900         
18901         this.lastItem = Roo.apply([], this.item);
18902         
18903         if(btn && btn.name == 'cancel'){
18904             this.tickItems = Roo.apply([], this.item);
18905             this.collapse();
18906             return;
18907         }
18908         
18909         this.clearItem();
18910         
18911         var _this = this;
18912         
18913         Roo.each(this.tickItems, function(o){
18914             _this.addItem(o);
18915         });
18916         
18917         this.collapse();
18918         
18919     },
18920     
18921     validate : function()
18922     {
18923         if(this.getVisibilityEl().hasClass('hidden')){
18924             return true;
18925         }
18926         
18927         var v = this.getRawValue();
18928         
18929         if(this.multiple){
18930             v = this.getValue();
18931         }
18932         
18933         if(this.disabled || this.allowBlank || v.length){
18934             this.markValid();
18935             return true;
18936         }
18937         
18938         this.markInvalid();
18939         return false;
18940     },
18941     
18942     tickableInputEl : function()
18943     {
18944         if(!this.tickable || !this.editable){
18945             return this.inputEl();
18946         }
18947         
18948         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18949     },
18950     
18951     
18952     getAutoCreateTouchView : function()
18953     {
18954         var id = Roo.id();
18955         
18956         var cfg = {
18957             cls: 'form-group' //input-group
18958         };
18959         
18960         var input =  {
18961             tag: 'input',
18962             id : id,
18963             type : this.inputType,
18964             cls : 'form-control x-combo-noedit',
18965             autocomplete: 'new-password',
18966             placeholder : this.placeholder || '',
18967             readonly : true
18968         };
18969         
18970         if (this.name) {
18971             input.name = this.name;
18972         }
18973         
18974         if (this.size) {
18975             input.cls += ' input-' + this.size;
18976         }
18977         
18978         if (this.disabled) {
18979             input.disabled = true;
18980         }
18981         
18982         var inputblock = {
18983             cls : 'roo-combobox-wrap',
18984             cn : [
18985                 input
18986             ]
18987         };
18988         
18989         if(this.before){
18990             inputblock.cls += ' input-group';
18991             
18992             inputblock.cn.unshift({
18993                 tag :'span',
18994                 cls : 'input-group-addon input-group-prepend input-group-text',
18995                 html : this.before
18996             });
18997         }
18998         
18999         if(this.removable && !this.multiple){
19000             inputblock.cls += ' roo-removable';
19001             
19002             inputblock.cn.push({
19003                 tag: 'button',
19004                 html : 'x',
19005                 cls : 'roo-combo-removable-btn close'
19006             });
19007         }
19008
19009         if(this.hasFeedback && !this.allowBlank){
19010             
19011             inputblock.cls += ' has-feedback';
19012             
19013             inputblock.cn.push({
19014                 tag: 'span',
19015                 cls: 'glyphicon form-control-feedback'
19016             });
19017             
19018         }
19019         
19020         if (this.after) {
19021             
19022             inputblock.cls += (this.before) ? '' : ' input-group';
19023             
19024             inputblock.cn.push({
19025                 tag :'span',
19026                 cls : 'input-group-addon input-group-append input-group-text',
19027                 html : this.after
19028             });
19029         }
19030
19031         
19032         var ibwrap = inputblock;
19033         
19034         if(this.multiple){
19035             ibwrap = {
19036                 tag: 'ul',
19037                 cls: 'roo-select2-choices',
19038                 cn:[
19039                     {
19040                         tag: 'li',
19041                         cls: 'roo-select2-search-field',
19042                         cn: [
19043
19044                             inputblock
19045                         ]
19046                     }
19047                 ]
19048             };
19049         
19050             
19051         }
19052         
19053         var combobox = {
19054             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19055             cn: [
19056                 {
19057                     tag: 'input',
19058                     type : 'hidden',
19059                     cls: 'form-hidden-field'
19060                 },
19061                 ibwrap
19062             ]
19063         };
19064         
19065         if(!this.multiple && this.showToggleBtn){
19066             
19067             var caret = {
19068                 cls: 'caret'
19069             };
19070             
19071             if (this.caret != false) {
19072                 caret = {
19073                      tag: 'i',
19074                      cls: 'fa fa-' + this.caret
19075                 };
19076                 
19077             }
19078             
19079             combobox.cn.push({
19080                 tag :'span',
19081                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19082                 cn : [
19083                     Roo.bootstrap.version == 3 ? caret : '',
19084                     {
19085                         tag: 'span',
19086                         cls: 'combobox-clear',
19087                         cn  : [
19088                             {
19089                                 tag : 'i',
19090                                 cls: 'icon-remove'
19091                             }
19092                         ]
19093                     }
19094                 ]
19095
19096             })
19097         }
19098         
19099         if(this.multiple){
19100             combobox.cls += ' roo-select2-container-multi';
19101         }
19102         
19103         var required =  this.allowBlank ?  {
19104                     tag : 'i',
19105                     style: 'display: none'
19106                 } : {
19107                    tag : 'i',
19108                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19109                    tooltip : 'This field is required'
19110                 };
19111         
19112         var align = this.labelAlign || this.parentLabelAlign();
19113         
19114         if (align ==='left' && this.fieldLabel.length) {
19115
19116             cfg.cn = [
19117                 required,
19118                 {
19119                     tag: 'label',
19120                     cls : 'control-label col-form-label',
19121                     html : this.fieldLabel
19122
19123                 },
19124                 {
19125                     cls : 'roo-combobox-wrap ', 
19126                     cn: [
19127                         combobox
19128                     ]
19129                 }
19130             ];
19131             
19132             var labelCfg = cfg.cn[1];
19133             var contentCfg = cfg.cn[2];
19134             
19135
19136             if(this.indicatorpos == 'right'){
19137                 cfg.cn = [
19138                     {
19139                         tag: 'label',
19140                         'for' :  id,
19141                         cls : 'control-label col-form-label',
19142                         cn : [
19143                             {
19144                                 tag : 'span',
19145                                 html : this.fieldLabel
19146                             },
19147                             required
19148                         ]
19149                     },
19150                     {
19151                         cls : "roo-combobox-wrap ",
19152                         cn: [
19153                             combobox
19154                         ]
19155                     }
19156
19157                 ];
19158                 
19159                 labelCfg = cfg.cn[0];
19160                 contentCfg = cfg.cn[1];
19161             }
19162             
19163            
19164             
19165             if(this.labelWidth > 12){
19166                 labelCfg.style = "width: " + this.labelWidth + 'px';
19167             }
19168            
19169             if(this.labelWidth < 13 && this.labelmd == 0){
19170                 this.labelmd = this.labelWidth;
19171             }
19172             
19173             if(this.labellg > 0){
19174                 labelCfg.cls += ' col-lg-' + this.labellg;
19175                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19176             }
19177             
19178             if(this.labelmd > 0){
19179                 labelCfg.cls += ' col-md-' + this.labelmd;
19180                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19181             }
19182             
19183             if(this.labelsm > 0){
19184                 labelCfg.cls += ' col-sm-' + this.labelsm;
19185                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19186             }
19187             
19188             if(this.labelxs > 0){
19189                 labelCfg.cls += ' col-xs-' + this.labelxs;
19190                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19191             }
19192                 
19193                 
19194         } else if ( this.fieldLabel.length) {
19195             cfg.cn = [
19196                required,
19197                 {
19198                     tag: 'label',
19199                     cls : 'control-label',
19200                     html : this.fieldLabel
19201
19202                 },
19203                 {
19204                     cls : '', 
19205                     cn: [
19206                         combobox
19207                     ]
19208                 }
19209             ];
19210             
19211             if(this.indicatorpos == 'right'){
19212                 cfg.cn = [
19213                     {
19214                         tag: 'label',
19215                         cls : 'control-label',
19216                         html : this.fieldLabel,
19217                         cn : [
19218                             required
19219                         ]
19220                     },
19221                     {
19222                         cls : '', 
19223                         cn: [
19224                             combobox
19225                         ]
19226                     }
19227                 ];
19228             }
19229         } else {
19230             cfg.cn = combobox;    
19231         }
19232         
19233         
19234         var settings = this;
19235         
19236         ['xs','sm','md','lg'].map(function(size){
19237             if (settings[size]) {
19238                 cfg.cls += ' col-' + size + '-' + settings[size];
19239             }
19240         });
19241         
19242         return cfg;
19243     },
19244     
19245     initTouchView : function()
19246     {
19247         this.renderTouchView();
19248         
19249         this.touchViewEl.on('scroll', function(){
19250             this.el.dom.scrollTop = 0;
19251         }, this);
19252         
19253         this.originalValue = this.getValue();
19254         
19255         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19256         
19257         this.inputEl().on("click", this.showTouchView, this);
19258         if (this.triggerEl) {
19259             this.triggerEl.on("click", this.showTouchView, this);
19260         }
19261         
19262         
19263         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19264         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19265         
19266         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19267         
19268         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19269         this.store.on('load', this.onTouchViewLoad, this);
19270         this.store.on('loadexception', this.onTouchViewLoadException, this);
19271         
19272         if(this.hiddenName){
19273             
19274             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19275             
19276             this.hiddenField.dom.value =
19277                 this.hiddenValue !== undefined ? this.hiddenValue :
19278                 this.value !== undefined ? this.value : '';
19279         
19280             this.el.dom.removeAttribute('name');
19281             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19282         }
19283         
19284         if(this.multiple){
19285             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19286             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19287         }
19288         
19289         if(this.removable && !this.multiple){
19290             var close = this.closeTriggerEl();
19291             if(close){
19292                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19293                 close.on('click', this.removeBtnClick, this, close);
19294             }
19295         }
19296         /*
19297          * fix the bug in Safari iOS8
19298          */
19299         this.inputEl().on("focus", function(e){
19300             document.activeElement.blur();
19301         }, this);
19302         
19303         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19304         
19305         return;
19306         
19307         
19308     },
19309     
19310     renderTouchView : function()
19311     {
19312         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19313         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19314         
19315         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19316         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19317         
19318         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19319         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19320         this.touchViewBodyEl.setStyle('overflow', 'auto');
19321         
19322         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19323         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19324         
19325         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19326         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         
19328     },
19329     
19330     showTouchView : function()
19331     {
19332         if(this.disabled){
19333             return;
19334         }
19335         
19336         this.touchViewHeaderEl.hide();
19337
19338         if(this.modalTitle.length){
19339             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19340             this.touchViewHeaderEl.show();
19341         }
19342
19343         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19344         this.touchViewEl.show();
19345
19346         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19347         
19348         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19349         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19350
19351         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19352
19353         if(this.modalTitle.length){
19354             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19355         }
19356         
19357         this.touchViewBodyEl.setHeight(bodyHeight);
19358
19359         if(this.animate){
19360             var _this = this;
19361             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19362         }else{
19363             this.touchViewEl.addClass(['in','show']);
19364         }
19365         
19366         if(this._touchViewMask){
19367             Roo.get(document.body).addClass("x-body-masked");
19368             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19369             this._touchViewMask.setStyle('z-index', 10000);
19370             this._touchViewMask.addClass('show');
19371         }
19372         
19373         this.doTouchViewQuery();
19374         
19375     },
19376     
19377     hideTouchView : function()
19378     {
19379         this.touchViewEl.removeClass(['in','show']);
19380
19381         if(this.animate){
19382             var _this = this;
19383             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19384         }else{
19385             this.touchViewEl.setStyle('display', 'none');
19386         }
19387         
19388         if(this._touchViewMask){
19389             this._touchViewMask.removeClass('show');
19390             Roo.get(document.body).removeClass("x-body-masked");
19391         }
19392     },
19393     
19394     setTouchViewValue : function()
19395     {
19396         if(this.multiple){
19397             this.clearItem();
19398         
19399             var _this = this;
19400
19401             Roo.each(this.tickItems, function(o){
19402                 this.addItem(o);
19403             }, this);
19404         }
19405         
19406         this.hideTouchView();
19407     },
19408     
19409     doTouchViewQuery : function()
19410     {
19411         var qe = {
19412             query: '',
19413             forceAll: true,
19414             combo: this,
19415             cancel:false
19416         };
19417         
19418         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19419             return false;
19420         }
19421         
19422         if(!this.alwaysQuery || this.mode == 'local'){
19423             this.onTouchViewLoad();
19424             return;
19425         }
19426         
19427         this.store.load();
19428     },
19429     
19430     onTouchViewBeforeLoad : function(combo,opts)
19431     {
19432         return;
19433     },
19434
19435     // private
19436     onTouchViewLoad : function()
19437     {
19438         if(this.store.getCount() < 1){
19439             this.onTouchViewEmptyResults();
19440             return;
19441         }
19442         
19443         this.clearTouchView();
19444         
19445         var rawValue = this.getRawValue();
19446         
19447         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19448         
19449         this.tickItems = [];
19450         
19451         this.store.data.each(function(d, rowIndex){
19452             var row = this.touchViewListGroup.createChild(template);
19453             
19454             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19455                 row.addClass(d.data.cls);
19456             }
19457             
19458             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19459                 var cfg = {
19460                     data : d.data,
19461                     html : d.data[this.displayField]
19462                 };
19463                 
19464                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19465                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19466                 }
19467             }
19468             row.removeClass('selected');
19469             if(!this.multiple && this.valueField &&
19470                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19471             {
19472                 // radio buttons..
19473                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19474                 row.addClass('selected');
19475             }
19476             
19477             if(this.multiple && this.valueField &&
19478                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19479             {
19480                 
19481                 // checkboxes...
19482                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19483                 this.tickItems.push(d.data);
19484             }
19485             
19486             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19487             
19488         }, this);
19489         
19490         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19491         
19492         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19493
19494         if(this.modalTitle.length){
19495             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19496         }
19497
19498         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19499         
19500         if(this.mobile_restrict_height && listHeight < bodyHeight){
19501             this.touchViewBodyEl.setHeight(listHeight);
19502         }
19503         
19504         var _this = this;
19505         
19506         if(firstChecked && listHeight > bodyHeight){
19507             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19508         }
19509         
19510     },
19511     
19512     onTouchViewLoadException : function()
19513     {
19514         this.hideTouchView();
19515     },
19516     
19517     onTouchViewEmptyResults : function()
19518     {
19519         this.clearTouchView();
19520         
19521         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19522         
19523         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19524         
19525     },
19526     
19527     clearTouchView : function()
19528     {
19529         this.touchViewListGroup.dom.innerHTML = '';
19530     },
19531     
19532     onTouchViewClick : function(e, el, o)
19533     {
19534         e.preventDefault();
19535         
19536         var row = o.row;
19537         var rowIndex = o.rowIndex;
19538         
19539         var r = this.store.getAt(rowIndex);
19540         
19541         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19542             
19543             if(!this.multiple){
19544                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19545                     c.dom.removeAttribute('checked');
19546                 }, this);
19547
19548                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19549
19550                 this.setFromData(r.data);
19551
19552                 var close = this.closeTriggerEl();
19553
19554                 if(close){
19555                     close.show();
19556                 }
19557
19558                 this.hideTouchView();
19559
19560                 this.fireEvent('select', this, r, rowIndex);
19561
19562                 return;
19563             }
19564
19565             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19566                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19567                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19568                 return;
19569             }
19570
19571             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19572             this.addItem(r.data);
19573             this.tickItems.push(r.data);
19574         }
19575     },
19576     
19577     getAutoCreateNativeIOS : function()
19578     {
19579         var cfg = {
19580             cls: 'form-group' //input-group,
19581         };
19582         
19583         var combobox =  {
19584             tag: 'select',
19585             cls : 'roo-ios-select'
19586         };
19587         
19588         if (this.name) {
19589             combobox.name = this.name;
19590         }
19591         
19592         if (this.disabled) {
19593             combobox.disabled = true;
19594         }
19595         
19596         var settings = this;
19597         
19598         ['xs','sm','md','lg'].map(function(size){
19599             if (settings[size]) {
19600                 cfg.cls += ' col-' + size + '-' + settings[size];
19601             }
19602         });
19603         
19604         cfg.cn = combobox;
19605         
19606         return cfg;
19607         
19608     },
19609     
19610     initIOSView : function()
19611     {
19612         this.store.on('load', this.onIOSViewLoad, this);
19613         
19614         return;
19615     },
19616     
19617     onIOSViewLoad : function()
19618     {
19619         if(this.store.getCount() < 1){
19620             return;
19621         }
19622         
19623         this.clearIOSView();
19624         
19625         if(this.allowBlank) {
19626             
19627             var default_text = '-- SELECT --';
19628             
19629             if(this.placeholder.length){
19630                 default_text = this.placeholder;
19631             }
19632             
19633             if(this.emptyTitle.length){
19634                 default_text += ' - ' + this.emptyTitle + ' -';
19635             }
19636             
19637             var opt = this.inputEl().createChild({
19638                 tag: 'option',
19639                 value : 0,
19640                 html : default_text
19641             });
19642             
19643             var o = {};
19644             o[this.valueField] = 0;
19645             o[this.displayField] = default_text;
19646             
19647             this.ios_options.push({
19648                 data : o,
19649                 el : opt
19650             });
19651             
19652         }
19653         
19654         this.store.data.each(function(d, rowIndex){
19655             
19656             var html = '';
19657             
19658             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19659                 html = d.data[this.displayField];
19660             }
19661             
19662             var value = '';
19663             
19664             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19665                 value = d.data[this.valueField];
19666             }
19667             
19668             var option = {
19669                 tag: 'option',
19670                 value : value,
19671                 html : html
19672             };
19673             
19674             if(this.value == d.data[this.valueField]){
19675                 option['selected'] = true;
19676             }
19677             
19678             var opt = this.inputEl().createChild(option);
19679             
19680             this.ios_options.push({
19681                 data : d.data,
19682                 el : opt
19683             });
19684             
19685         }, this);
19686         
19687         this.inputEl().on('change', function(){
19688            this.fireEvent('select', this);
19689         }, this);
19690         
19691     },
19692     
19693     clearIOSView: function()
19694     {
19695         this.inputEl().dom.innerHTML = '';
19696         
19697         this.ios_options = [];
19698     },
19699     
19700     setIOSValue: function(v)
19701     {
19702         this.value = v;
19703         
19704         if(!this.ios_options){
19705             return;
19706         }
19707         
19708         Roo.each(this.ios_options, function(opts){
19709            
19710            opts.el.dom.removeAttribute('selected');
19711            
19712            if(opts.data[this.valueField] != v){
19713                return;
19714            }
19715            
19716            opts.el.dom.setAttribute('selected', true);
19717            
19718         }, this);
19719     }
19720
19721     /** 
19722     * @cfg {Boolean} grow 
19723     * @hide 
19724     */
19725     /** 
19726     * @cfg {Number} growMin 
19727     * @hide 
19728     */
19729     /** 
19730     * @cfg {Number} growMax 
19731     * @hide 
19732     */
19733     /**
19734      * @hide
19735      * @method autoSize
19736      */
19737 });
19738
19739 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19740     
19741     header : {
19742         tag: 'div',
19743         cls: 'modal-header',
19744         cn: [
19745             {
19746                 tag: 'h4',
19747                 cls: 'modal-title'
19748             }
19749         ]
19750     },
19751     
19752     body : {
19753         tag: 'div',
19754         cls: 'modal-body',
19755         cn: [
19756             {
19757                 tag: 'ul',
19758                 cls: 'list-group'
19759             }
19760         ]
19761     },
19762     
19763     listItemRadio : {
19764         tag: 'li',
19765         cls: 'list-group-item',
19766         cn: [
19767             {
19768                 tag: 'span',
19769                 cls: 'roo-combobox-list-group-item-value'
19770             },
19771             {
19772                 tag: 'div',
19773                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19774                 cn: [
19775                     {
19776                         tag: 'input',
19777                         type: 'radio'
19778                     },
19779                     {
19780                         tag: 'label'
19781                     }
19782                 ]
19783             }
19784         ]
19785     },
19786     
19787     listItemCheckbox : {
19788         tag: 'li',
19789         cls: 'list-group-item',
19790         cn: [
19791             {
19792                 tag: 'span',
19793                 cls: 'roo-combobox-list-group-item-value'
19794             },
19795             {
19796                 tag: 'div',
19797                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19798                 cn: [
19799                     {
19800                         tag: 'input',
19801                         type: 'checkbox'
19802                     },
19803                     {
19804                         tag: 'label'
19805                     }
19806                 ]
19807             }
19808         ]
19809     },
19810     
19811     emptyResult : {
19812         tag: 'div',
19813         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19814     },
19815     
19816     footer : {
19817         tag: 'div',
19818         cls: 'modal-footer',
19819         cn: [
19820             {
19821                 tag: 'div',
19822                 cls: 'row',
19823                 cn: [
19824                     {
19825                         tag: 'div',
19826                         cls: 'col-xs-6 text-left',
19827                         cn: {
19828                             tag: 'button',
19829                             cls: 'btn btn-danger roo-touch-view-cancel',
19830                             html: 'Cancel'
19831                         }
19832                     },
19833                     {
19834                         tag: 'div',
19835                         cls: 'col-xs-6 text-right',
19836                         cn: {
19837                             tag: 'button',
19838                             cls: 'btn btn-success roo-touch-view-ok',
19839                             html: 'OK'
19840                         }
19841                     }
19842                 ]
19843             }
19844         ]
19845         
19846     }
19847 });
19848
19849 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19850     
19851     touchViewTemplate : {
19852         tag: 'div',
19853         cls: 'modal fade roo-combobox-touch-view',
19854         cn: [
19855             {
19856                 tag: 'div',
19857                 cls: 'modal-dialog',
19858                 style : 'position:fixed', // we have to fix position....
19859                 cn: [
19860                     {
19861                         tag: 'div',
19862                         cls: 'modal-content',
19863                         cn: [
19864                             Roo.bootstrap.form.ComboBox.header,
19865                             Roo.bootstrap.form.ComboBox.body,
19866                             Roo.bootstrap.form.ComboBox.footer
19867                         ]
19868                     }
19869                 ]
19870             }
19871         ]
19872     }
19873 });/*
19874  * Based on:
19875  * Ext JS Library 1.1.1
19876  * Copyright(c) 2006-2007, Ext JS, LLC.
19877  *
19878  * Originally Released Under LGPL - original licence link has changed is not relivant.
19879  *
19880  * Fork - LGPL
19881  * <script type="text/javascript">
19882  */
19883
19884 /**
19885  * @class Roo.View
19886  * @extends Roo.util.Observable
19887  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19888  * This class also supports single and multi selection modes. <br>
19889  * Create a data model bound view:
19890  <pre><code>
19891  var store = new Roo.data.Store(...);
19892
19893  var view = new Roo.View({
19894     el : "my-element",
19895     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19896  
19897     singleSelect: true,
19898     selectedClass: "ydataview-selected",
19899     store: store
19900  });
19901
19902  // listen for node click?
19903  view.on("click", function(vw, index, node, e){
19904  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19905  });
19906
19907  // load XML data
19908  dataModel.load("foobar.xml");
19909  </code></pre>
19910  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19911  * <br><br>
19912  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19913  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19914  * 
19915  * Note: old style constructor is still suported (container, template, config)
19916  * 
19917  * @constructor
19918  * Create a new View
19919  * @param {Object} config The config object
19920  * 
19921  */
19922 Roo.View = function(config, depreciated_tpl, depreciated_config){
19923     
19924     this.parent = false;
19925     
19926     if (typeof(depreciated_tpl) == 'undefined') {
19927         // new way.. - universal constructor.
19928         Roo.apply(this, config);
19929         this.el  = Roo.get(this.el);
19930     } else {
19931         // old format..
19932         this.el  = Roo.get(config);
19933         this.tpl = depreciated_tpl;
19934         Roo.apply(this, depreciated_config);
19935     }
19936     this.wrapEl  = this.el.wrap().wrap();
19937     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19938     
19939     
19940     if(typeof(this.tpl) == "string"){
19941         this.tpl = new Roo.Template(this.tpl);
19942     } else {
19943         // support xtype ctors..
19944         this.tpl = new Roo.factory(this.tpl, Roo);
19945     }
19946     
19947     
19948     this.tpl.compile();
19949     
19950     /** @private */
19951     this.addEvents({
19952         /**
19953          * @event beforeclick
19954          * Fires before a click is processed. Returns false to cancel the default action.
19955          * @param {Roo.View} this
19956          * @param {Number} index The index of the target node
19957          * @param {HTMLElement} node The target node
19958          * @param {Roo.EventObject} e The raw event object
19959          */
19960             "beforeclick" : true,
19961         /**
19962          * @event click
19963          * Fires when a template node is clicked.
19964          * @param {Roo.View} this
19965          * @param {Number} index The index of the target node
19966          * @param {HTMLElement} node The target node
19967          * @param {Roo.EventObject} e The raw event object
19968          */
19969             "click" : true,
19970         /**
19971          * @event dblclick
19972          * Fires when a template node is double clicked.
19973          * @param {Roo.View} this
19974          * @param {Number} index The index of the target node
19975          * @param {HTMLElement} node The target node
19976          * @param {Roo.EventObject} e The raw event object
19977          */
19978             "dblclick" : true,
19979         /**
19980          * @event contextmenu
19981          * Fires when a template node is right clicked.
19982          * @param {Roo.View} this
19983          * @param {Number} index The index of the target node
19984          * @param {HTMLElement} node The target node
19985          * @param {Roo.EventObject} e The raw event object
19986          */
19987             "contextmenu" : true,
19988         /**
19989          * @event selectionchange
19990          * Fires when the selected nodes change.
19991          * @param {Roo.View} this
19992          * @param {Array} selections Array of the selected nodes
19993          */
19994             "selectionchange" : true,
19995     
19996         /**
19997          * @event beforeselect
19998          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19999          * @param {Roo.View} this
20000          * @param {HTMLElement} node The node to be selected
20001          * @param {Array} selections Array of currently selected nodes
20002          */
20003             "beforeselect" : true,
20004         /**
20005          * @event preparedata
20006          * Fires on every row to render, to allow you to change the data.
20007          * @param {Roo.View} this
20008          * @param {Object} data to be rendered (change this)
20009          */
20010           "preparedata" : true
20011           
20012           
20013         });
20014
20015
20016
20017     this.el.on({
20018         "click": this.onClick,
20019         "dblclick": this.onDblClick,
20020         "contextmenu": this.onContextMenu,
20021         scope:this
20022     });
20023
20024     this.selections = [];
20025     this.nodes = [];
20026     this.cmp = new Roo.CompositeElementLite([]);
20027     if(this.store){
20028         this.store = Roo.factory(this.store, Roo.data);
20029         this.setStore(this.store, true);
20030     }
20031     
20032     if ( this.footer && this.footer.xtype) {
20033            
20034          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20035         
20036         this.footer.dataSource = this.store;
20037         this.footer.container = fctr;
20038         this.footer = Roo.factory(this.footer, Roo);
20039         fctr.insertFirst(this.el);
20040         
20041         // this is a bit insane - as the paging toolbar seems to detach the el..
20042 //        dom.parentNode.parentNode.parentNode
20043          // they get detached?
20044     }
20045     
20046     
20047     Roo.View.superclass.constructor.call(this);
20048     
20049     
20050 };
20051
20052 Roo.extend(Roo.View, Roo.util.Observable, {
20053     
20054      /**
20055      * @cfg {Roo.data.Store} store Data store to load data from.
20056      */
20057     store : false,
20058     
20059     /**
20060      * @cfg {String|Roo.Element} el The container element.
20061      */
20062     el : '',
20063     
20064     /**
20065      * @cfg {String|Roo.Template} tpl The template used by this View 
20066      */
20067     tpl : false,
20068     /**
20069      * @cfg {String} dataName the named area of the template to use as the data area
20070      *                          Works with domtemplates roo-name="name"
20071      */
20072     dataName: false,
20073     /**
20074      * @cfg {String} selectedClass The css class to add to selected nodes
20075      */
20076     selectedClass : "x-view-selected",
20077      /**
20078      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20079      */
20080     emptyText : "",
20081     
20082     /**
20083      * @cfg {String} text to display on mask (default Loading)
20084      */
20085     mask : false,
20086     /**
20087      * @cfg {Boolean} multiSelect Allow multiple selection
20088      */
20089     multiSelect : false,
20090     /**
20091      * @cfg {Boolean} singleSelect Allow single selection
20092      */
20093     singleSelect:  false,
20094     
20095     /**
20096      * @cfg {Boolean} toggleSelect - selecting 
20097      */
20098     toggleSelect : false,
20099     
20100     /**
20101      * @cfg {Boolean} tickable - selecting 
20102      */
20103     tickable : false,
20104     
20105     /**
20106      * Returns the element this view is bound to.
20107      * @return {Roo.Element}
20108      */
20109     getEl : function(){
20110         return this.wrapEl;
20111     },
20112     
20113     
20114
20115     /**
20116      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20117      */
20118     refresh : function(){
20119         //Roo.log('refresh');
20120         var t = this.tpl;
20121         
20122         // if we are using something like 'domtemplate', then
20123         // the what gets used is:
20124         // t.applySubtemplate(NAME, data, wrapping data..)
20125         // the outer template then get' applied with
20126         //     the store 'extra data'
20127         // and the body get's added to the
20128         //      roo-name="data" node?
20129         //      <span class='roo-tpl-{name}'></span> ?????
20130         
20131         
20132         
20133         this.clearSelections();
20134         this.el.update("");
20135         var html = [];
20136         var records = this.store.getRange();
20137         if(records.length < 1) {
20138             
20139             // is this valid??  = should it render a template??
20140             
20141             this.el.update(this.emptyText);
20142             return;
20143         }
20144         var el = this.el;
20145         if (this.dataName) {
20146             this.el.update(t.apply(this.store.meta)); //????
20147             el = this.el.child('.roo-tpl-' + this.dataName);
20148         }
20149         
20150         for(var i = 0, len = records.length; i < len; i++){
20151             var data = this.prepareData(records[i].data, i, records[i]);
20152             this.fireEvent("preparedata", this, data, i, records[i]);
20153             
20154             var d = Roo.apply({}, data);
20155             
20156             if(this.tickable){
20157                 Roo.apply(d, {'roo-id' : Roo.id()});
20158                 
20159                 var _this = this;
20160             
20161                 Roo.each(this.parent.item, function(item){
20162                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20163                         return;
20164                     }
20165                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20166                 });
20167             }
20168             
20169             html[html.length] = Roo.util.Format.trim(
20170                 this.dataName ?
20171                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20172                     t.apply(d)
20173             );
20174         }
20175         
20176         
20177         
20178         el.update(html.join(""));
20179         this.nodes = el.dom.childNodes;
20180         this.updateIndexes(0);
20181     },
20182     
20183
20184     /**
20185      * Function to override to reformat the data that is sent to
20186      * the template for each node.
20187      * DEPRICATED - use the preparedata event handler.
20188      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20189      * a JSON object for an UpdateManager bound view).
20190      */
20191     prepareData : function(data, index, record)
20192     {
20193         this.fireEvent("preparedata", this, data, index, record);
20194         return data;
20195     },
20196
20197     onUpdate : function(ds, record){
20198         // Roo.log('on update');   
20199         this.clearSelections();
20200         var index = this.store.indexOf(record);
20201         var n = this.nodes[index];
20202         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20203         n.parentNode.removeChild(n);
20204         this.updateIndexes(index, index);
20205     },
20206
20207     
20208     
20209 // --------- FIXME     
20210     onAdd : function(ds, records, index)
20211     {
20212         //Roo.log(['on Add', ds, records, index] );        
20213         this.clearSelections();
20214         if(this.nodes.length == 0){
20215             this.refresh();
20216             return;
20217         }
20218         var n = this.nodes[index];
20219         for(var i = 0, len = records.length; i < len; i++){
20220             var d = this.prepareData(records[i].data, i, records[i]);
20221             if(n){
20222                 this.tpl.insertBefore(n, d);
20223             }else{
20224                 
20225                 this.tpl.append(this.el, d);
20226             }
20227         }
20228         this.updateIndexes(index);
20229     },
20230
20231     onRemove : function(ds, record, index){
20232        // Roo.log('onRemove');
20233         this.clearSelections();
20234         var el = this.dataName  ?
20235             this.el.child('.roo-tpl-' + this.dataName) :
20236             this.el; 
20237         
20238         el.dom.removeChild(this.nodes[index]);
20239         this.updateIndexes(index);
20240     },
20241
20242     /**
20243      * Refresh an individual node.
20244      * @param {Number} index
20245      */
20246     refreshNode : function(index){
20247         this.onUpdate(this.store, this.store.getAt(index));
20248     },
20249
20250     updateIndexes : function(startIndex, endIndex){
20251         var ns = this.nodes;
20252         startIndex = startIndex || 0;
20253         endIndex = endIndex || ns.length - 1;
20254         for(var i = startIndex; i <= endIndex; i++){
20255             ns[i].nodeIndex = i;
20256         }
20257     },
20258
20259     /**
20260      * Changes the data store this view uses and refresh the view.
20261      * @param {Store} store
20262      */
20263     setStore : function(store, initial){
20264         if(!initial && this.store){
20265             this.store.un("datachanged", this.refresh);
20266             this.store.un("add", this.onAdd);
20267             this.store.un("remove", this.onRemove);
20268             this.store.un("update", this.onUpdate);
20269             this.store.un("clear", this.refresh);
20270             this.store.un("beforeload", this.onBeforeLoad);
20271             this.store.un("load", this.onLoad);
20272             this.store.un("loadexception", this.onLoad);
20273         }
20274         if(store){
20275           
20276             store.on("datachanged", this.refresh, this);
20277             store.on("add", this.onAdd, this);
20278             store.on("remove", this.onRemove, this);
20279             store.on("update", this.onUpdate, this);
20280             store.on("clear", this.refresh, this);
20281             store.on("beforeload", this.onBeforeLoad, this);
20282             store.on("load", this.onLoad, this);
20283             store.on("loadexception", this.onLoad, this);
20284         }
20285         
20286         if(store){
20287             this.refresh();
20288         }
20289     },
20290     /**
20291      * onbeforeLoad - masks the loading area.
20292      *
20293      */
20294     onBeforeLoad : function(store,opts)
20295     {
20296          //Roo.log('onBeforeLoad');   
20297         if (!opts.add) {
20298             this.el.update("");
20299         }
20300         this.el.mask(this.mask ? this.mask : "Loading" ); 
20301     },
20302     onLoad : function ()
20303     {
20304         this.el.unmask();
20305     },
20306     
20307
20308     /**
20309      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20310      * @param {HTMLElement} node
20311      * @return {HTMLElement} The template node
20312      */
20313     findItemFromChild : function(node){
20314         var el = this.dataName  ?
20315             this.el.child('.roo-tpl-' + this.dataName,true) :
20316             this.el.dom; 
20317         
20318         if(!node || node.parentNode == el){
20319                     return node;
20320             }
20321             var p = node.parentNode;
20322             while(p && p != el){
20323             if(p.parentNode == el){
20324                 return p;
20325             }
20326             p = p.parentNode;
20327         }
20328             return null;
20329     },
20330
20331     /** @ignore */
20332     onClick : function(e){
20333         var item = this.findItemFromChild(e.getTarget());
20334         if(item){
20335             var index = this.indexOf(item);
20336             if(this.onItemClick(item, index, e) !== false){
20337                 this.fireEvent("click", this, index, item, e);
20338             }
20339         }else{
20340             this.clearSelections();
20341         }
20342     },
20343
20344     /** @ignore */
20345     onContextMenu : function(e){
20346         var item = this.findItemFromChild(e.getTarget());
20347         if(item){
20348             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20349         }
20350     },
20351
20352     /** @ignore */
20353     onDblClick : function(e){
20354         var item = this.findItemFromChild(e.getTarget());
20355         if(item){
20356             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20357         }
20358     },
20359
20360     onItemClick : function(item, index, e)
20361     {
20362         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20363             return false;
20364         }
20365         if (this.toggleSelect) {
20366             var m = this.isSelected(item) ? 'unselect' : 'select';
20367             //Roo.log(m);
20368             var _t = this;
20369             _t[m](item, true, false);
20370             return true;
20371         }
20372         if(this.multiSelect || this.singleSelect){
20373             if(this.multiSelect && e.shiftKey && this.lastSelection){
20374                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20375             }else{
20376                 this.select(item, this.multiSelect && e.ctrlKey);
20377                 this.lastSelection = item;
20378             }
20379             
20380             if(!this.tickable){
20381                 e.preventDefault();
20382             }
20383             
20384         }
20385         return true;
20386     },
20387
20388     /**
20389      * Get the number of selected nodes.
20390      * @return {Number}
20391      */
20392     getSelectionCount : function(){
20393         return this.selections.length;
20394     },
20395
20396     /**
20397      * Get the currently selected nodes.
20398      * @return {Array} An array of HTMLElements
20399      */
20400     getSelectedNodes : function(){
20401         return this.selections;
20402     },
20403
20404     /**
20405      * Get the indexes of the selected nodes.
20406      * @return {Array}
20407      */
20408     getSelectedIndexes : function(){
20409         var indexes = [], s = this.selections;
20410         for(var i = 0, len = s.length; i < len; i++){
20411             indexes.push(s[i].nodeIndex);
20412         }
20413         return indexes;
20414     },
20415
20416     /**
20417      * Clear all selections
20418      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20419      */
20420     clearSelections : function(suppressEvent){
20421         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20422             this.cmp.elements = this.selections;
20423             this.cmp.removeClass(this.selectedClass);
20424             this.selections = [];
20425             if(!suppressEvent){
20426                 this.fireEvent("selectionchange", this, this.selections);
20427             }
20428         }
20429     },
20430
20431     /**
20432      * Returns true if the passed node is selected
20433      * @param {HTMLElement/Number} node The node or node index
20434      * @return {Boolean}
20435      */
20436     isSelected : function(node){
20437         var s = this.selections;
20438         if(s.length < 1){
20439             return false;
20440         }
20441         node = this.getNode(node);
20442         return s.indexOf(node) !== -1;
20443     },
20444
20445     /**
20446      * Selects nodes.
20447      * @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
20448      * @param {Boolean} keepExisting (optional) true to keep existing selections
20449      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20450      */
20451     select : function(nodeInfo, keepExisting, suppressEvent){
20452         if(nodeInfo instanceof Array){
20453             if(!keepExisting){
20454                 this.clearSelections(true);
20455             }
20456             for(var i = 0, len = nodeInfo.length; i < len; i++){
20457                 this.select(nodeInfo[i], true, true);
20458             }
20459             return;
20460         } 
20461         var node = this.getNode(nodeInfo);
20462         if(!node || this.isSelected(node)){
20463             return; // already selected.
20464         }
20465         if(!keepExisting){
20466             this.clearSelections(true);
20467         }
20468         
20469         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20470             Roo.fly(node).addClass(this.selectedClass);
20471             this.selections.push(node);
20472             if(!suppressEvent){
20473                 this.fireEvent("selectionchange", this, this.selections);
20474             }
20475         }
20476         
20477         
20478     },
20479       /**
20480      * Unselects nodes.
20481      * @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
20482      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20484      */
20485     unselect : function(nodeInfo, keepExisting, suppressEvent)
20486     {
20487         if(nodeInfo instanceof Array){
20488             Roo.each(this.selections, function(s) {
20489                 this.unselect(s, nodeInfo);
20490             }, this);
20491             return;
20492         }
20493         var node = this.getNode(nodeInfo);
20494         if(!node || !this.isSelected(node)){
20495             //Roo.log("not selected");
20496             return; // not selected.
20497         }
20498         // fireevent???
20499         var ns = [];
20500         Roo.each(this.selections, function(s) {
20501             if (s == node ) {
20502                 Roo.fly(node).removeClass(this.selectedClass);
20503
20504                 return;
20505             }
20506             ns.push(s);
20507         },this);
20508         
20509         this.selections= ns;
20510         this.fireEvent("selectionchange", this, this.selections);
20511     },
20512
20513     /**
20514      * Gets a template node.
20515      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20516      * @return {HTMLElement} The node or null if it wasn't found
20517      */
20518     getNode : function(nodeInfo){
20519         if(typeof nodeInfo == "string"){
20520             return document.getElementById(nodeInfo);
20521         }else if(typeof nodeInfo == "number"){
20522             return this.nodes[nodeInfo];
20523         }
20524         return nodeInfo;
20525     },
20526
20527     /**
20528      * Gets a range template nodes.
20529      * @param {Number} startIndex
20530      * @param {Number} endIndex
20531      * @return {Array} An array of nodes
20532      */
20533     getNodes : function(start, end){
20534         var ns = this.nodes;
20535         start = start || 0;
20536         end = typeof end == "undefined" ? ns.length - 1 : end;
20537         var nodes = [];
20538         if(start <= end){
20539             for(var i = start; i <= end; i++){
20540                 nodes.push(ns[i]);
20541             }
20542         } else{
20543             for(var i = start; i >= end; i--){
20544                 nodes.push(ns[i]);
20545             }
20546         }
20547         return nodes;
20548     },
20549
20550     /**
20551      * Finds the index of the passed node
20552      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20553      * @return {Number} The index of the node or -1
20554      */
20555     indexOf : function(node){
20556         node = this.getNode(node);
20557         if(typeof node.nodeIndex == "number"){
20558             return node.nodeIndex;
20559         }
20560         var ns = this.nodes;
20561         for(var i = 0, len = ns.length; i < len; i++){
20562             if(ns[i] == node){
20563                 return i;
20564             }
20565         }
20566         return -1;
20567     }
20568 });
20569 /*
20570  * - LGPL
20571  *
20572  * based on jquery fullcalendar
20573  * 
20574  */
20575
20576 Roo.bootstrap = Roo.bootstrap || {};
20577 /**
20578  * @class Roo.bootstrap.Calendar
20579  * @extends Roo.bootstrap.Component
20580  * Bootstrap Calendar class
20581  * @cfg {Boolean} loadMask (true|false) default false
20582  * @cfg {Object} header generate the user specific header of the calendar, default false
20583
20584  * @constructor
20585  * Create a new Container
20586  * @param {Object} config The config object
20587  */
20588
20589
20590
20591 Roo.bootstrap.Calendar = function(config){
20592     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20593      this.addEvents({
20594         /**
20595              * @event select
20596              * Fires when a date is selected
20597              * @param {DatePicker} this
20598              * @param {Date} date The selected date
20599              */
20600         'select': true,
20601         /**
20602              * @event monthchange
20603              * Fires when the displayed month changes 
20604              * @param {DatePicker} this
20605              * @param {Date} date The selected month
20606              */
20607         'monthchange': true,
20608         /**
20609              * @event evententer
20610              * Fires when mouse over an event
20611              * @param {Calendar} this
20612              * @param {event} Event
20613              */
20614         'evententer': true,
20615         /**
20616              * @event eventleave
20617              * Fires when the mouse leaves an
20618              * @param {Calendar} this
20619              * @param {event}
20620              */
20621         'eventleave': true,
20622         /**
20623              * @event eventclick
20624              * Fires when the mouse click an
20625              * @param {Calendar} this
20626              * @param {event}
20627              */
20628         'eventclick': true
20629         
20630     });
20631
20632 };
20633
20634 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20635     
20636           /**
20637      * @cfg {Roo.data.Store} store
20638      * The data source for the calendar
20639      */
20640         store : false,
20641      /**
20642      * @cfg {Number} startDay
20643      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20644      */
20645     startDay : 0,
20646     
20647     loadMask : false,
20648     
20649     header : false,
20650       
20651     getAutoCreate : function(){
20652         
20653         
20654         var fc_button = function(name, corner, style, content ) {
20655             return Roo.apply({},{
20656                 tag : 'span',
20657                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20658                          (corner.length ?
20659                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20660                             ''
20661                         ),
20662                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20663                 unselectable: 'on'
20664             });
20665         };
20666         
20667         var header = {};
20668         
20669         if(!this.header){
20670             header = {
20671                 tag : 'table',
20672                 cls : 'fc-header',
20673                 style : 'width:100%',
20674                 cn : [
20675                     {
20676                         tag: 'tr',
20677                         cn : [
20678                             {
20679                                 tag : 'td',
20680                                 cls : 'fc-header-left',
20681                                 cn : [
20682                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20683                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20684                                     { tag: 'span', cls: 'fc-header-space' },
20685                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20686
20687
20688                                 ]
20689                             },
20690
20691                             {
20692                                 tag : 'td',
20693                                 cls : 'fc-header-center',
20694                                 cn : [
20695                                     {
20696                                         tag: 'span',
20697                                         cls: 'fc-header-title',
20698                                         cn : {
20699                                             tag: 'H2',
20700                                             html : 'month / year'
20701                                         }
20702                                     }
20703
20704                                 ]
20705                             },
20706                             {
20707                                 tag : 'td',
20708                                 cls : 'fc-header-right',
20709                                 cn : [
20710                               /*      fc_button('month', 'left', '', 'month' ),
20711                                     fc_button('week', '', '', 'week' ),
20712                                     fc_button('day', 'right', '', 'day' )
20713                                 */    
20714
20715                                 ]
20716                             }
20717
20718                         ]
20719                     }
20720                 ]
20721             };
20722         }
20723         
20724         header = this.header;
20725         
20726        
20727         var cal_heads = function() {
20728             var ret = [];
20729             // fixme - handle this.
20730             
20731             for (var i =0; i < Date.dayNames.length; i++) {
20732                 var d = Date.dayNames[i];
20733                 ret.push({
20734                     tag: 'th',
20735                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20736                     html : d.substring(0,3)
20737                 });
20738                 
20739             }
20740             ret[0].cls += ' fc-first';
20741             ret[6].cls += ' fc-last';
20742             return ret;
20743         };
20744         var cal_cell = function(n) {
20745             return  {
20746                 tag: 'td',
20747                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20748                 cn : [
20749                     {
20750                         cn : [
20751                             {
20752                                 cls: 'fc-day-number',
20753                                 html: 'D'
20754                             },
20755                             {
20756                                 cls: 'fc-day-content',
20757                              
20758                                 cn : [
20759                                      {
20760                                         style: 'position: relative;' // height: 17px;
20761                                     }
20762                                 ]
20763                             }
20764                             
20765                             
20766                         ]
20767                     }
20768                 ]
20769                 
20770             }
20771         };
20772         var cal_rows = function() {
20773             
20774             var ret = [];
20775             for (var r = 0; r < 6; r++) {
20776                 var row= {
20777                     tag : 'tr',
20778                     cls : 'fc-week',
20779                     cn : []
20780                 };
20781                 
20782                 for (var i =0; i < Date.dayNames.length; i++) {
20783                     var d = Date.dayNames[i];
20784                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20785
20786                 }
20787                 row.cn[0].cls+=' fc-first';
20788                 row.cn[0].cn[0].style = 'min-height:90px';
20789                 row.cn[6].cls+=' fc-last';
20790                 ret.push(row);
20791                 
20792             }
20793             ret[0].cls += ' fc-first';
20794             ret[4].cls += ' fc-prev-last';
20795             ret[5].cls += ' fc-last';
20796             return ret;
20797             
20798         };
20799         
20800         var cal_table = {
20801             tag: 'table',
20802             cls: 'fc-border-separate',
20803             style : 'width:100%',
20804             cellspacing  : 0,
20805             cn : [
20806                 { 
20807                     tag: 'thead',
20808                     cn : [
20809                         { 
20810                             tag: 'tr',
20811                             cls : 'fc-first fc-last',
20812                             cn : cal_heads()
20813                         }
20814                     ]
20815                 },
20816                 { 
20817                     tag: 'tbody',
20818                     cn : cal_rows()
20819                 }
20820                   
20821             ]
20822         };
20823          
20824          var cfg = {
20825             cls : 'fc fc-ltr',
20826             cn : [
20827                 header,
20828                 {
20829                     cls : 'fc-content',
20830                     style : "position: relative;",
20831                     cn : [
20832                         {
20833                             cls : 'fc-view fc-view-month fc-grid',
20834                             style : 'position: relative',
20835                             unselectable : 'on',
20836                             cn : [
20837                                 {
20838                                     cls : 'fc-event-container',
20839                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20840                                 },
20841                                 cal_table
20842                             ]
20843                         }
20844                     ]
20845     
20846                 }
20847            ] 
20848             
20849         };
20850         
20851          
20852         
20853         return cfg;
20854     },
20855     
20856     
20857     initEvents : function()
20858     {
20859         if(!this.store){
20860             throw "can not find store for calendar";
20861         }
20862         
20863         var mark = {
20864             tag: "div",
20865             cls:"x-dlg-mask",
20866             style: "text-align:center",
20867             cn: [
20868                 {
20869                     tag: "div",
20870                     style: "background-color:white;width:50%;margin:250 auto",
20871                     cn: [
20872                         {
20873                             tag: "img",
20874                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20875                         },
20876                         {
20877                             tag: "span",
20878                             html: "Loading"
20879                         }
20880                         
20881                     ]
20882                 }
20883             ]
20884         };
20885         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20886         
20887         var size = this.el.select('.fc-content', true).first().getSize();
20888         this.maskEl.setSize(size.width, size.height);
20889         this.maskEl.enableDisplayMode("block");
20890         if(!this.loadMask){
20891             this.maskEl.hide();
20892         }
20893         
20894         this.store = Roo.factory(this.store, Roo.data);
20895         this.store.on('load', this.onLoad, this);
20896         this.store.on('beforeload', this.onBeforeLoad, this);
20897         
20898         this.resize();
20899         
20900         this.cells = this.el.select('.fc-day',true);
20901         //Roo.log(this.cells);
20902         this.textNodes = this.el.query('.fc-day-number');
20903         this.cells.addClassOnOver('fc-state-hover');
20904         
20905         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20906         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20907         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20908         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20909         
20910         this.on('monthchange', this.onMonthChange, this);
20911         
20912         this.update(new Date().clearTime());
20913     },
20914     
20915     resize : function() {
20916         var sz  = this.el.getSize();
20917         
20918         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20919         this.el.select('.fc-day-content div',true).setHeight(34);
20920     },
20921     
20922     
20923     // private
20924     showPrevMonth : function(e){
20925         this.update(this.activeDate.add("mo", -1));
20926     },
20927     showToday : function(e){
20928         this.update(new Date().clearTime());
20929     },
20930     // private
20931     showNextMonth : function(e){
20932         this.update(this.activeDate.add("mo", 1));
20933     },
20934
20935     // private
20936     showPrevYear : function(){
20937         this.update(this.activeDate.add("y", -1));
20938     },
20939
20940     // private
20941     showNextYear : function(){
20942         this.update(this.activeDate.add("y", 1));
20943     },
20944
20945     
20946    // private
20947     update : function(date)
20948     {
20949         var vd = this.activeDate;
20950         this.activeDate = date;
20951 //        if(vd && this.el){
20952 //            var t = date.getTime();
20953 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20954 //                Roo.log('using add remove');
20955 //                
20956 //                this.fireEvent('monthchange', this, date);
20957 //                
20958 //                this.cells.removeClass("fc-state-highlight");
20959 //                this.cells.each(function(c){
20960 //                   if(c.dateValue == t){
20961 //                       c.addClass("fc-state-highlight");
20962 //                       setTimeout(function(){
20963 //                            try{c.dom.firstChild.focus();}catch(e){}
20964 //                       }, 50);
20965 //                       return false;
20966 //                   }
20967 //                   return true;
20968 //                });
20969 //                return;
20970 //            }
20971 //        }
20972         
20973         var days = date.getDaysInMonth();
20974         
20975         var firstOfMonth = date.getFirstDateOfMonth();
20976         var startingPos = firstOfMonth.getDay()-this.startDay;
20977         
20978         if(startingPos < this.startDay){
20979             startingPos += 7;
20980         }
20981         
20982         var pm = date.add(Date.MONTH, -1);
20983         var prevStart = pm.getDaysInMonth()-startingPos;
20984 //        
20985         this.cells = this.el.select('.fc-day',true);
20986         this.textNodes = this.el.query('.fc-day-number');
20987         this.cells.addClassOnOver('fc-state-hover');
20988         
20989         var cells = this.cells.elements;
20990         var textEls = this.textNodes;
20991         
20992         Roo.each(cells, function(cell){
20993             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20994         });
20995         
20996         days += startingPos;
20997
20998         // convert everything to numbers so it's fast
20999         var day = 86400000;
21000         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21001         //Roo.log(d);
21002         //Roo.log(pm);
21003         //Roo.log(prevStart);
21004         
21005         var today = new Date().clearTime().getTime();
21006         var sel = date.clearTime().getTime();
21007         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21008         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21009         var ddMatch = this.disabledDatesRE;
21010         var ddText = this.disabledDatesText;
21011         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21012         var ddaysText = this.disabledDaysText;
21013         var format = this.format;
21014         
21015         var setCellClass = function(cal, cell){
21016             cell.row = 0;
21017             cell.events = [];
21018             cell.more = [];
21019             //Roo.log('set Cell Class');
21020             cell.title = "";
21021             var t = d.getTime();
21022             
21023             //Roo.log(d);
21024             
21025             cell.dateValue = t;
21026             if(t == today){
21027                 cell.className += " fc-today";
21028                 cell.className += " fc-state-highlight";
21029                 cell.title = cal.todayText;
21030             }
21031             if(t == sel){
21032                 // disable highlight in other month..
21033                 //cell.className += " fc-state-highlight";
21034                 
21035             }
21036             // disabling
21037             if(t < min) {
21038                 cell.className = " fc-state-disabled";
21039                 cell.title = cal.minText;
21040                 return;
21041             }
21042             if(t > max) {
21043                 cell.className = " fc-state-disabled";
21044                 cell.title = cal.maxText;
21045                 return;
21046             }
21047             if(ddays){
21048                 if(ddays.indexOf(d.getDay()) != -1){
21049                     cell.title = ddaysText;
21050                     cell.className = " fc-state-disabled";
21051                 }
21052             }
21053             if(ddMatch && format){
21054                 var fvalue = d.dateFormat(format);
21055                 if(ddMatch.test(fvalue)){
21056                     cell.title = ddText.replace("%0", fvalue);
21057                     cell.className = " fc-state-disabled";
21058                 }
21059             }
21060             
21061             if (!cell.initialClassName) {
21062                 cell.initialClassName = cell.dom.className;
21063             }
21064             
21065             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21066         };
21067
21068         var i = 0;
21069         
21070         for(; i < startingPos; i++) {
21071             textEls[i].innerHTML = (++prevStart);
21072             d.setDate(d.getDate()+1);
21073             
21074             cells[i].className = "fc-past fc-other-month";
21075             setCellClass(this, cells[i]);
21076         }
21077         
21078         var intDay = 0;
21079         
21080         for(; i < days; i++){
21081             intDay = i - startingPos + 1;
21082             textEls[i].innerHTML = (intDay);
21083             d.setDate(d.getDate()+1);
21084             
21085             cells[i].className = ''; // "x-date-active";
21086             setCellClass(this, cells[i]);
21087         }
21088         var extraDays = 0;
21089         
21090         for(; i < 42; i++) {
21091             textEls[i].innerHTML = (++extraDays);
21092             d.setDate(d.getDate()+1);
21093             
21094             cells[i].className = "fc-future fc-other-month";
21095             setCellClass(this, cells[i]);
21096         }
21097         
21098         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21099         
21100         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21101         
21102         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21103         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21104         
21105         if(totalRows != 6){
21106             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21107             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21108         }
21109         
21110         this.fireEvent('monthchange', this, date);
21111         
21112         
21113         /*
21114         if(!this.internalRender){
21115             var main = this.el.dom.firstChild;
21116             var w = main.offsetWidth;
21117             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21118             Roo.fly(main).setWidth(w);
21119             this.internalRender = true;
21120             // opera does not respect the auto grow header center column
21121             // then, after it gets a width opera refuses to recalculate
21122             // without a second pass
21123             if(Roo.isOpera && !this.secondPass){
21124                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21125                 this.secondPass = true;
21126                 this.update.defer(10, this, [date]);
21127             }
21128         }
21129         */
21130         
21131     },
21132     
21133     findCell : function(dt) {
21134         dt = dt.clearTime().getTime();
21135         var ret = false;
21136         this.cells.each(function(c){
21137             //Roo.log("check " +c.dateValue + '?=' + dt);
21138             if(c.dateValue == dt){
21139                 ret = c;
21140                 return false;
21141             }
21142             return true;
21143         });
21144         
21145         return ret;
21146     },
21147     
21148     findCells : function(ev) {
21149         var s = ev.start.clone().clearTime().getTime();
21150        // Roo.log(s);
21151         var e= ev.end.clone().clearTime().getTime();
21152        // Roo.log(e);
21153         var ret = [];
21154         this.cells.each(function(c){
21155              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21156             
21157             if(c.dateValue > e){
21158                 return ;
21159             }
21160             if(c.dateValue < s){
21161                 return ;
21162             }
21163             ret.push(c);
21164         });
21165         
21166         return ret;    
21167     },
21168     
21169 //    findBestRow: function(cells)
21170 //    {
21171 //        var ret = 0;
21172 //        
21173 //        for (var i =0 ; i < cells.length;i++) {
21174 //            ret  = Math.max(cells[i].rows || 0,ret);
21175 //        }
21176 //        return ret;
21177 //        
21178 //    },
21179     
21180     
21181     addItem : function(ev)
21182     {
21183         // look for vertical location slot in
21184         var cells = this.findCells(ev);
21185         
21186 //        ev.row = this.findBestRow(cells);
21187         
21188         // work out the location.
21189         
21190         var crow = false;
21191         var rows = [];
21192         for(var i =0; i < cells.length; i++) {
21193             
21194             cells[i].row = cells[0].row;
21195             
21196             if(i == 0){
21197                 cells[i].row = cells[i].row + 1;
21198             }
21199             
21200             if (!crow) {
21201                 crow = {
21202                     start : cells[i],
21203                     end :  cells[i]
21204                 };
21205                 continue;
21206             }
21207             if (crow.start.getY() == cells[i].getY()) {
21208                 // on same row.
21209                 crow.end = cells[i];
21210                 continue;
21211             }
21212             // different row.
21213             rows.push(crow);
21214             crow = {
21215                 start: cells[i],
21216                 end : cells[i]
21217             };
21218             
21219         }
21220         
21221         rows.push(crow);
21222         ev.els = [];
21223         ev.rows = rows;
21224         ev.cells = cells;
21225         
21226         cells[0].events.push(ev);
21227         
21228         this.calevents.push(ev);
21229     },
21230     
21231     clearEvents: function() {
21232         
21233         if(!this.calevents){
21234             return;
21235         }
21236         
21237         Roo.each(this.cells.elements, function(c){
21238             c.row = 0;
21239             c.events = [];
21240             c.more = [];
21241         });
21242         
21243         Roo.each(this.calevents, function(e) {
21244             Roo.each(e.els, function(el) {
21245                 el.un('mouseenter' ,this.onEventEnter, this);
21246                 el.un('mouseleave' ,this.onEventLeave, this);
21247                 el.remove();
21248             },this);
21249         },this);
21250         
21251         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21252             e.remove();
21253         });
21254         
21255     },
21256     
21257     renderEvents: function()
21258     {   
21259         var _this = this;
21260         
21261         this.cells.each(function(c) {
21262             
21263             if(c.row < 5){
21264                 return;
21265             }
21266             
21267             var ev = c.events;
21268             
21269             var r = 4;
21270             if(c.row != c.events.length){
21271                 r = 4 - (4 - (c.row - c.events.length));
21272             }
21273             
21274             c.events = ev.slice(0, r);
21275             c.more = ev.slice(r);
21276             
21277             if(c.more.length && c.more.length == 1){
21278                 c.events.push(c.more.pop());
21279             }
21280             
21281             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21282             
21283         });
21284             
21285         this.cells.each(function(c) {
21286             
21287             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21288             
21289             
21290             for (var e = 0; e < c.events.length; e++){
21291                 var ev = c.events[e];
21292                 var rows = ev.rows;
21293                 
21294                 for(var i = 0; i < rows.length; i++) {
21295                 
21296                     // how many rows should it span..
21297
21298                     var  cfg = {
21299                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21300                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21301
21302                         unselectable : "on",
21303                         cn : [
21304                             {
21305                                 cls: 'fc-event-inner',
21306                                 cn : [
21307     //                                {
21308     //                                  tag:'span',
21309     //                                  cls: 'fc-event-time',
21310     //                                  html : cells.length > 1 ? '' : ev.time
21311     //                                },
21312                                     {
21313                                       tag:'span',
21314                                       cls: 'fc-event-title',
21315                                       html : String.format('{0}', ev.title)
21316                                     }
21317
21318
21319                                 ]
21320                             },
21321                             {
21322                                 cls: 'ui-resizable-handle ui-resizable-e',
21323                                 html : '&nbsp;&nbsp;&nbsp'
21324                             }
21325
21326                         ]
21327                     };
21328
21329                     if (i == 0) {
21330                         cfg.cls += ' fc-event-start';
21331                     }
21332                     if ((i+1) == rows.length) {
21333                         cfg.cls += ' fc-event-end';
21334                     }
21335
21336                     var ctr = _this.el.select('.fc-event-container',true).first();
21337                     var cg = ctr.createChild(cfg);
21338
21339                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21340                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21341
21342                     var r = (c.more.length) ? 1 : 0;
21343                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21344                     cg.setWidth(ebox.right - sbox.x -2);
21345
21346                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21347                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21348                     cg.on('click', _this.onEventClick, _this, ev);
21349
21350                     ev.els.push(cg);
21351                     
21352                 }
21353                 
21354             }
21355             
21356             
21357             if(c.more.length){
21358                 var  cfg = {
21359                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21360                     style : 'position: absolute',
21361                     unselectable : "on",
21362                     cn : [
21363                         {
21364                             cls: 'fc-event-inner',
21365                             cn : [
21366                                 {
21367                                   tag:'span',
21368                                   cls: 'fc-event-title',
21369                                   html : 'More'
21370                                 }
21371
21372
21373                             ]
21374                         },
21375                         {
21376                             cls: 'ui-resizable-handle ui-resizable-e',
21377                             html : '&nbsp;&nbsp;&nbsp'
21378                         }
21379
21380                     ]
21381                 };
21382
21383                 var ctr = _this.el.select('.fc-event-container',true).first();
21384                 var cg = ctr.createChild(cfg);
21385
21386                 var sbox = c.select('.fc-day-content',true).first().getBox();
21387                 var ebox = c.select('.fc-day-content',true).first().getBox();
21388                 //Roo.log(cg);
21389                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21390                 cg.setWidth(ebox.right - sbox.x -2);
21391
21392                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21393                 
21394             }
21395             
21396         });
21397         
21398         
21399         
21400     },
21401     
21402     onEventEnter: function (e, el,event,d) {
21403         this.fireEvent('evententer', this, el, event);
21404     },
21405     
21406     onEventLeave: function (e, el,event,d) {
21407         this.fireEvent('eventleave', this, el, event);
21408     },
21409     
21410     onEventClick: function (e, el,event,d) {
21411         this.fireEvent('eventclick', this, el, event);
21412     },
21413     
21414     onMonthChange: function () {
21415         this.store.load();
21416     },
21417     
21418     onMoreEventClick: function(e, el, more)
21419     {
21420         var _this = this;
21421         
21422         this.calpopover.placement = 'right';
21423         this.calpopover.setTitle('More');
21424         
21425         this.calpopover.setContent('');
21426         
21427         var ctr = this.calpopover.el.select('.popover-content', true).first();
21428         
21429         Roo.each(more, function(m){
21430             var cfg = {
21431                 cls : 'fc-event-hori fc-event-draggable',
21432                 html : m.title
21433             };
21434             var cg = ctr.createChild(cfg);
21435             
21436             cg.on('click', _this.onEventClick, _this, m);
21437         });
21438         
21439         this.calpopover.show(el);
21440         
21441         
21442     },
21443     
21444     onLoad: function () 
21445     {   
21446         this.calevents = [];
21447         var cal = this;
21448         
21449         if(this.store.getCount() > 0){
21450             this.store.data.each(function(d){
21451                cal.addItem({
21452                     id : d.data.id,
21453                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21454                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21455                     time : d.data.start_time,
21456                     title : d.data.title,
21457                     description : d.data.description,
21458                     venue : d.data.venue
21459                 });
21460             });
21461         }
21462         
21463         this.renderEvents();
21464         
21465         if(this.calevents.length && this.loadMask){
21466             this.maskEl.hide();
21467         }
21468     },
21469     
21470     onBeforeLoad: function()
21471     {
21472         this.clearEvents();
21473         if(this.loadMask){
21474             this.maskEl.show();
21475         }
21476     }
21477 });
21478
21479  
21480  /*
21481  * - LGPL
21482  *
21483  * element
21484  * 
21485  */
21486
21487 /**
21488  * @class Roo.bootstrap.Popover
21489  * @extends Roo.bootstrap.Component
21490  * @parent none builder
21491  * @children Roo.bootstrap.Component
21492  * Bootstrap Popover class
21493  * @cfg {String} html contents of the popover   (or false to use children..)
21494  * @cfg {String} title of popover (or false to hide)
21495  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21496  * @cfg {String} trigger click || hover (or false to trigger manually)
21497  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21498  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21499  *      - if false and it has a 'parent' then it will be automatically added to that element
21500  *      - if string - Roo.get  will be called 
21501  * @cfg {Number} delay - delay before showing
21502  
21503  * @constructor
21504  * Create a new Popover
21505  * @param {Object} config The config object
21506  */
21507
21508 Roo.bootstrap.Popover = function(config){
21509     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21510     
21511     this.addEvents({
21512         // raw events
21513          /**
21514          * @event show
21515          * After the popover show
21516          * 
21517          * @param {Roo.bootstrap.Popover} this
21518          */
21519         "show" : true,
21520         /**
21521          * @event hide
21522          * After the popover hide
21523          * 
21524          * @param {Roo.bootstrap.Popover} this
21525          */
21526         "hide" : true
21527     });
21528 };
21529
21530 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21531     
21532     title: false,
21533     html: false,
21534     
21535     placement : 'right',
21536     trigger : 'hover', // hover
21537     modal : false,
21538     delay : 0,
21539     
21540     over: false,
21541     
21542     can_build_overlaid : false,
21543     
21544     maskEl : false, // the mask element
21545     headerEl : false,
21546     contentEl : false,
21547     alignEl : false, // when show is called with an element - this get's stored.
21548     
21549     getChildContainer : function()
21550     {
21551         return this.contentEl;
21552         
21553     },
21554     getPopoverHeader : function()
21555     {
21556         this.title = true; // flag not to hide it..
21557         this.headerEl.addClass('p-0');
21558         return this.headerEl
21559     },
21560     
21561     
21562     getAutoCreate : function(){
21563          
21564         var cfg = {
21565            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21566            style: 'display:block',
21567            cn : [
21568                 {
21569                     cls : 'arrow'
21570                 },
21571                 {
21572                     cls : 'popover-inner ',
21573                     cn : [
21574                         {
21575                             tag: 'h3',
21576                             cls: 'popover-title popover-header',
21577                             html : this.title === false ? '' : this.title
21578                         },
21579                         {
21580                             cls : 'popover-content popover-body '  + (this.cls || ''),
21581                             html : this.html || ''
21582                         }
21583                     ]
21584                     
21585                 }
21586            ]
21587         };
21588         
21589         return cfg;
21590     },
21591     /**
21592      * @param {string} the title
21593      */
21594     setTitle: function(str)
21595     {
21596         this.title = str;
21597         if (this.el) {
21598             this.headerEl.dom.innerHTML = str;
21599         }
21600         
21601     },
21602     /**
21603      * @param {string} the body content
21604      */
21605     setContent: function(str)
21606     {
21607         this.html = str;
21608         if (this.contentEl) {
21609             this.contentEl.dom.innerHTML = str;
21610         }
21611         
21612     },
21613     // as it get's added to the bottom of the page.
21614     onRender : function(ct, position)
21615     {
21616         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21617         
21618         
21619         
21620         if(!this.el){
21621             var cfg = Roo.apply({},  this.getAutoCreate());
21622             cfg.id = Roo.id();
21623             
21624             if (this.cls) {
21625                 cfg.cls += ' ' + this.cls;
21626             }
21627             if (this.style) {
21628                 cfg.style = this.style;
21629             }
21630             //Roo.log("adding to ");
21631             this.el = Roo.get(document.body).createChild(cfg, position);
21632 //            Roo.log(this.el);
21633         }
21634         
21635         this.contentEl = this.el.select('.popover-content',true).first();
21636         this.headerEl =  this.el.select('.popover-title',true).first();
21637         
21638         var nitems = [];
21639         if(typeof(this.items) != 'undefined'){
21640             var items = this.items;
21641             delete this.items;
21642
21643             for(var i =0;i < items.length;i++) {
21644                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21645             }
21646         }
21647
21648         this.items = nitems;
21649         
21650         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21651         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21652         
21653         
21654         
21655         this.initEvents();
21656     },
21657     
21658     resizeMask : function()
21659     {
21660         this.maskEl.setSize(
21661             Roo.lib.Dom.getViewWidth(true),
21662             Roo.lib.Dom.getViewHeight(true)
21663         );
21664     },
21665     
21666     initEvents : function()
21667     {
21668         
21669         if (!this.modal) { 
21670             Roo.bootstrap.Popover.register(this);
21671         }
21672          
21673         this.arrowEl = this.el.select('.arrow',true).first();
21674         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21675         this.el.enableDisplayMode('block');
21676         this.el.hide();
21677  
21678         
21679         if (this.over === false && !this.parent()) {
21680             return; 
21681         }
21682         if (this.triggers === false) {
21683             return;
21684         }
21685          
21686         // support parent
21687         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21688         var triggers = this.trigger ? this.trigger.split(' ') : [];
21689         Roo.each(triggers, function(trigger) {
21690         
21691             if (trigger == 'click') {
21692                 on_el.on('click', this.toggle, this);
21693             } else if (trigger != 'manual') {
21694                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21695                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21696       
21697                 on_el.on(eventIn  ,this.enter, this);
21698                 on_el.on(eventOut, this.leave, this);
21699             }
21700         }, this);
21701     },
21702     
21703     
21704     // private
21705     timeout : null,
21706     hoverState : null,
21707     
21708     toggle : function () {
21709         this.hoverState == 'in' ? this.leave() : this.enter();
21710     },
21711     
21712     enter : function () {
21713         
21714         clearTimeout(this.timeout);
21715     
21716         this.hoverState = 'in';
21717     
21718         if (!this.delay || !this.delay.show) {
21719             this.show();
21720             return;
21721         }
21722         var _t = this;
21723         this.timeout = setTimeout(function () {
21724             if (_t.hoverState == 'in') {
21725                 _t.show();
21726             }
21727         }, this.delay.show)
21728     },
21729     
21730     leave : function() {
21731         clearTimeout(this.timeout);
21732     
21733         this.hoverState = 'out';
21734     
21735         if (!this.delay || !this.delay.hide) {
21736             this.hide();
21737             return;
21738         }
21739         var _t = this;
21740         this.timeout = setTimeout(function () {
21741             if (_t.hoverState == 'out') {
21742                 _t.hide();
21743             }
21744         }, this.delay.hide)
21745     },
21746     
21747     /**
21748      * update the position of the dialog
21749      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21750      * 
21751      *
21752      */
21753     
21754     doAlign : function()
21755     {
21756         
21757         if (this.alignEl) {
21758             this.updatePosition(this.placement, true);
21759              
21760         } else {
21761             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21762             var es = this.el.getSize();
21763             var x = Roo.lib.Dom.getViewWidth()/2;
21764             var y = Roo.lib.Dom.getViewHeight()/2;
21765             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21766             
21767         }
21768
21769          
21770          
21771         
21772         
21773     },
21774     
21775     /**
21776      * Show the popover
21777      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21778      * @param {string} (left|right|top|bottom) position
21779      */
21780     show : function (on_el, placement)
21781     {
21782         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21783         on_el = on_el || false; // default to false
21784          
21785         if (!on_el) {
21786             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21787                 on_el = this.parent().el;
21788             } else if (this.over) {
21789                 on_el = Roo.get(this.over);
21790             }
21791             
21792         }
21793         
21794         this.alignEl = Roo.get( on_el );
21795
21796         if (!this.el) {
21797             this.render(document.body);
21798         }
21799         
21800         
21801          
21802         
21803         if (this.title === false) {
21804             this.headerEl.hide();
21805         }
21806         
21807        
21808         this.el.show();
21809         this.el.dom.style.display = 'block';
21810          
21811         this.doAlign();
21812         
21813         //var arrow = this.el.select('.arrow',true).first();
21814         //arrow.set(align[2], 
21815         
21816         this.el.addClass('in');
21817         
21818          
21819         
21820         this.hoverState = 'in';
21821         
21822         if (this.modal) {
21823             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21824             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21825             this.maskEl.dom.style.display = 'block';
21826             this.maskEl.addClass('show');
21827         }
21828         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21829  
21830         this.fireEvent('show', this);
21831         
21832     },
21833     /**
21834      * fire this manually after loading a grid in the table for example
21835      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21836      * @param {Boolean} try and move it if we cant get right position.
21837      */
21838     updatePosition : function(placement, try_move)
21839     {
21840         // allow for calling with no parameters
21841         placement = placement   ? placement :  this.placement;
21842         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21843         
21844         this.el.removeClass([
21845             'fade','top','bottom', 'left', 'right','in',
21846             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21847         ]);
21848         this.el.addClass(placement + ' bs-popover-' + placement);
21849         
21850         if (!this.alignEl ) {
21851             return false;
21852         }
21853         
21854         switch (placement) {
21855             case 'right':
21856                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21857                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21858                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21859                     //normal display... or moved up/down.
21860                     this.el.setXY(offset);
21861                     var xy = this.alignEl.getAnchorXY('tr', false);
21862                     xy[0]+=2;xy[1]+=5;
21863                     this.arrowEl.setXY(xy);
21864                     return true;
21865                 }
21866                 // continue through...
21867                 return this.updatePosition('left', false);
21868                 
21869             
21870             case 'left':
21871                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21872                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21873                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21874                     //normal display... or moved up/down.
21875                     this.el.setXY(offset);
21876                     var xy = this.alignEl.getAnchorXY('tl', false);
21877                     xy[0]-=10;xy[1]+=5; // << fix me
21878                     this.arrowEl.setXY(xy);
21879                     return true;
21880                 }
21881                 // call self...
21882                 return this.updatePosition('right', false);
21883             
21884             case 'top':
21885                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21886                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21887                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21888                     //normal display... or moved up/down.
21889                     this.el.setXY(offset);
21890                     var xy = this.alignEl.getAnchorXY('t', false);
21891                     xy[1]-=10; // << fix me
21892                     this.arrowEl.setXY(xy);
21893                     return true;
21894                 }
21895                 // fall through
21896                return this.updatePosition('bottom', false);
21897             
21898             case 'bottom':
21899                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21900                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21901                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21902                     //normal display... or moved up/down.
21903                     this.el.setXY(offset);
21904                     var xy = this.alignEl.getAnchorXY('b', false);
21905                      xy[1]+=2; // << fix me
21906                     this.arrowEl.setXY(xy);
21907                     return true;
21908                 }
21909                 // fall through
21910                 return this.updatePosition('top', false);
21911                 
21912             
21913         }
21914         
21915         
21916         return false;
21917     },
21918     
21919     hide : function()
21920     {
21921         this.el.setXY([0,0]);
21922         this.el.removeClass('in');
21923         this.el.hide();
21924         this.hoverState = null;
21925         this.maskEl.hide(); // always..
21926         this.fireEvent('hide', this);
21927     }
21928     
21929 });
21930
21931
21932 Roo.apply(Roo.bootstrap.Popover, {
21933
21934     alignment : {
21935         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21936         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21937         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21938         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21939     },
21940     
21941     zIndex : 20001,
21942
21943     clickHander : false,
21944     
21945     
21946
21947     onMouseDown : function(e)
21948     {
21949         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21950             /// what is nothing is showing..
21951             this.hideAll();
21952         }
21953          
21954     },
21955     
21956     
21957     popups : [],
21958     
21959     register : function(popup)
21960     {
21961         if (!Roo.bootstrap.Popover.clickHandler) {
21962             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21963         }
21964         // hide other popups.
21965         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21966         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21967         this.hideAll(); //<< why?
21968         //this.popups.push(popup);
21969     },
21970     hideAll : function()
21971     {
21972         this.popups.forEach(function(p) {
21973             p.hide();
21974         });
21975     },
21976     onShow : function() {
21977         Roo.bootstrap.Popover.popups.push(this);
21978     },
21979     onHide : function() {
21980         Roo.bootstrap.Popover.popups.remove(this);
21981     } 
21982
21983 });
21984 /**
21985  * @class Roo.bootstrap.PopoverNav
21986  * @extends Roo.bootstrap.nav.Simplebar
21987  * @parent Roo.bootstrap.Popover
21988  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21989  * @licence LGPL
21990  * Bootstrap Popover header navigation class
21991  * FIXME? should this go under nav?
21992  *
21993  * 
21994  * @constructor
21995  * Create a new Popover Header Navigation 
21996  * @param {Object} config The config object
21997  */
21998
21999 Roo.bootstrap.PopoverNav = function(config){
22000     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22001 };
22002
22003 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22004     
22005     
22006     container_method : 'getPopoverHeader' 
22007     
22008      
22009     
22010     
22011    
22012 });
22013
22014  
22015
22016  /*
22017  * - LGPL
22018  *
22019  * Progress
22020  * 
22021  */
22022
22023 /**
22024  * @class Roo.bootstrap.Progress
22025  * @extends Roo.bootstrap.Component
22026  * @children Roo.bootstrap.ProgressBar
22027  * Bootstrap Progress class
22028  * @cfg {Boolean} striped striped of the progress bar
22029  * @cfg {Boolean} active animated of the progress bar
22030  * 
22031  * 
22032  * @constructor
22033  * Create a new Progress
22034  * @param {Object} config The config object
22035  */
22036
22037 Roo.bootstrap.Progress = function(config){
22038     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22039 };
22040
22041 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22042     
22043     striped : false,
22044     active: false,
22045     
22046     getAutoCreate : function(){
22047         var cfg = {
22048             tag: 'div',
22049             cls: 'progress'
22050         };
22051         
22052         
22053         if(this.striped){
22054             cfg.cls += ' progress-striped';
22055         }
22056       
22057         if(this.active){
22058             cfg.cls += ' active';
22059         }
22060         
22061         
22062         return cfg;
22063     }
22064    
22065 });
22066
22067  
22068
22069  /*
22070  * - LGPL
22071  *
22072  * ProgressBar
22073  * 
22074  */
22075
22076 /**
22077  * @class Roo.bootstrap.ProgressBar
22078  * @extends Roo.bootstrap.Component
22079  * Bootstrap ProgressBar class
22080  * @cfg {Number} aria_valuenow aria-value now
22081  * @cfg {Number} aria_valuemin aria-value min
22082  * @cfg {Number} aria_valuemax aria-value max
22083  * @cfg {String} label label for the progress bar
22084  * @cfg {String} panel (success | info | warning | danger )
22085  * @cfg {String} role role of the progress bar
22086  * @cfg {String} sr_only text
22087  * 
22088  * 
22089  * @constructor
22090  * Create a new ProgressBar
22091  * @param {Object} config The config object
22092  */
22093
22094 Roo.bootstrap.ProgressBar = function(config){
22095     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22096 };
22097
22098 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22099     
22100     aria_valuenow : 0,
22101     aria_valuemin : 0,
22102     aria_valuemax : 100,
22103     label : false,
22104     panel : false,
22105     role : false,
22106     sr_only: false,
22107     
22108     getAutoCreate : function()
22109     {
22110         
22111         var cfg = {
22112             tag: 'div',
22113             cls: 'progress-bar',
22114             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22115         };
22116         
22117         if(this.sr_only){
22118             cfg.cn = {
22119                 tag: 'span',
22120                 cls: 'sr-only',
22121                 html: this.sr_only
22122             }
22123         }
22124         
22125         if(this.role){
22126             cfg.role = this.role;
22127         }
22128         
22129         if(this.aria_valuenow){
22130             cfg['aria-valuenow'] = this.aria_valuenow;
22131         }
22132         
22133         if(this.aria_valuemin){
22134             cfg['aria-valuemin'] = this.aria_valuemin;
22135         }
22136         
22137         if(this.aria_valuemax){
22138             cfg['aria-valuemax'] = this.aria_valuemax;
22139         }
22140         
22141         if(this.label && !this.sr_only){
22142             cfg.html = this.label;
22143         }
22144         
22145         if(this.panel){
22146             cfg.cls += ' progress-bar-' + this.panel;
22147         }
22148         
22149         return cfg;
22150     },
22151     
22152     update : function(aria_valuenow)
22153     {
22154         this.aria_valuenow = aria_valuenow;
22155         
22156         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22157     }
22158    
22159 });
22160
22161  
22162
22163  /**
22164  * @class Roo.bootstrap.TabGroup
22165  * @extends Roo.bootstrap.Column
22166  * @children Roo.bootstrap.TabPanel
22167  * Bootstrap Column class
22168  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22169  * @cfg {Boolean} carousel true to make the group behave like a carousel
22170  * @cfg {Boolean} bullets show bullets for the panels
22171  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22172  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22173  * @cfg {Boolean} showarrow (true|false) show arrow default true
22174  * 
22175  * @constructor
22176  * Create a new TabGroup
22177  * @param {Object} config The config object
22178  */
22179
22180 Roo.bootstrap.TabGroup = function(config){
22181     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22182     if (!this.navId) {
22183         this.navId = Roo.id();
22184     }
22185     this.tabs = [];
22186     Roo.bootstrap.TabGroup.register(this);
22187     
22188 };
22189
22190 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22191     
22192     carousel : false,
22193     transition : false,
22194     bullets : 0,
22195     timer : 0,
22196     autoslide : false,
22197     slideFn : false,
22198     slideOnTouch : false,
22199     showarrow : true,
22200     
22201     getAutoCreate : function()
22202     {
22203         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22204         
22205         cfg.cls += ' tab-content';
22206         
22207         if (this.carousel) {
22208             cfg.cls += ' carousel slide';
22209             
22210             cfg.cn = [{
22211                cls : 'carousel-inner',
22212                cn : []
22213             }];
22214         
22215             if(this.bullets  && !Roo.isTouch){
22216                 
22217                 var bullets = {
22218                     cls : 'carousel-bullets',
22219                     cn : []
22220                 };
22221                
22222                 if(this.bullets_cls){
22223                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22224                 }
22225                 
22226                 bullets.cn.push({
22227                     cls : 'clear'
22228                 });
22229                 
22230                 cfg.cn[0].cn.push(bullets);
22231             }
22232             
22233             if(this.showarrow){
22234                 cfg.cn[0].cn.push({
22235                     tag : 'div',
22236                     class : 'carousel-arrow',
22237                     cn : [
22238                         {
22239                             tag : 'div',
22240                             class : 'carousel-prev',
22241                             cn : [
22242                                 {
22243                                     tag : 'i',
22244                                     class : 'fa fa-chevron-left'
22245                                 }
22246                             ]
22247                         },
22248                         {
22249                             tag : 'div',
22250                             class : 'carousel-next',
22251                             cn : [
22252                                 {
22253                                     tag : 'i',
22254                                     class : 'fa fa-chevron-right'
22255                                 }
22256                             ]
22257                         }
22258                     ]
22259                 });
22260             }
22261             
22262         }
22263         
22264         return cfg;
22265     },
22266     
22267     initEvents:  function()
22268     {
22269 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22270 //            this.el.on("touchstart", this.onTouchStart, this);
22271 //        }
22272         
22273         if(this.autoslide){
22274             var _this = this;
22275             
22276             this.slideFn = window.setInterval(function() {
22277                 _this.showPanelNext();
22278             }, this.timer);
22279         }
22280         
22281         if(this.showarrow){
22282             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22283             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22284         }
22285         
22286         
22287     },
22288     
22289 //    onTouchStart : function(e, el, o)
22290 //    {
22291 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22292 //            return;
22293 //        }
22294 //        
22295 //        this.showPanelNext();
22296 //    },
22297     
22298     
22299     getChildContainer : function()
22300     {
22301         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22302     },
22303     
22304     /**
22305     * register a Navigation item
22306     * @param {Roo.bootstrap.nav.Item} the navitem to add
22307     */
22308     register : function(item)
22309     {
22310         this.tabs.push( item);
22311         item.navId = this.navId; // not really needed..
22312         this.addBullet();
22313     
22314     },
22315     
22316     getActivePanel : function()
22317     {
22318         var r = false;
22319         Roo.each(this.tabs, function(t) {
22320             if (t.active) {
22321                 r = t;
22322                 return false;
22323             }
22324             return null;
22325         });
22326         return r;
22327         
22328     },
22329     getPanelByName : function(n)
22330     {
22331         var r = false;
22332         Roo.each(this.tabs, function(t) {
22333             if (t.tabId == n) {
22334                 r = t;
22335                 return false;
22336             }
22337             return null;
22338         });
22339         return r;
22340     },
22341     indexOfPanel : function(p)
22342     {
22343         var r = false;
22344         Roo.each(this.tabs, function(t,i) {
22345             if (t.tabId == p.tabId) {
22346                 r = i;
22347                 return false;
22348             }
22349             return null;
22350         });
22351         return r;
22352     },
22353     /**
22354      * show a specific panel
22355      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22356      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22357      */
22358     showPanel : function (pan)
22359     {
22360         if(this.transition || typeof(pan) == 'undefined'){
22361             Roo.log("waiting for the transitionend");
22362             return false;
22363         }
22364         
22365         if (typeof(pan) == 'number') {
22366             pan = this.tabs[pan];
22367         }
22368         
22369         if (typeof(pan) == 'string') {
22370             pan = this.getPanelByName(pan);
22371         }
22372         
22373         var cur = this.getActivePanel();
22374         
22375         if(!pan || !cur){
22376             Roo.log('pan or acitve pan is undefined');
22377             return false;
22378         }
22379         
22380         if (pan.tabId == this.getActivePanel().tabId) {
22381             return true;
22382         }
22383         
22384         if (false === cur.fireEvent('beforedeactivate')) {
22385             return false;
22386         }
22387         
22388         if(this.bullets > 0 && !Roo.isTouch){
22389             this.setActiveBullet(this.indexOfPanel(pan));
22390         }
22391         
22392         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22393             
22394             //class="carousel-item carousel-item-next carousel-item-left"
22395             
22396             this.transition = true;
22397             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22398             var lr = dir == 'next' ? 'left' : 'right';
22399             pan.el.addClass(dir); // or prev
22400             pan.el.addClass('carousel-item-' + dir); // or prev
22401             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22402             cur.el.addClass(lr); // or right
22403             pan.el.addClass(lr);
22404             cur.el.addClass('carousel-item-' +lr); // or right
22405             pan.el.addClass('carousel-item-' +lr);
22406             
22407             
22408             var _this = this;
22409             cur.el.on('transitionend', function() {
22410                 Roo.log("trans end?");
22411                 
22412                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22413                 pan.setActive(true);
22414                 
22415                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22416                 cur.setActive(false);
22417                 
22418                 _this.transition = false;
22419                 
22420             }, this, { single:  true } );
22421             
22422             return true;
22423         }
22424         
22425         cur.setActive(false);
22426         pan.setActive(true);
22427         
22428         return true;
22429         
22430     },
22431     showPanelNext : function()
22432     {
22433         var i = this.indexOfPanel(this.getActivePanel());
22434         
22435         if (i >= this.tabs.length - 1 && !this.autoslide) {
22436             return;
22437         }
22438         
22439         if (i >= this.tabs.length - 1 && this.autoslide) {
22440             i = -1;
22441         }
22442         
22443         this.showPanel(this.tabs[i+1]);
22444     },
22445     
22446     showPanelPrev : function()
22447     {
22448         var i = this.indexOfPanel(this.getActivePanel());
22449         
22450         if (i  < 1 && !this.autoslide) {
22451             return;
22452         }
22453         
22454         if (i < 1 && this.autoslide) {
22455             i = this.tabs.length;
22456         }
22457         
22458         this.showPanel(this.tabs[i-1]);
22459     },
22460     
22461     
22462     addBullet: function()
22463     {
22464         if(!this.bullets || Roo.isTouch){
22465             return;
22466         }
22467         var ctr = this.el.select('.carousel-bullets',true).first();
22468         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22469         var bullet = ctr.createChild({
22470             cls : 'bullet bullet-' + i
22471         },ctr.dom.lastChild);
22472         
22473         
22474         var _this = this;
22475         
22476         bullet.on('click', (function(e, el, o, ii, t){
22477
22478             e.preventDefault();
22479
22480             this.showPanel(ii);
22481
22482             if(this.autoslide && this.slideFn){
22483                 clearInterval(this.slideFn);
22484                 this.slideFn = window.setInterval(function() {
22485                     _this.showPanelNext();
22486                 }, this.timer);
22487             }
22488
22489         }).createDelegate(this, [i, bullet], true));
22490                 
22491         
22492     },
22493      
22494     setActiveBullet : function(i)
22495     {
22496         if(Roo.isTouch){
22497             return;
22498         }
22499         
22500         Roo.each(this.el.select('.bullet', true).elements, function(el){
22501             el.removeClass('selected');
22502         });
22503
22504         var bullet = this.el.select('.bullet-' + i, true).first();
22505         
22506         if(!bullet){
22507             return;
22508         }
22509         
22510         bullet.addClass('selected');
22511     }
22512     
22513     
22514   
22515 });
22516
22517  
22518
22519  
22520  
22521 Roo.apply(Roo.bootstrap.TabGroup, {
22522     
22523     groups: {},
22524      /**
22525     * register a Navigation Group
22526     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22527     */
22528     register : function(navgrp)
22529     {
22530         this.groups[navgrp.navId] = navgrp;
22531         
22532     },
22533     /**
22534     * fetch a Navigation Group based on the navigation ID
22535     * if one does not exist , it will get created.
22536     * @param {string} the navgroup to add
22537     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22538     */
22539     get: function(navId) {
22540         if (typeof(this.groups[navId]) == 'undefined') {
22541             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22542         }
22543         return this.groups[navId] ;
22544     }
22545     
22546     
22547     
22548 });
22549
22550  /*
22551  * - LGPL
22552  *
22553  * TabPanel
22554  * 
22555  */
22556
22557 /**
22558  * @class Roo.bootstrap.TabPanel
22559  * @extends Roo.bootstrap.Component
22560  * @children Roo.bootstrap.Component
22561  * Bootstrap TabPanel class
22562  * @cfg {Boolean} active panel active
22563  * @cfg {String} html panel content
22564  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22565  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22566  * @cfg {String} href click to link..
22567  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22568  * 
22569  * 
22570  * @constructor
22571  * Create a new TabPanel
22572  * @param {Object} config The config object
22573  */
22574
22575 Roo.bootstrap.TabPanel = function(config){
22576     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22577     this.addEvents({
22578         /**
22579              * @event changed
22580              * Fires when the active status changes
22581              * @param {Roo.bootstrap.TabPanel} this
22582              * @param {Boolean} state the new state
22583             
22584          */
22585         'changed': true,
22586         /**
22587              * @event beforedeactivate
22588              * Fires before a tab is de-activated - can be used to do validation on a form.
22589              * @param {Roo.bootstrap.TabPanel} this
22590              * @return {Boolean} false if there is an error
22591             
22592          */
22593         'beforedeactivate': true
22594      });
22595     
22596     this.tabId = this.tabId || Roo.id();
22597   
22598 };
22599
22600 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22601     
22602     active: false,
22603     html: false,
22604     tabId: false,
22605     navId : false,
22606     href : '',
22607     touchSlide : false,
22608     getAutoCreate : function(){
22609         
22610         
22611         var cfg = {
22612             tag: 'div',
22613             // item is needed for carousel - not sure if it has any effect otherwise
22614             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22615             html: this.html || ''
22616         };
22617         
22618         if(this.active){
22619             cfg.cls += ' active';
22620         }
22621         
22622         if(this.tabId){
22623             cfg.tabId = this.tabId;
22624         }
22625         
22626         
22627         
22628         return cfg;
22629     },
22630     
22631     initEvents:  function()
22632     {
22633         var p = this.parent();
22634         
22635         this.navId = this.navId || p.navId;
22636         
22637         if (typeof(this.navId) != 'undefined') {
22638             // not really needed.. but just in case.. parent should be a NavGroup.
22639             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22640             
22641             tg.register(this);
22642             
22643             var i = tg.tabs.length - 1;
22644             
22645             if(this.active && tg.bullets > 0 && i < tg.bullets){
22646                 tg.setActiveBullet(i);
22647             }
22648         }
22649         
22650         this.el.on('click', this.onClick, this);
22651         
22652         if(Roo.isTouch && this.touchSlide){
22653             this.el.on("touchstart", this.onTouchStart, this);
22654             this.el.on("touchmove", this.onTouchMove, this);
22655             this.el.on("touchend", this.onTouchEnd, this);
22656         }
22657         
22658     },
22659     
22660     onRender : function(ct, position)
22661     {
22662         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22663     },
22664     
22665     setActive : function(state)
22666     {
22667         Roo.log("panel - set active " + this.tabId + "=" + state);
22668         
22669         this.active = state;
22670         if (!state) {
22671             this.el.removeClass('active');
22672             
22673         } else  if (!this.el.hasClass('active')) {
22674             this.el.addClass('active');
22675         }
22676         
22677         this.fireEvent('changed', this, state);
22678     },
22679     
22680     onClick : function(e)
22681     {
22682         e.preventDefault();
22683         
22684         if(!this.href.length){
22685             return;
22686         }
22687         
22688         window.location.href = this.href;
22689     },
22690     
22691     startX : 0,
22692     startY : 0,
22693     endX : 0,
22694     endY : 0,
22695     swiping : false,
22696     
22697     onTouchStart : function(e)
22698     {
22699         this.swiping = false;
22700         
22701         this.startX = e.browserEvent.touches[0].clientX;
22702         this.startY = e.browserEvent.touches[0].clientY;
22703     },
22704     
22705     onTouchMove : function(e)
22706     {
22707         this.swiping = true;
22708         
22709         this.endX = e.browserEvent.touches[0].clientX;
22710         this.endY = e.browserEvent.touches[0].clientY;
22711     },
22712     
22713     onTouchEnd : function(e)
22714     {
22715         if(!this.swiping){
22716             this.onClick(e);
22717             return;
22718         }
22719         
22720         var tabGroup = this.parent();
22721         
22722         if(this.endX > this.startX){ // swiping right
22723             tabGroup.showPanelPrev();
22724             return;
22725         }
22726         
22727         if(this.startX > this.endX){ // swiping left
22728             tabGroup.showPanelNext();
22729             return;
22730         }
22731     }
22732     
22733     
22734 });
22735  
22736
22737  
22738
22739  /*
22740  * - LGPL
22741  *
22742  * DateField
22743  * 
22744  */
22745
22746 /**
22747  * @class Roo.bootstrap.form.DateField
22748  * @extends Roo.bootstrap.form.Input
22749  * Bootstrap DateField class
22750  * @cfg {Number} weekStart default 0
22751  * @cfg {String} viewMode default empty, (months|years)
22752  * @cfg {String} minViewMode default empty, (months|years)
22753  * @cfg {Number} startDate default -Infinity
22754  * @cfg {Number} endDate default Infinity
22755  * @cfg {Boolean} todayHighlight default false
22756  * @cfg {Boolean} todayBtn default false
22757  * @cfg {Boolean} calendarWeeks default false
22758  * @cfg {Object} daysOfWeekDisabled default empty
22759  * @cfg {Boolean} singleMode default false (true | false)
22760  * 
22761  * @cfg {Boolean} keyboardNavigation default true
22762  * @cfg {String} language default en
22763  * 
22764  * @constructor
22765  * Create a new DateField
22766  * @param {Object} config The config object
22767  */
22768
22769 Roo.bootstrap.form.DateField = function(config){
22770     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22771      this.addEvents({
22772             /**
22773              * @event show
22774              * Fires when this field show.
22775              * @param {Roo.bootstrap.form.DateField} this
22776              * @param {Mixed} date The date value
22777              */
22778             show : true,
22779             /**
22780              * @event show
22781              * Fires when this field hide.
22782              * @param {Roo.bootstrap.form.DateField} this
22783              * @param {Mixed} date The date value
22784              */
22785             hide : true,
22786             /**
22787              * @event select
22788              * Fires when select a date.
22789              * @param {Roo.bootstrap.form.DateField} this
22790              * @param {Mixed} date The date value
22791              */
22792             select : true,
22793             /**
22794              * @event beforeselect
22795              * Fires when before select a date.
22796              * @param {Roo.bootstrap.form.DateField} this
22797              * @param {Mixed} date The date value
22798              */
22799             beforeselect : true
22800         });
22801 };
22802
22803 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22804     
22805     /**
22806      * @cfg {String} format
22807      * The default date format string which can be overriden for localization support.  The format must be
22808      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22809      */
22810     format : "m/d/y",
22811     /**
22812      * @cfg {String} altFormats
22813      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22814      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22815      */
22816     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22817     
22818     weekStart : 0,
22819     
22820     viewMode : '',
22821     
22822     minViewMode : '',
22823     
22824     todayHighlight : false,
22825     
22826     todayBtn: false,
22827     
22828     language: 'en',
22829     
22830     keyboardNavigation: true,
22831     
22832     calendarWeeks: false,
22833     
22834     startDate: -Infinity,
22835     
22836     endDate: Infinity,
22837     
22838     daysOfWeekDisabled: [],
22839     
22840     _events: [],
22841     
22842     singleMode : false,
22843     
22844     UTCDate: function()
22845     {
22846         return new Date(Date.UTC.apply(Date, arguments));
22847     },
22848     
22849     UTCToday: function()
22850     {
22851         var today = new Date();
22852         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22853     },
22854     
22855     getDate: function() {
22856             var d = this.getUTCDate();
22857             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22858     },
22859     
22860     getUTCDate: function() {
22861             return this.date;
22862     },
22863     
22864     setDate: function(d) {
22865             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22866     },
22867     
22868     setUTCDate: function(d) {
22869             this.date = d;
22870             this.setValue(this.formatDate(this.date));
22871     },
22872         
22873     onRender: function(ct, position)
22874     {
22875         
22876         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22877         
22878         this.language = this.language || 'en';
22879         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22880         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22881         
22882         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22883         this.format = this.format || 'm/d/y';
22884         this.isInline = false;
22885         this.isInput = true;
22886         this.component = this.el.select('.add-on', true).first() || false;
22887         this.component = (this.component && this.component.length === 0) ? false : this.component;
22888         this.hasInput = this.component && this.inputEl().length;
22889         
22890         if (typeof(this.minViewMode === 'string')) {
22891             switch (this.minViewMode) {
22892                 case 'months':
22893                     this.minViewMode = 1;
22894                     break;
22895                 case 'years':
22896                     this.minViewMode = 2;
22897                     break;
22898                 default:
22899                     this.minViewMode = 0;
22900                     break;
22901             }
22902         }
22903         
22904         if (typeof(this.viewMode === 'string')) {
22905             switch (this.viewMode) {
22906                 case 'months':
22907                     this.viewMode = 1;
22908                     break;
22909                 case 'years':
22910                     this.viewMode = 2;
22911                     break;
22912                 default:
22913                     this.viewMode = 0;
22914                     break;
22915             }
22916         }
22917                 
22918         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22919         
22920 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22921         
22922         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22923         
22924         this.picker().on('mousedown', this.onMousedown, this);
22925         this.picker().on('click', this.onClick, this);
22926         
22927         this.picker().addClass('datepicker-dropdown');
22928         
22929         this.startViewMode = this.viewMode;
22930         
22931         if(this.singleMode){
22932             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22933                 v.setVisibilityMode(Roo.Element.DISPLAY);
22934                 v.hide();
22935             });
22936             
22937             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22938                 v.setStyle('width', '189px');
22939             });
22940         }
22941         
22942         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22943             if(!this.calendarWeeks){
22944                 v.remove();
22945                 return;
22946             }
22947             
22948             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22949             v.attr('colspan', function(i, val){
22950                 return parseInt(val) + 1;
22951             });
22952         });
22953                         
22954         
22955         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22956         
22957         this.setStartDate(this.startDate);
22958         this.setEndDate(this.endDate);
22959         
22960         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22961         
22962         this.fillDow();
22963         this.fillMonths();
22964         this.update();
22965         this.showMode();
22966         
22967         if(this.isInline) {
22968             this.showPopup();
22969         }
22970     },
22971     
22972     picker : function()
22973     {
22974         return this.pickerEl;
22975 //        return this.el.select('.datepicker', true).first();
22976     },
22977     
22978     fillDow: function()
22979     {
22980         var dowCnt = this.weekStart;
22981         
22982         var dow = {
22983             tag: 'tr',
22984             cn: [
22985                 
22986             ]
22987         };
22988         
22989         if(this.calendarWeeks){
22990             dow.cn.push({
22991                 tag: 'th',
22992                 cls: 'cw',
22993                 html: '&nbsp;'
22994             })
22995         }
22996         
22997         while (dowCnt < this.weekStart + 7) {
22998             dow.cn.push({
22999                 tag: 'th',
23000                 cls: 'dow',
23001                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23002             });
23003         }
23004         
23005         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23006     },
23007     
23008     fillMonths: function()
23009     {    
23010         var i = 0;
23011         var months = this.picker().select('>.datepicker-months td', true).first();
23012         
23013         months.dom.innerHTML = '';
23014         
23015         while (i < 12) {
23016             var month = {
23017                 tag: 'span',
23018                 cls: 'month',
23019                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23020             };
23021             
23022             months.createChild(month);
23023         }
23024         
23025     },
23026     
23027     update: function()
23028     {
23029         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;
23030         
23031         if (this.date < this.startDate) {
23032             this.viewDate = new Date(this.startDate);
23033         } else if (this.date > this.endDate) {
23034             this.viewDate = new Date(this.endDate);
23035         } else {
23036             this.viewDate = new Date(this.date);
23037         }
23038         
23039         this.fill();
23040     },
23041     
23042     fill: function() 
23043     {
23044         var d = new Date(this.viewDate),
23045                 year = d.getUTCFullYear(),
23046                 month = d.getUTCMonth(),
23047                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23048                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23049                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23050                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23051                 currentDate = this.date && this.date.valueOf(),
23052                 today = this.UTCToday();
23053         
23054         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23055         
23056 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23057         
23058 //        this.picker.select('>tfoot th.today').
23059 //                                              .text(dates[this.language].today)
23060 //                                              .toggle(this.todayBtn !== false);
23061     
23062         this.updateNavArrows();
23063         this.fillMonths();
23064                                                 
23065         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23066         
23067         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23068          
23069         prevMonth.setUTCDate(day);
23070         
23071         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23072         
23073         var nextMonth = new Date(prevMonth);
23074         
23075         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23076         
23077         nextMonth = nextMonth.valueOf();
23078         
23079         var fillMonths = false;
23080         
23081         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23082         
23083         while(prevMonth.valueOf() <= nextMonth) {
23084             var clsName = '';
23085             
23086             if (prevMonth.getUTCDay() === this.weekStart) {
23087                 if(fillMonths){
23088                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23089                 }
23090                     
23091                 fillMonths = {
23092                     tag: 'tr',
23093                     cn: []
23094                 };
23095                 
23096                 if(this.calendarWeeks){
23097                     // ISO 8601: First week contains first thursday.
23098                     // ISO also states week starts on Monday, but we can be more abstract here.
23099                     var
23100                     // Start of current week: based on weekstart/current date
23101                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23102                     // Thursday of this week
23103                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23104                     // First Thursday of year, year from thursday
23105                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23106                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23107                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23108                     
23109                     fillMonths.cn.push({
23110                         tag: 'td',
23111                         cls: 'cw',
23112                         html: calWeek
23113                     });
23114                 }
23115             }
23116             
23117             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23118                 clsName += ' old';
23119             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23120                 clsName += ' new';
23121             }
23122             if (this.todayHighlight &&
23123                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23124                 prevMonth.getUTCMonth() == today.getMonth() &&
23125                 prevMonth.getUTCDate() == today.getDate()) {
23126                 clsName += ' today';
23127             }
23128             
23129             if (currentDate && prevMonth.valueOf() === currentDate) {
23130                 clsName += ' active';
23131             }
23132             
23133             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23134                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23135                     clsName += ' disabled';
23136             }
23137             
23138             fillMonths.cn.push({
23139                 tag: 'td',
23140                 cls: 'day ' + clsName,
23141                 html: prevMonth.getDate()
23142             });
23143             
23144             prevMonth.setDate(prevMonth.getDate()+1);
23145         }
23146           
23147         var currentYear = this.date && this.date.getUTCFullYear();
23148         var currentMonth = this.date && this.date.getUTCMonth();
23149         
23150         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23151         
23152         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23153             v.removeClass('active');
23154             
23155             if(currentYear === year && k === currentMonth){
23156                 v.addClass('active');
23157             }
23158             
23159             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23160                 v.addClass('disabled');
23161             }
23162             
23163         });
23164         
23165         
23166         year = parseInt(year/10, 10) * 10;
23167         
23168         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23169         
23170         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23171         
23172         year -= 1;
23173         for (var i = -1; i < 11; i++) {
23174             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23175                 tag: 'span',
23176                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23177                 html: year
23178             });
23179             
23180             year += 1;
23181         }
23182     },
23183     
23184     showMode: function(dir) 
23185     {
23186         if (dir) {
23187             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23188         }
23189         
23190         Roo.each(this.picker().select('>div',true).elements, function(v){
23191             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23192             v.hide();
23193         });
23194         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23195     },
23196     
23197     place: function()
23198     {
23199         if(this.isInline) {
23200             return;
23201         }
23202         
23203         this.picker().removeClass(['bottom', 'top']);
23204         
23205         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23206             /*
23207              * place to the top of element!
23208              *
23209              */
23210             
23211             this.picker().addClass('top');
23212             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23213             
23214             return;
23215         }
23216         
23217         this.picker().addClass('bottom');
23218         
23219         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23220     },
23221     
23222     parseDate : function(value)
23223     {
23224         if(!value || value instanceof Date){
23225             return value;
23226         }
23227         var v = Date.parseDate(value, this.format);
23228         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23229             v = Date.parseDate(value, 'Y-m-d');
23230         }
23231         if(!v && this.altFormats){
23232             if(!this.altFormatsArray){
23233                 this.altFormatsArray = this.altFormats.split("|");
23234             }
23235             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23236                 v = Date.parseDate(value, this.altFormatsArray[i]);
23237             }
23238         }
23239         return v;
23240     },
23241     
23242     formatDate : function(date, fmt)
23243     {   
23244         return (!date || !(date instanceof Date)) ?
23245         date : date.dateFormat(fmt || this.format);
23246     },
23247     
23248     onFocus : function()
23249     {
23250         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23251         this.showPopup();
23252     },
23253     
23254     onBlur : function()
23255     {
23256         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23257         
23258         var d = this.inputEl().getValue();
23259         
23260         this.setValue(d);
23261                 
23262         this.hidePopup();
23263     },
23264     
23265     showPopup : function()
23266     {
23267         this.picker().show();
23268         this.update();
23269         this.place();
23270         
23271         this.fireEvent('showpopup', this, this.date);
23272     },
23273     
23274     hidePopup : function()
23275     {
23276         if(this.isInline) {
23277             return;
23278         }
23279         this.picker().hide();
23280         this.viewMode = this.startViewMode;
23281         this.showMode();
23282         
23283         this.fireEvent('hidepopup', this, this.date);
23284         
23285     },
23286     
23287     onMousedown: function(e)
23288     {
23289         e.stopPropagation();
23290         e.preventDefault();
23291     },
23292     
23293     keyup: function(e)
23294     {
23295         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23296         this.update();
23297     },
23298
23299     setValue: function(v)
23300     {
23301         if(this.fireEvent('beforeselect', this, v) !== false){
23302             var d = new Date(this.parseDate(v) ).clearTime();
23303         
23304             if(isNaN(d.getTime())){
23305                 this.date = this.viewDate = '';
23306                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23307                 return;
23308             }
23309
23310             v = this.formatDate(d);
23311
23312             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23313
23314             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23315
23316             this.update();
23317
23318             this.fireEvent('select', this, this.date);
23319         }
23320     },
23321     
23322     getValue: function()
23323     {
23324         return this.formatDate(this.date);
23325     },
23326     
23327     fireKey: function(e)
23328     {
23329         if (!this.picker().isVisible()){
23330             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23331                 this.showPopup();
23332             }
23333             return;
23334         }
23335         
23336         var dateChanged = false,
23337         dir, day, month,
23338         newDate, newViewDate;
23339         
23340         switch(e.keyCode){
23341             case 27: // escape
23342                 this.hidePopup();
23343                 e.preventDefault();
23344                 break;
23345             case 37: // left
23346             case 39: // right
23347                 if (!this.keyboardNavigation) {
23348                     break;
23349                 }
23350                 dir = e.keyCode == 37 ? -1 : 1;
23351                 
23352                 if (e.ctrlKey){
23353                     newDate = this.moveYear(this.date, dir);
23354                     newViewDate = this.moveYear(this.viewDate, dir);
23355                 } else if (e.shiftKey){
23356                     newDate = this.moveMonth(this.date, dir);
23357                     newViewDate = this.moveMonth(this.viewDate, dir);
23358                 } else {
23359                     newDate = new Date(this.date);
23360                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23361                     newViewDate = new Date(this.viewDate);
23362                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23363                 }
23364                 if (this.dateWithinRange(newDate)){
23365                     this.date = newDate;
23366                     this.viewDate = newViewDate;
23367                     this.setValue(this.formatDate(this.date));
23368 //                    this.update();
23369                     e.preventDefault();
23370                     dateChanged = true;
23371                 }
23372                 break;
23373             case 38: // up
23374             case 40: // down
23375                 if (!this.keyboardNavigation) {
23376                     break;
23377                 }
23378                 dir = e.keyCode == 38 ? -1 : 1;
23379                 if (e.ctrlKey){
23380                     newDate = this.moveYear(this.date, dir);
23381                     newViewDate = this.moveYear(this.viewDate, dir);
23382                 } else if (e.shiftKey){
23383                     newDate = this.moveMonth(this.date, dir);
23384                     newViewDate = this.moveMonth(this.viewDate, dir);
23385                 } else {
23386                     newDate = new Date(this.date);
23387                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23388                     newViewDate = new Date(this.viewDate);
23389                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23390                 }
23391                 if (this.dateWithinRange(newDate)){
23392                     this.date = newDate;
23393                     this.viewDate = newViewDate;
23394                     this.setValue(this.formatDate(this.date));
23395 //                    this.update();
23396                     e.preventDefault();
23397                     dateChanged = true;
23398                 }
23399                 break;
23400             case 13: // enter
23401                 this.setValue(this.formatDate(this.date));
23402                 this.hidePopup();
23403                 e.preventDefault();
23404                 break;
23405             case 9: // tab
23406                 this.setValue(this.formatDate(this.date));
23407                 this.hidePopup();
23408                 break;
23409             case 16: // shift
23410             case 17: // ctrl
23411             case 18: // alt
23412                 break;
23413             default :
23414                 this.hidePopup();
23415                 
23416         }
23417     },
23418     
23419     
23420     onClick: function(e) 
23421     {
23422         e.stopPropagation();
23423         e.preventDefault();
23424         
23425         var target = e.getTarget();
23426         
23427         if(target.nodeName.toLowerCase() === 'i'){
23428             target = Roo.get(target).dom.parentNode;
23429         }
23430         
23431         var nodeName = target.nodeName;
23432         var className = target.className;
23433         var html = target.innerHTML;
23434         //Roo.log(nodeName);
23435         
23436         switch(nodeName.toLowerCase()) {
23437             case 'th':
23438                 switch(className) {
23439                     case 'switch':
23440                         this.showMode(1);
23441                         break;
23442                     case 'prev':
23443                     case 'next':
23444                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23445                         switch(this.viewMode){
23446                                 case 0:
23447                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23448                                         break;
23449                                 case 1:
23450                                 case 2:
23451                                         this.viewDate = this.moveYear(this.viewDate, dir);
23452                                         break;
23453                         }
23454                         this.fill();
23455                         break;
23456                     case 'today':
23457                         var date = new Date();
23458                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23459 //                        this.fill()
23460                         this.setValue(this.formatDate(this.date));
23461                         
23462                         this.hidePopup();
23463                         break;
23464                 }
23465                 break;
23466             case 'span':
23467                 if (className.indexOf('disabled') < 0) {
23468                 if (!this.viewDate) {
23469                     this.viewDate = new Date();
23470                 }
23471                 this.viewDate.setUTCDate(1);
23472                     if (className.indexOf('month') > -1) {
23473                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23474                     } else {
23475                         var year = parseInt(html, 10) || 0;
23476                         this.viewDate.setUTCFullYear(year);
23477                         
23478                     }
23479                     
23480                     if(this.singleMode){
23481                         this.setValue(this.formatDate(this.viewDate));
23482                         this.hidePopup();
23483                         return;
23484                     }
23485                     
23486                     this.showMode(-1);
23487                     this.fill();
23488                 }
23489                 break;
23490                 
23491             case 'td':
23492                 //Roo.log(className);
23493                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23494                     var day = parseInt(html, 10) || 1;
23495                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23496                         month = (this.viewDate || new Date()).getUTCMonth();
23497
23498                     if (className.indexOf('old') > -1) {
23499                         if(month === 0 ){
23500                             month = 11;
23501                             year -= 1;
23502                         }else{
23503                             month -= 1;
23504                         }
23505                     } else if (className.indexOf('new') > -1) {
23506                         if (month == 11) {
23507                             month = 0;
23508                             year += 1;
23509                         } else {
23510                             month += 1;
23511                         }
23512                     }
23513                     //Roo.log([year,month,day]);
23514                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23515                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23516 //                    this.fill();
23517                     //Roo.log(this.formatDate(this.date));
23518                     this.setValue(this.formatDate(this.date));
23519                     this.hidePopup();
23520                 }
23521                 break;
23522         }
23523     },
23524     
23525     setStartDate: function(startDate)
23526     {
23527         this.startDate = startDate || -Infinity;
23528         if (this.startDate !== -Infinity) {
23529             this.startDate = this.parseDate(this.startDate);
23530         }
23531         this.update();
23532         this.updateNavArrows();
23533     },
23534
23535     setEndDate: function(endDate)
23536     {
23537         this.endDate = endDate || Infinity;
23538         if (this.endDate !== Infinity) {
23539             this.endDate = this.parseDate(this.endDate);
23540         }
23541         this.update();
23542         this.updateNavArrows();
23543     },
23544     
23545     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23546     {
23547         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23548         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23549             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23550         }
23551         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23552             return parseInt(d, 10);
23553         });
23554         this.update();
23555         this.updateNavArrows();
23556     },
23557     
23558     updateNavArrows: function() 
23559     {
23560         if(this.singleMode){
23561             return;
23562         }
23563         
23564         var d = new Date(this.viewDate),
23565         year = d.getUTCFullYear(),
23566         month = d.getUTCMonth();
23567         
23568         Roo.each(this.picker().select('.prev', true).elements, function(v){
23569             v.show();
23570             switch (this.viewMode) {
23571                 case 0:
23572
23573                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23574                         v.hide();
23575                     }
23576                     break;
23577                 case 1:
23578                 case 2:
23579                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23580                         v.hide();
23581                     }
23582                     break;
23583             }
23584         });
23585         
23586         Roo.each(this.picker().select('.next', true).elements, function(v){
23587             v.show();
23588             switch (this.viewMode) {
23589                 case 0:
23590
23591                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23592                         v.hide();
23593                     }
23594                     break;
23595                 case 1:
23596                 case 2:
23597                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23598                         v.hide();
23599                     }
23600                     break;
23601             }
23602         })
23603     },
23604     
23605     moveMonth: function(date, dir)
23606     {
23607         if (!dir) {
23608             return date;
23609         }
23610         var new_date = new Date(date.valueOf()),
23611         day = new_date.getUTCDate(),
23612         month = new_date.getUTCMonth(),
23613         mag = Math.abs(dir),
23614         new_month, test;
23615         dir = dir > 0 ? 1 : -1;
23616         if (mag == 1){
23617             test = dir == -1
23618             // If going back one month, make sure month is not current month
23619             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23620             ? function(){
23621                 return new_date.getUTCMonth() == month;
23622             }
23623             // If going forward one month, make sure month is as expected
23624             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23625             : function(){
23626                 return new_date.getUTCMonth() != new_month;
23627             };
23628             new_month = month + dir;
23629             new_date.setUTCMonth(new_month);
23630             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23631             if (new_month < 0 || new_month > 11) {
23632                 new_month = (new_month + 12) % 12;
23633             }
23634         } else {
23635             // For magnitudes >1, move one month at a time...
23636             for (var i=0; i<mag; i++) {
23637                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23638                 new_date = this.moveMonth(new_date, dir);
23639             }
23640             // ...then reset the day, keeping it in the new month
23641             new_month = new_date.getUTCMonth();
23642             new_date.setUTCDate(day);
23643             test = function(){
23644                 return new_month != new_date.getUTCMonth();
23645             };
23646         }
23647         // Common date-resetting loop -- if date is beyond end of month, make it
23648         // end of month
23649         while (test()){
23650             new_date.setUTCDate(--day);
23651             new_date.setUTCMonth(new_month);
23652         }
23653         return new_date;
23654     },
23655
23656     moveYear: function(date, dir)
23657     {
23658         return this.moveMonth(date, dir*12);
23659     },
23660
23661     dateWithinRange: function(date)
23662     {
23663         return date >= this.startDate && date <= this.endDate;
23664     },
23665
23666     
23667     remove: function() 
23668     {
23669         this.picker().remove();
23670     },
23671     
23672     validateValue : function(value)
23673     {
23674         if(this.getVisibilityEl().hasClass('hidden')){
23675             return true;
23676         }
23677         
23678         if(value.length < 1)  {
23679             if(this.allowBlank){
23680                 return true;
23681             }
23682             return false;
23683         }
23684         
23685         if(value.length < this.minLength){
23686             return false;
23687         }
23688         if(value.length > this.maxLength){
23689             return false;
23690         }
23691         if(this.vtype){
23692             var vt = Roo.form.VTypes;
23693             if(!vt[this.vtype](value, this)){
23694                 return false;
23695             }
23696         }
23697         if(typeof this.validator == "function"){
23698             var msg = this.validator(value);
23699             if(msg !== true){
23700                 return false;
23701             }
23702         }
23703         
23704         if(this.regex && !this.regex.test(value)){
23705             return false;
23706         }
23707         
23708         if(typeof(this.parseDate(value)) == 'undefined'){
23709             return false;
23710         }
23711         
23712         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23713             return false;
23714         }      
23715         
23716         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23717             return false;
23718         } 
23719         
23720         
23721         return true;
23722     },
23723     
23724     reset : function()
23725     {
23726         this.date = this.viewDate = '';
23727         
23728         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23729     }
23730    
23731 });
23732
23733 Roo.apply(Roo.bootstrap.form.DateField,  {
23734     
23735     head : {
23736         tag: 'thead',
23737         cn: [
23738         {
23739             tag: 'tr',
23740             cn: [
23741             {
23742                 tag: 'th',
23743                 cls: 'prev',
23744                 html: '<i class="fa fa-arrow-left"/>'
23745             },
23746             {
23747                 tag: 'th',
23748                 cls: 'switch',
23749                 colspan: '5'
23750             },
23751             {
23752                 tag: 'th',
23753                 cls: 'next',
23754                 html: '<i class="fa fa-arrow-right"/>'
23755             }
23756
23757             ]
23758         }
23759         ]
23760     },
23761     
23762     content : {
23763         tag: 'tbody',
23764         cn: [
23765         {
23766             tag: 'tr',
23767             cn: [
23768             {
23769                 tag: 'td',
23770                 colspan: '7'
23771             }
23772             ]
23773         }
23774         ]
23775     },
23776     
23777     footer : {
23778         tag: 'tfoot',
23779         cn: [
23780         {
23781             tag: 'tr',
23782             cn: [
23783             {
23784                 tag: 'th',
23785                 colspan: '7',
23786                 cls: 'today'
23787             }
23788                     
23789             ]
23790         }
23791         ]
23792     },
23793     
23794     dates:{
23795         en: {
23796             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23797             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23798             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23799             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23800             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23801             today: "Today"
23802         }
23803     },
23804     
23805     modes: [
23806     {
23807         clsName: 'days',
23808         navFnc: 'Month',
23809         navStep: 1
23810     },
23811     {
23812         clsName: 'months',
23813         navFnc: 'FullYear',
23814         navStep: 1
23815     },
23816     {
23817         clsName: 'years',
23818         navFnc: 'FullYear',
23819         navStep: 10
23820     }]
23821 });
23822
23823 Roo.apply(Roo.bootstrap.form.DateField,  {
23824   
23825     template : {
23826         tag: 'div',
23827         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23828         cn: [
23829         {
23830             tag: 'div',
23831             cls: 'datepicker-days',
23832             cn: [
23833             {
23834                 tag: 'table',
23835                 cls: 'table-condensed',
23836                 cn:[
23837                 Roo.bootstrap.form.DateField.head,
23838                 {
23839                     tag: 'tbody'
23840                 },
23841                 Roo.bootstrap.form.DateField.footer
23842                 ]
23843             }
23844             ]
23845         },
23846         {
23847             tag: 'div',
23848             cls: 'datepicker-months',
23849             cn: [
23850             {
23851                 tag: 'table',
23852                 cls: 'table-condensed',
23853                 cn:[
23854                 Roo.bootstrap.form.DateField.head,
23855                 Roo.bootstrap.form.DateField.content,
23856                 Roo.bootstrap.form.DateField.footer
23857                 ]
23858             }
23859             ]
23860         },
23861         {
23862             tag: 'div',
23863             cls: 'datepicker-years',
23864             cn: [
23865             {
23866                 tag: 'table',
23867                 cls: 'table-condensed',
23868                 cn:[
23869                 Roo.bootstrap.form.DateField.head,
23870                 Roo.bootstrap.form.DateField.content,
23871                 Roo.bootstrap.form.DateField.footer
23872                 ]
23873             }
23874             ]
23875         }
23876         ]
23877     }
23878 });
23879
23880  
23881
23882  /*
23883  * - LGPL
23884  *
23885  * TimeField
23886  * 
23887  */
23888
23889 /**
23890  * @class Roo.bootstrap.form.TimeField
23891  * @extends Roo.bootstrap.form.Input
23892  * Bootstrap DateField class
23893  * 
23894  * 
23895  * @constructor
23896  * Create a new TimeField
23897  * @param {Object} config The config object
23898  */
23899
23900 Roo.bootstrap.form.TimeField = function(config){
23901     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23902     this.addEvents({
23903             /**
23904              * @event show
23905              * Fires when this field show.
23906              * @param {Roo.bootstrap.form.DateField} thisthis
23907              * @param {Mixed} date The date value
23908              */
23909             show : true,
23910             /**
23911              * @event show
23912              * Fires when this field hide.
23913              * @param {Roo.bootstrap.form.DateField} this
23914              * @param {Mixed} date The date value
23915              */
23916             hide : true,
23917             /**
23918              * @event select
23919              * Fires when select a date.
23920              * @param {Roo.bootstrap.form.DateField} this
23921              * @param {Mixed} date The date value
23922              */
23923             select : true
23924         });
23925 };
23926
23927 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23928     
23929     /**
23930      * @cfg {String} format
23931      * The default time format string which can be overriden for localization support.  The format must be
23932      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23933      */
23934     format : "H:i",
23935
23936     getAutoCreate : function()
23937     {
23938         this.after = '<i class="fa far fa-clock"></i>';
23939         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23940         
23941          
23942     },
23943     onRender: function(ct, position)
23944     {
23945         
23946         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23947                 
23948         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23949         
23950         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23951         
23952         this.pop = this.picker().select('>.datepicker-time',true).first();
23953         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23954         
23955         this.picker().on('mousedown', this.onMousedown, this);
23956         this.picker().on('click', this.onClick, this);
23957         
23958         this.picker().addClass('datepicker-dropdown');
23959     
23960         this.fillTime();
23961         this.update();
23962             
23963         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23964         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23965         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23966         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23967         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23968         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23969
23970     },
23971     
23972     fireKey: function(e){
23973         if (!this.picker().isVisible()){
23974             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23975                 this.show();
23976             }
23977             return;
23978         }
23979
23980         e.preventDefault();
23981         
23982         switch(e.keyCode){
23983             case 27: // escape
23984                 this.hide();
23985                 break;
23986             case 37: // left
23987             case 39: // right
23988                 this.onTogglePeriod();
23989                 break;
23990             case 38: // up
23991                 this.onIncrementMinutes();
23992                 break;
23993             case 40: // down
23994                 this.onDecrementMinutes();
23995                 break;
23996             case 13: // enter
23997             case 9: // tab
23998                 this.setTime();
23999                 break;
24000         }
24001     },
24002     
24003     onClick: function(e) {
24004         e.stopPropagation();
24005         e.preventDefault();
24006     },
24007     
24008     picker : function()
24009     {
24010         return this.pickerEl;
24011     },
24012     
24013     fillTime: function()
24014     {    
24015         var time = this.pop.select('tbody', true).first();
24016         
24017         time.dom.innerHTML = '';
24018         
24019         time.createChild({
24020             tag: 'tr',
24021             cn: [
24022                 {
24023                     tag: 'td',
24024                     cn: [
24025                         {
24026                             tag: 'a',
24027                             href: '#',
24028                             cls: 'btn',
24029                             cn: [
24030                                 {
24031                                     tag: 'i',
24032                                     cls: 'hours-up fa fas fa-chevron-up'
24033                                 }
24034                             ]
24035                         } 
24036                     ]
24037                 },
24038                 {
24039                     tag: 'td',
24040                     cls: 'separator'
24041                 },
24042                 {
24043                     tag: 'td',
24044                     cn: [
24045                         {
24046                             tag: 'a',
24047                             href: '#',
24048                             cls: 'btn',
24049                             cn: [
24050                                 {
24051                                     tag: 'i',
24052                                     cls: 'minutes-up fa fas fa-chevron-up'
24053                                 }
24054                             ]
24055                         }
24056                     ]
24057                 },
24058                 {
24059                     tag: 'td',
24060                     cls: 'separator'
24061                 }
24062             ]
24063         });
24064         
24065         time.createChild({
24066             tag: 'tr',
24067             cn: [
24068                 {
24069                     tag: 'td',
24070                     cn: [
24071                         {
24072                             tag: 'span',
24073                             cls: 'timepicker-hour',
24074                             html: '00'
24075                         }  
24076                     ]
24077                 },
24078                 {
24079                     tag: 'td',
24080                     cls: 'separator',
24081                     html: ':'
24082                 },
24083                 {
24084                     tag: 'td',
24085                     cn: [
24086                         {
24087                             tag: 'span',
24088                             cls: 'timepicker-minute',
24089                             html: '00'
24090                         }  
24091                     ]
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cls: 'separator'
24096                 },
24097                 {
24098                     tag: 'td',
24099                     cn: [
24100                         {
24101                             tag: 'button',
24102                             type: 'button',
24103                             cls: 'btn btn-primary period',
24104                             html: 'AM'
24105                             
24106                         }
24107                     ]
24108                 }
24109             ]
24110         });
24111         
24112         time.createChild({
24113             tag: 'tr',
24114             cn: [
24115                 {
24116                     tag: 'td',
24117                     cn: [
24118                         {
24119                             tag: 'a',
24120                             href: '#',
24121                             cls: 'btn',
24122                             cn: [
24123                                 {
24124                                     tag: 'span',
24125                                     cls: 'hours-down fa fas fa-chevron-down'
24126                                 }
24127                             ]
24128                         }
24129                     ]
24130                 },
24131                 {
24132                     tag: 'td',
24133                     cls: 'separator'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'a',
24140                             href: '#',
24141                             cls: 'btn',
24142                             cn: [
24143                                 {
24144                                     tag: 'span',
24145                                     cls: 'minutes-down fa fas fa-chevron-down'
24146                                 }
24147                             ]
24148                         }
24149                     ]
24150                 },
24151                 {
24152                     tag: 'td',
24153                     cls: 'separator'
24154                 }
24155             ]
24156         });
24157         
24158     },
24159     
24160     update: function()
24161     {
24162         
24163         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24164         
24165         this.fill();
24166     },
24167     
24168     fill: function() 
24169     {
24170         var hours = this.time.getHours();
24171         var minutes = this.time.getMinutes();
24172         var period = 'AM';
24173         
24174         if(hours > 11){
24175             period = 'PM';
24176         }
24177         
24178         if(hours == 0){
24179             hours = 12;
24180         }
24181         
24182         
24183         if(hours > 12){
24184             hours = hours - 12;
24185         }
24186         
24187         if(hours < 10){
24188             hours = '0' + hours;
24189         }
24190         
24191         if(minutes < 10){
24192             minutes = '0' + minutes;
24193         }
24194         
24195         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24196         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24197         this.pop.select('button', true).first().dom.innerHTML = period;
24198         
24199     },
24200     
24201     place: function()
24202     {   
24203         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24204         
24205         var cls = ['bottom'];
24206         
24207         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24208             cls.pop();
24209             cls.push('top');
24210         }
24211         
24212         cls.push('right');
24213         
24214         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24215             cls.pop();
24216             cls.push('left');
24217         }
24218         //this.picker().setXY(20000,20000);
24219         this.picker().addClass(cls.join('-'));
24220         
24221         var _this = this;
24222         
24223         Roo.each(cls, function(c){
24224             if(c == 'bottom'){
24225                 (function() {
24226                  //  
24227                 }).defer(200);
24228                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24229                 //_this.picker().setTop(_this.inputEl().getHeight());
24230                 return;
24231             }
24232             if(c == 'top'){
24233                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24234                 
24235                 //_this.picker().setTop(0 - _this.picker().getHeight());
24236                 return;
24237             }
24238             /*
24239             if(c == 'left'){
24240                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24241                 return;
24242             }
24243             if(c == 'right'){
24244                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24245                 return;
24246             }
24247             */
24248         });
24249         
24250     },
24251   
24252     onFocus : function()
24253     {
24254         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24255         this.show();
24256     },
24257     
24258     onBlur : function()
24259     {
24260         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24261         this.hide();
24262     },
24263     
24264     show : function()
24265     {
24266         this.picker().show();
24267         this.pop.show();
24268         this.update();
24269         this.place();
24270         
24271         this.fireEvent('show', this, this.date);
24272     },
24273     
24274     hide : function()
24275     {
24276         this.picker().hide();
24277         this.pop.hide();
24278         
24279         this.fireEvent('hide', this, this.date);
24280     },
24281     
24282     setTime : function()
24283     {
24284         this.hide();
24285         this.setValue(this.time.format(this.format));
24286         
24287         this.fireEvent('select', this, this.date);
24288         
24289         
24290     },
24291     
24292     onMousedown: function(e){
24293         e.stopPropagation();
24294         e.preventDefault();
24295     },
24296     
24297     onIncrementHours: function()
24298     {
24299         Roo.log('onIncrementHours');
24300         this.time = this.time.add(Date.HOUR, 1);
24301         this.update();
24302         
24303     },
24304     
24305     onDecrementHours: function()
24306     {
24307         Roo.log('onDecrementHours');
24308         this.time = this.time.add(Date.HOUR, -1);
24309         this.update();
24310     },
24311     
24312     onIncrementMinutes: function()
24313     {
24314         Roo.log('onIncrementMinutes');
24315         this.time = this.time.add(Date.MINUTE, 1);
24316         this.update();
24317     },
24318     
24319     onDecrementMinutes: function()
24320     {
24321         Roo.log('onDecrementMinutes');
24322         this.time = this.time.add(Date.MINUTE, -1);
24323         this.update();
24324     },
24325     
24326     onTogglePeriod: function()
24327     {
24328         Roo.log('onTogglePeriod');
24329         this.time = this.time.add(Date.HOUR, 12);
24330         this.update();
24331     }
24332     
24333    
24334 });
24335  
24336
24337 Roo.apply(Roo.bootstrap.form.TimeField,  {
24338   
24339     template : {
24340         tag: 'div',
24341         cls: 'datepicker dropdown-menu',
24342         cn: [
24343             {
24344                 tag: 'div',
24345                 cls: 'datepicker-time',
24346                 cn: [
24347                 {
24348                     tag: 'table',
24349                     cls: 'table-condensed',
24350                     cn:[
24351                         {
24352                             tag: 'tbody',
24353                             cn: [
24354                                 {
24355                                     tag: 'tr',
24356                                     cn: [
24357                                     {
24358                                         tag: 'td',
24359                                         colspan: '7'
24360                                     }
24361                                     ]
24362                                 }
24363                             ]
24364                         },
24365                         {
24366                             tag: 'tfoot',
24367                             cn: [
24368                                 {
24369                                     tag: 'tr',
24370                                     cn: [
24371                                     {
24372                                         tag: 'th',
24373                                         colspan: '7',
24374                                         cls: '',
24375                                         cn: [
24376                                             {
24377                                                 tag: 'button',
24378                                                 cls: 'btn btn-info ok',
24379                                                 html: 'OK'
24380                                             }
24381                                         ]
24382                                     }
24383                     
24384                                     ]
24385                                 }
24386                             ]
24387                         }
24388                     ]
24389                 }
24390                 ]
24391             }
24392         ]
24393     }
24394 });
24395
24396  
24397
24398  /*
24399  * - LGPL
24400  *
24401  * MonthField
24402  * 
24403  */
24404
24405 /**
24406  * @class Roo.bootstrap.form.MonthField
24407  * @extends Roo.bootstrap.form.Input
24408  * Bootstrap MonthField class
24409  * 
24410  * @cfg {String} language default en
24411  * 
24412  * @constructor
24413  * Create a new MonthField
24414  * @param {Object} config The config object
24415  */
24416
24417 Roo.bootstrap.form.MonthField = function(config){
24418     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24419     
24420     this.addEvents({
24421         /**
24422          * @event show
24423          * Fires when this field show.
24424          * @param {Roo.bootstrap.form.MonthField} this
24425          * @param {Mixed} date The date value
24426          */
24427         show : true,
24428         /**
24429          * @event show
24430          * Fires when this field hide.
24431          * @param {Roo.bootstrap.form.MonthField} this
24432          * @param {Mixed} date The date value
24433          */
24434         hide : true,
24435         /**
24436          * @event select
24437          * Fires when select a date.
24438          * @param {Roo.bootstrap.form.MonthField} this
24439          * @param {String} oldvalue The old value
24440          * @param {String} newvalue The new value
24441          */
24442         select : true
24443     });
24444 };
24445
24446 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24447     
24448     onRender: function(ct, position)
24449     {
24450         
24451         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24452         
24453         this.language = this.language || 'en';
24454         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24455         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24456         
24457         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24458         this.isInline = false;
24459         this.isInput = true;
24460         this.component = this.el.select('.add-on', true).first() || false;
24461         this.component = (this.component && this.component.length === 0) ? false : this.component;
24462         this.hasInput = this.component && this.inputEL().length;
24463         
24464         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24465         
24466         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24467         
24468         this.picker().on('mousedown', this.onMousedown, this);
24469         this.picker().on('click', this.onClick, this);
24470         
24471         this.picker().addClass('datepicker-dropdown');
24472         
24473         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24474             v.setStyle('width', '189px');
24475         });
24476         
24477         this.fillMonths();
24478         
24479         this.update();
24480         
24481         if(this.isInline) {
24482             this.show();
24483         }
24484         
24485     },
24486     
24487     setValue: function(v, suppressEvent)
24488     {   
24489         var o = this.getValue();
24490         
24491         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24492         
24493         this.update();
24494
24495         if(suppressEvent !== true){
24496             this.fireEvent('select', this, o, v);
24497         }
24498         
24499     },
24500     
24501     getValue: function()
24502     {
24503         return this.value;
24504     },
24505     
24506     onClick: function(e) 
24507     {
24508         e.stopPropagation();
24509         e.preventDefault();
24510         
24511         var target = e.getTarget();
24512         
24513         if(target.nodeName.toLowerCase() === 'i'){
24514             target = Roo.get(target).dom.parentNode;
24515         }
24516         
24517         var nodeName = target.nodeName;
24518         var className = target.className;
24519         var html = target.innerHTML;
24520         
24521         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24522             return;
24523         }
24524         
24525         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24526         
24527         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24528         
24529         this.hide();
24530                         
24531     },
24532     
24533     picker : function()
24534     {
24535         return this.pickerEl;
24536     },
24537     
24538     fillMonths: function()
24539     {    
24540         var i = 0;
24541         var months = this.picker().select('>.datepicker-months td', true).first();
24542         
24543         months.dom.innerHTML = '';
24544         
24545         while (i < 12) {
24546             var month = {
24547                 tag: 'span',
24548                 cls: 'month',
24549                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24550             };
24551             
24552             months.createChild(month);
24553         }
24554         
24555     },
24556     
24557     update: function()
24558     {
24559         var _this = this;
24560         
24561         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24562             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24563         }
24564         
24565         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24566             e.removeClass('active');
24567             
24568             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24569                 e.addClass('active');
24570             }
24571         })
24572     },
24573     
24574     place: function()
24575     {
24576         if(this.isInline) {
24577             return;
24578         }
24579         
24580         this.picker().removeClass(['bottom', 'top']);
24581         
24582         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24583             /*
24584              * place to the top of element!
24585              *
24586              */
24587             
24588             this.picker().addClass('top');
24589             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24590             
24591             return;
24592         }
24593         
24594         this.picker().addClass('bottom');
24595         
24596         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24597     },
24598     
24599     onFocus : function()
24600     {
24601         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24602         this.show();
24603     },
24604     
24605     onBlur : function()
24606     {
24607         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24608         
24609         var d = this.inputEl().getValue();
24610         
24611         this.setValue(d);
24612                 
24613         this.hide();
24614     },
24615     
24616     show : function()
24617     {
24618         this.picker().show();
24619         this.picker().select('>.datepicker-months', true).first().show();
24620         this.update();
24621         this.place();
24622         
24623         this.fireEvent('show', this, this.date);
24624     },
24625     
24626     hide : function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         this.picker().hide();
24632         this.fireEvent('hide', this, this.date);
24633         
24634     },
24635     
24636     onMousedown: function(e)
24637     {
24638         e.stopPropagation();
24639         e.preventDefault();
24640     },
24641     
24642     keyup: function(e)
24643     {
24644         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24645         this.update();
24646     },
24647
24648     fireKey: function(e)
24649     {
24650         if (!this.picker().isVisible()){
24651             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24652                 this.show();
24653             }
24654             return;
24655         }
24656         
24657         var dir;
24658         
24659         switch(e.keyCode){
24660             case 27: // escape
24661                 this.hide();
24662                 e.preventDefault();
24663                 break;
24664             case 37: // left
24665             case 39: // right
24666                 dir = e.keyCode == 37 ? -1 : 1;
24667                 
24668                 this.vIndex = this.vIndex + dir;
24669                 
24670                 if(this.vIndex < 0){
24671                     this.vIndex = 0;
24672                 }
24673                 
24674                 if(this.vIndex > 11){
24675                     this.vIndex = 11;
24676                 }
24677                 
24678                 if(isNaN(this.vIndex)){
24679                     this.vIndex = 0;
24680                 }
24681                 
24682                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24683                 
24684                 break;
24685             case 38: // up
24686             case 40: // down
24687                 
24688                 dir = e.keyCode == 38 ? -1 : 1;
24689                 
24690                 this.vIndex = this.vIndex + dir * 4;
24691                 
24692                 if(this.vIndex < 0){
24693                     this.vIndex = 0;
24694                 }
24695                 
24696                 if(this.vIndex > 11){
24697                     this.vIndex = 11;
24698                 }
24699                 
24700                 if(isNaN(this.vIndex)){
24701                     this.vIndex = 0;
24702                 }
24703                 
24704                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24705                 break;
24706                 
24707             case 13: // enter
24708                 
24709                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24710                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24711                 }
24712                 
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 9: // tab
24717                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24718                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24719                 }
24720                 this.hide();
24721                 break;
24722             case 16: // shift
24723             case 17: // ctrl
24724             case 18: // alt
24725                 break;
24726             default :
24727                 this.hide();
24728                 
24729         }
24730     },
24731     
24732     remove: function() 
24733     {
24734         this.picker().remove();
24735     }
24736    
24737 });
24738
24739 Roo.apply(Roo.bootstrap.form.MonthField,  {
24740     
24741     content : {
24742         tag: 'tbody',
24743         cn: [
24744         {
24745             tag: 'tr',
24746             cn: [
24747             {
24748                 tag: 'td',
24749                 colspan: '7'
24750             }
24751             ]
24752         }
24753         ]
24754     },
24755     
24756     dates:{
24757         en: {
24758             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24759             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24760         }
24761     }
24762 });
24763
24764 Roo.apply(Roo.bootstrap.form.MonthField,  {
24765   
24766     template : {
24767         tag: 'div',
24768         cls: 'datepicker dropdown-menu roo-dynamic',
24769         cn: [
24770             {
24771                 tag: 'div',
24772                 cls: 'datepicker-months',
24773                 cn: [
24774                 {
24775                     tag: 'table',
24776                     cls: 'table-condensed',
24777                     cn:[
24778                         Roo.bootstrap.form.DateField.content
24779                     ]
24780                 }
24781                 ]
24782             }
24783         ]
24784     }
24785 });
24786
24787  
24788
24789  
24790  /*
24791  * - LGPL
24792  *
24793  * CheckBox
24794  * 
24795  */
24796
24797 /**
24798  * @class Roo.bootstrap.form.CheckBox
24799  * @extends Roo.bootstrap.form.Input
24800  * Bootstrap CheckBox class
24801  * 
24802  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24803  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24804  * @cfg {String} boxLabel The text that appears beside the checkbox
24805  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24806  * @cfg {Boolean} checked initnal the element
24807  * @cfg {Boolean} inline inline the element (default false)
24808  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24809  * @cfg {String} tooltip label tooltip
24810  * 
24811  * @constructor
24812  * Create a new CheckBox
24813  * @param {Object} config The config object
24814  */
24815
24816 Roo.bootstrap.form.CheckBox = function(config){
24817     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24818    
24819     this.addEvents({
24820         /**
24821         * @event check
24822         * Fires when the element is checked or unchecked.
24823         * @param {Roo.bootstrap.form.CheckBox} this This input
24824         * @param {Boolean} checked The new checked value
24825         */
24826        check : true,
24827        /**
24828         * @event click
24829         * Fires when the element is click.
24830         * @param {Roo.bootstrap.form.CheckBox} this This input
24831         */
24832        click : true
24833     });
24834     
24835 };
24836
24837 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24838   
24839     inputType: 'checkbox',
24840     inputValue: 1,
24841     valueOff: 0,
24842     boxLabel: false,
24843     checked: false,
24844     weight : false,
24845     inline: false,
24846     tooltip : '',
24847     
24848     // checkbox success does not make any sense really.. 
24849     invalidClass : "",
24850     validClass : "",
24851     
24852     
24853     getAutoCreate : function()
24854     {
24855         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24856         
24857         var id = Roo.id();
24858         
24859         var cfg = {};
24860         
24861         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24862         
24863         if(this.inline){
24864             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24865         }
24866         
24867         var input =  {
24868             tag: 'input',
24869             id : id,
24870             type : this.inputType,
24871             value : this.inputValue,
24872             cls : 'roo-' + this.inputType, //'form-box',
24873             placeholder : this.placeholder || ''
24874             
24875         };
24876         
24877         if(this.inputType != 'radio'){
24878             var hidden =  {
24879                 tag: 'input',
24880                 type : 'hidden',
24881                 cls : 'roo-hidden-value',
24882                 value : this.checked ? this.inputValue : this.valueOff
24883             };
24884         }
24885         
24886             
24887         if (this.weight) { // Validity check?
24888             cfg.cls += " " + this.inputType + "-" + this.weight;
24889         }
24890         
24891         if (this.disabled) {
24892             input.disabled=true;
24893         }
24894         
24895         if(this.checked){
24896             input.checked = this.checked;
24897         }
24898         
24899         if (this.name) {
24900             
24901             input.name = this.name;
24902             
24903             if(this.inputType != 'radio'){
24904                 hidden.name = this.name;
24905                 input.name = '_hidden_' + this.name;
24906             }
24907         }
24908         
24909         if (this.size) {
24910             input.cls += ' input-' + this.size;
24911         }
24912         
24913         var settings=this;
24914         
24915         ['xs','sm','md','lg'].map(function(size){
24916             if (settings[size]) {
24917                 cfg.cls += ' col-' + size + '-' + settings[size];
24918             }
24919         });
24920         
24921         var inputblock = input;
24922          
24923         if (this.before || this.after) {
24924             
24925             inputblock = {
24926                 cls : 'input-group',
24927                 cn :  [] 
24928             };
24929             
24930             if (this.before) {
24931                 inputblock.cn.push({
24932                     tag :'span',
24933                     cls : 'input-group-addon',
24934                     html : this.before
24935                 });
24936             }
24937             
24938             inputblock.cn.push(input);
24939             
24940             if(this.inputType != 'radio'){
24941                 inputblock.cn.push(hidden);
24942             }
24943             
24944             if (this.after) {
24945                 inputblock.cn.push({
24946                     tag :'span',
24947                     cls : 'input-group-addon',
24948                     html : this.after
24949                 });
24950             }
24951             
24952         }
24953         var boxLabelCfg = false;
24954         
24955         if(this.boxLabel){
24956            
24957             boxLabelCfg = {
24958                 tag: 'label',
24959                 //'for': id, // box label is handled by onclick - so no for...
24960                 cls: 'box-label',
24961                 html: this.boxLabel
24962             };
24963             if(this.tooltip){
24964                 boxLabelCfg.tooltip = this.tooltip;
24965             }
24966              
24967         }
24968         
24969         
24970         if (align ==='left' && this.fieldLabel.length) {
24971 //                Roo.log("left and has label");
24972             cfg.cn = [
24973                 {
24974                     tag: 'label',
24975                     'for' :  id,
24976                     cls : 'control-label',
24977                     html : this.fieldLabel
24978                 },
24979                 {
24980                     cls : "", 
24981                     cn: [
24982                         inputblock
24983                     ]
24984                 }
24985             ];
24986             
24987             if (boxLabelCfg) {
24988                 cfg.cn[1].cn.push(boxLabelCfg);
24989             }
24990             
24991             if(this.labelWidth > 12){
24992                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24993             }
24994             
24995             if(this.labelWidth < 13 && this.labelmd == 0){
24996                 this.labelmd = this.labelWidth;
24997             }
24998             
24999             if(this.labellg > 0){
25000                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25001                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25002             }
25003             
25004             if(this.labelmd > 0){
25005                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25006                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25007             }
25008             
25009             if(this.labelsm > 0){
25010                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25011                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25012             }
25013             
25014             if(this.labelxs > 0){
25015                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25016                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25017             }
25018             
25019         } else if ( this.fieldLabel.length) {
25020 //                Roo.log(" label");
25021                 cfg.cn = [
25022                    
25023                     {
25024                         tag: this.boxLabel ? 'span' : 'label',
25025                         'for': id,
25026                         cls: 'control-label box-input-label',
25027                         //cls : 'input-group-addon',
25028                         html : this.fieldLabel
25029                     },
25030                     
25031                     inputblock
25032                     
25033                 ];
25034                 if (boxLabelCfg) {
25035                     cfg.cn.push(boxLabelCfg);
25036                 }
25037
25038         } else {
25039             
25040 //                Roo.log(" no label && no align");
25041                 cfg.cn = [  inputblock ] ;
25042                 if (boxLabelCfg) {
25043                     cfg.cn.push(boxLabelCfg);
25044                 }
25045
25046                 
25047         }
25048         
25049        
25050         
25051         if(this.inputType != 'radio'){
25052             cfg.cn.push(hidden);
25053         }
25054         
25055         return cfg;
25056         
25057     },
25058     
25059     /**
25060      * return the real input element.
25061      */
25062     inputEl: function ()
25063     {
25064         return this.el.select('input.roo-' + this.inputType,true).first();
25065     },
25066     hiddenEl: function ()
25067     {
25068         return this.el.select('input.roo-hidden-value',true).first();
25069     },
25070     
25071     labelEl: function()
25072     {
25073         return this.el.select('label.control-label',true).first();
25074     },
25075     /* depricated... */
25076     
25077     label: function()
25078     {
25079         return this.labelEl();
25080     },
25081     
25082     boxLabelEl: function()
25083     {
25084         return this.el.select('label.box-label',true).first();
25085     },
25086     
25087     initEvents : function()
25088     {
25089 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25090         
25091         this.inputEl().on('click', this.onClick,  this);
25092         
25093         if (this.boxLabel) { 
25094             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25095         }
25096         
25097         this.startValue = this.getValue();
25098         
25099         if(this.groupId){
25100             Roo.bootstrap.form.CheckBox.register(this);
25101         }
25102     },
25103     
25104     onClick : function(e)
25105     {   
25106         if(this.fireEvent('click', this, e) !== false){
25107             this.setChecked(!this.checked);
25108         }
25109         
25110     },
25111     
25112     setChecked : function(state,suppressEvent)
25113     {
25114         this.startValue = this.getValue();
25115
25116         if(this.inputType == 'radio'){
25117             
25118             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25119                 e.dom.checked = false;
25120             });
25121             
25122             this.inputEl().dom.checked = true;
25123             
25124             this.inputEl().dom.value = this.inputValue;
25125             
25126             if(suppressEvent !== true){
25127                 this.fireEvent('check', this, true);
25128             }
25129             
25130             this.validate();
25131             
25132             return;
25133         }
25134         
25135         this.checked = state;
25136         
25137         this.inputEl().dom.checked = state;
25138         
25139         
25140         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25141         
25142         if(suppressEvent !== true){
25143             this.fireEvent('check', this, state);
25144         }
25145         
25146         this.validate();
25147     },
25148     
25149     getValue : function()
25150     {
25151         if(this.inputType == 'radio'){
25152             return this.getGroupValue();
25153         }
25154         
25155         return this.hiddenEl().dom.value;
25156         
25157     },
25158     
25159     getGroupValue : function()
25160     {
25161         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25162             return '';
25163         }
25164         
25165         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25166     },
25167     
25168     setValue : function(v,suppressEvent)
25169     {
25170         if(this.inputType == 'radio'){
25171             this.setGroupValue(v, suppressEvent);
25172             return;
25173         }
25174         
25175         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25176         
25177         this.validate();
25178     },
25179     
25180     setGroupValue : function(v, suppressEvent)
25181     {
25182         this.startValue = this.getValue();
25183         
25184         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25185             e.dom.checked = false;
25186             
25187             if(e.dom.value == v){
25188                 e.dom.checked = true;
25189             }
25190         });
25191         
25192         if(suppressEvent !== true){
25193             this.fireEvent('check', this, true);
25194         }
25195
25196         this.validate();
25197         
25198         return;
25199     },
25200     
25201     validate : function()
25202     {
25203         if(this.getVisibilityEl().hasClass('hidden')){
25204             return true;
25205         }
25206         
25207         if(
25208                 this.disabled || 
25209                 (this.inputType == 'radio' && this.validateRadio()) ||
25210                 (this.inputType == 'checkbox' && this.validateCheckbox())
25211         ){
25212             this.markValid();
25213             return true;
25214         }
25215         
25216         this.markInvalid();
25217         return false;
25218     },
25219     
25220     validateRadio : function()
25221     {
25222         if(this.getVisibilityEl().hasClass('hidden')){
25223             return true;
25224         }
25225         
25226         if(this.allowBlank){
25227             return true;
25228         }
25229         
25230         var valid = false;
25231         
25232         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25233             if(!e.dom.checked){
25234                 return;
25235             }
25236             
25237             valid = true;
25238             
25239             return false;
25240         });
25241         
25242         return valid;
25243     },
25244     
25245     validateCheckbox : function()
25246     {
25247         if(!this.groupId){
25248             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25249             //return (this.getValue() == this.inputValue) ? true : false;
25250         }
25251         
25252         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25253         
25254         if(!group){
25255             return false;
25256         }
25257         
25258         var r = false;
25259         
25260         for(var i in group){
25261             if(group[i].el.isVisible(true)){
25262                 r = false;
25263                 break;
25264             }
25265             
25266             r = true;
25267         }
25268         
25269         for(var i in group){
25270             if(r){
25271                 break;
25272             }
25273             
25274             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25275         }
25276         
25277         return r;
25278     },
25279     
25280     /**
25281      * Mark this field as valid
25282      */
25283     markValid : function()
25284     {
25285         var _this = this;
25286         
25287         this.fireEvent('valid', this);
25288         
25289         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25290         
25291         if(this.groupId){
25292             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25293         }
25294         
25295         if(label){
25296             label.markValid();
25297         }
25298
25299         if(this.inputType == 'radio'){
25300             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25301                 var fg = e.findParent('.form-group', false, true);
25302                 if (Roo.bootstrap.version == 3) {
25303                     fg.removeClass([_this.invalidClass, _this.validClass]);
25304                     fg.addClass(_this.validClass);
25305                 } else {
25306                     fg.removeClass(['is-valid', 'is-invalid']);
25307                     fg.addClass('is-valid');
25308                 }
25309             });
25310             
25311             return;
25312         }
25313
25314         if(!this.groupId){
25315             var fg = this.el.findParent('.form-group', false, true);
25316             if (Roo.bootstrap.version == 3) {
25317                 fg.removeClass([this.invalidClass, this.validClass]);
25318                 fg.addClass(this.validClass);
25319             } else {
25320                 fg.removeClass(['is-valid', 'is-invalid']);
25321                 fg.addClass('is-valid');
25322             }
25323             return;
25324         }
25325         
25326         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25327         
25328         if(!group){
25329             return;
25330         }
25331         
25332         for(var i in group){
25333             var fg = group[i].el.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     
25344      /**
25345      * Mark this field as invalid
25346      * @param {String} msg The validation message
25347      */
25348     markInvalid : function(msg)
25349     {
25350         if(this.allowBlank){
25351             return;
25352         }
25353         
25354         var _this = this;
25355         
25356         this.fireEvent('invalid', this, msg);
25357         
25358         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25359         
25360         if(this.groupId){
25361             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25362         }
25363         
25364         if(label){
25365             label.markInvalid();
25366         }
25367             
25368         if(this.inputType == 'radio'){
25369             
25370             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25371                 var fg = e.findParent('.form-group', false, true);
25372                 if (Roo.bootstrap.version == 3) {
25373                     fg.removeClass([_this.invalidClass, _this.validClass]);
25374                     fg.addClass(_this.invalidClass);
25375                 } else {
25376                     fg.removeClass(['is-invalid', 'is-valid']);
25377                     fg.addClass('is-invalid');
25378                 }
25379             });
25380             
25381             return;
25382         }
25383         
25384         if(!this.groupId){
25385             var fg = this.el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([_this.invalidClass, _this.validClass]);
25388                 fg.addClass(_this.invalidClass);
25389             } else {
25390                 fg.removeClass(['is-invalid', 'is-valid']);
25391                 fg.addClass('is-invalid');
25392             }
25393             return;
25394         }
25395         
25396         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25397         
25398         if(!group){
25399             return;
25400         }
25401         
25402         for(var i in group){
25403             var fg = group[i].el.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     },
25414     
25415     clearInvalid : function()
25416     {
25417         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25418         
25419         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25420         
25421         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25422         
25423         if (label && label.iconEl) {
25424             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25425             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25426         }
25427     },
25428     
25429     disable : function()
25430     {
25431         if(this.inputType != 'radio'){
25432             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25433             return;
25434         }
25435         
25436         var _this = this;
25437         
25438         if(this.rendered){
25439             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25440                 _this.getActionEl().addClass(this.disabledClass);
25441                 e.dom.disabled = true;
25442             });
25443         }
25444         
25445         this.disabled = true;
25446         this.fireEvent("disable", this);
25447         return this;
25448     },
25449
25450     enable : function()
25451     {
25452         if(this.inputType != 'radio'){
25453             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25454             return;
25455         }
25456         
25457         var _this = this;
25458         
25459         if(this.rendered){
25460             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25461                 _this.getActionEl().removeClass(this.disabledClass);
25462                 e.dom.disabled = false;
25463             });
25464         }
25465         
25466         this.disabled = false;
25467         this.fireEvent("enable", this);
25468         return this;
25469     },
25470     
25471     setBoxLabel : function(v)
25472     {
25473         this.boxLabel = v;
25474         
25475         if(this.rendered){
25476             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25477         }
25478     }
25479
25480 });
25481
25482 Roo.apply(Roo.bootstrap.form.CheckBox, {
25483     
25484     groups: {},
25485     
25486      /**
25487     * register a CheckBox Group
25488     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25489     */
25490     register : function(checkbox)
25491     {
25492         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25493             this.groups[checkbox.groupId] = {};
25494         }
25495         
25496         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25497             return;
25498         }
25499         
25500         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25501         
25502     },
25503     /**
25504     * fetch a CheckBox Group based on the group ID
25505     * @param {string} the group ID
25506     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25507     */
25508     get: function(groupId) {
25509         if (typeof(this.groups[groupId]) == 'undefined') {
25510             return false;
25511         }
25512         
25513         return this.groups[groupId] ;
25514     }
25515     
25516     
25517 });
25518 /*
25519  * - LGPL
25520  *
25521  * RadioItem
25522  * 
25523  */
25524
25525 /**
25526  * @class Roo.bootstrap.form.Radio
25527  * @extends Roo.bootstrap.Component
25528  * Bootstrap Radio class
25529  * @cfg {String} boxLabel - the label associated
25530  * @cfg {String} value - the value of radio
25531  * 
25532  * @constructor
25533  * Create a new Radio
25534  * @param {Object} config The config object
25535  */
25536 Roo.bootstrap.form.Radio = function(config){
25537     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25538     
25539 };
25540
25541 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25542     
25543     boxLabel : '',
25544     
25545     value : '',
25546     
25547     getAutoCreate : function()
25548     {
25549         var cfg = {
25550             tag : 'div',
25551             cls : 'form-group radio',
25552             cn : [
25553                 {
25554                     tag : 'label',
25555                     cls : 'box-label',
25556                     html : this.boxLabel
25557                 }
25558             ]
25559         };
25560         
25561         return cfg;
25562     },
25563     
25564     initEvents : function() 
25565     {
25566         this.parent().register(this);
25567         
25568         this.el.on('click', this.onClick, this);
25569         
25570     },
25571     
25572     onClick : function(e)
25573     {
25574         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25575             this.setChecked(true);
25576         }
25577     },
25578     
25579     setChecked : function(state, suppressEvent)
25580     {
25581         this.parent().setValue(this.value, suppressEvent);
25582         
25583     },
25584     
25585     setBoxLabel : function(v)
25586     {
25587         this.boxLabel = v;
25588         
25589         if(this.rendered){
25590             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25591         }
25592     }
25593     
25594 });
25595  
25596
25597  /*
25598  * - LGPL
25599  *
25600  * Input
25601  * 
25602  */
25603
25604 /**
25605  * @class Roo.bootstrap.form.SecurePass
25606  * @extends Roo.bootstrap.form.Input
25607  * Bootstrap SecurePass class
25608  *
25609  * 
25610  * @constructor
25611  * Create a new SecurePass
25612  * @param {Object} config The config object
25613  */
25614  
25615 Roo.bootstrap.form.SecurePass = function (config) {
25616     // these go here, so the translation tool can replace them..
25617     this.errors = {
25618         PwdEmpty: "Please type a password, and then retype it to confirm.",
25619         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25620         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25621         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25622         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25623         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25624         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25625         TooWeak: "Your password is Too Weak."
25626     },
25627     this.meterLabel = "Password strength:";
25628     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25629     this.meterClass = [
25630         "roo-password-meter-tooweak", 
25631         "roo-password-meter-weak", 
25632         "roo-password-meter-medium", 
25633         "roo-password-meter-strong", 
25634         "roo-password-meter-grey"
25635     ];
25636     
25637     this.errors = {};
25638     
25639     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25640 }
25641
25642 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25643     /**
25644      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25645      * {
25646      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25647      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25648      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25649      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25650      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25651      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25652      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25653      * })
25654      */
25655     // private
25656     
25657     meterWidth: 300,
25658     errorMsg :'',    
25659     errors: false,
25660     imageRoot: '/',
25661     /**
25662      * @cfg {String/Object} Label for the strength meter (defaults to
25663      * 'Password strength:')
25664      */
25665     // private
25666     meterLabel: '',
25667     /**
25668      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25669      * ['Weak', 'Medium', 'Strong'])
25670      */
25671     // private    
25672     pwdStrengths: false,    
25673     // private
25674     strength: 0,
25675     // private
25676     _lastPwd: null,
25677     // private
25678     kCapitalLetter: 0,
25679     kSmallLetter: 1,
25680     kDigit: 2,
25681     kPunctuation: 3,
25682     
25683     insecure: false,
25684     // private
25685     initEvents: function ()
25686     {
25687         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25688
25689         if (this.el.is('input[type=password]') && Roo.isSafari) {
25690             this.el.on('keydown', this.SafariOnKeyDown, this);
25691         }
25692
25693         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25694     },
25695     // private
25696     onRender: function (ct, position)
25697     {
25698         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25699         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25700         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25701
25702         this.trigger.createChild({
25703                    cn: [
25704                     {
25705                     //id: 'PwdMeter',
25706                     tag: 'div',
25707                     cls: 'roo-password-meter-grey col-xs-12',
25708                     style: {
25709                         //width: 0,
25710                         //width: this.meterWidth + 'px'                                                
25711                         }
25712                     },
25713                     {                            
25714                          cls: 'roo-password-meter-text'                          
25715                     }
25716                 ]            
25717         });
25718
25719          
25720         if (this.hideTrigger) {
25721             this.trigger.setDisplayed(false);
25722         }
25723         this.setSize(this.width || '', this.height || '');
25724     },
25725     // private
25726     onDestroy: function ()
25727     {
25728         if (this.trigger) {
25729             this.trigger.removeAllListeners();
25730             this.trigger.remove();
25731         }
25732         if (this.wrap) {
25733             this.wrap.remove();
25734         }
25735         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25736     },
25737     // private
25738     checkStrength: function ()
25739     {
25740         var pwd = this.inputEl().getValue();
25741         if (pwd == this._lastPwd) {
25742             return;
25743         }
25744
25745         var strength;
25746         if (this.ClientSideStrongPassword(pwd)) {
25747             strength = 3;
25748         } else if (this.ClientSideMediumPassword(pwd)) {
25749             strength = 2;
25750         } else if (this.ClientSideWeakPassword(pwd)) {
25751             strength = 1;
25752         } else {
25753             strength = 0;
25754         }
25755         
25756         Roo.log('strength1: ' + strength);
25757         
25758         //var pm = this.trigger.child('div/div/div').dom;
25759         var pm = this.trigger.child('div/div');
25760         pm.removeClass(this.meterClass);
25761         pm.addClass(this.meterClass[strength]);
25762                 
25763         
25764         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25765                 
25766         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25767         
25768         this._lastPwd = pwd;
25769     },
25770     reset: function ()
25771     {
25772         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25773         
25774         this._lastPwd = '';
25775         
25776         var pm = this.trigger.child('div/div');
25777         pm.removeClass(this.meterClass);
25778         pm.addClass('roo-password-meter-grey');        
25779         
25780         
25781         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25782         
25783         pt.innerHTML = '';
25784         this.inputEl().dom.type='password';
25785     },
25786     // private
25787     validateValue: function (value)
25788     {
25789         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25790             return false;
25791         }
25792         if (value.length == 0) {
25793             if (this.allowBlank) {
25794                 this.clearInvalid();
25795                 return true;
25796             }
25797
25798             this.markInvalid(this.errors.PwdEmpty);
25799             this.errorMsg = this.errors.PwdEmpty;
25800             return false;
25801         }
25802         
25803         if(this.insecure){
25804             return true;
25805         }
25806         
25807         if (!value.match(/[\x21-\x7e]+/)) {
25808             this.markInvalid(this.errors.PwdBadChar);
25809             this.errorMsg = this.errors.PwdBadChar;
25810             return false;
25811         }
25812         if (value.length < 6) {
25813             this.markInvalid(this.errors.PwdShort);
25814             this.errorMsg = this.errors.PwdShort;
25815             return false;
25816         }
25817         if (value.length > 16) {
25818             this.markInvalid(this.errors.PwdLong);
25819             this.errorMsg = this.errors.PwdLong;
25820             return false;
25821         }
25822         var strength;
25823         if (this.ClientSideStrongPassword(value)) {
25824             strength = 3;
25825         } else if (this.ClientSideMediumPassword(value)) {
25826             strength = 2;
25827         } else if (this.ClientSideWeakPassword(value)) {
25828             strength = 1;
25829         } else {
25830             strength = 0;
25831         }
25832
25833         
25834         if (strength < 2) {
25835             //this.markInvalid(this.errors.TooWeak);
25836             this.errorMsg = this.errors.TooWeak;
25837             //return false;
25838         }
25839         
25840         
25841         console.log('strength2: ' + strength);
25842         
25843         //var pm = this.trigger.child('div/div/div').dom;
25844         
25845         var pm = this.trigger.child('div/div');
25846         pm.removeClass(this.meterClass);
25847         pm.addClass(this.meterClass[strength]);
25848                 
25849         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25850                 
25851         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25852         
25853         this.errorMsg = ''; 
25854         return true;
25855     },
25856     // private
25857     CharacterSetChecks: function (type)
25858     {
25859         this.type = type;
25860         this.fResult = false;
25861     },
25862     // private
25863     isctype: function (character, type)
25864     {
25865         switch (type) {  
25866             case this.kCapitalLetter:
25867                 if (character >= 'A' && character <= 'Z') {
25868                     return true;
25869                 }
25870                 break;
25871             
25872             case this.kSmallLetter:
25873                 if (character >= 'a' && character <= 'z') {
25874                     return true;
25875                 }
25876                 break;
25877             
25878             case this.kDigit:
25879                 if (character >= '0' && character <= '9') {
25880                     return true;
25881                 }
25882                 break;
25883             
25884             case this.kPunctuation:
25885                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25886                     return true;
25887                 }
25888                 break;
25889             
25890             default:
25891                 return false;
25892         }
25893
25894     },
25895     // private
25896     IsLongEnough: function (pwd, size)
25897     {
25898         return !(pwd == null || isNaN(size) || pwd.length < size);
25899     },
25900     // private
25901     SpansEnoughCharacterSets: function (word, nb)
25902     {
25903         if (!this.IsLongEnough(word, nb))
25904         {
25905             return false;
25906         }
25907
25908         var characterSetChecks = new Array(
25909             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25910             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25911         );
25912         
25913         for (var index = 0; index < word.length; ++index) {
25914             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25915                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25916                     characterSetChecks[nCharSet].fResult = true;
25917                     break;
25918                 }
25919             }
25920         }
25921
25922         var nCharSets = 0;
25923         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25924             if (characterSetChecks[nCharSet].fResult) {
25925                 ++nCharSets;
25926             }
25927         }
25928
25929         if (nCharSets < nb) {
25930             return false;
25931         }
25932         return true;
25933     },
25934     // private
25935     ClientSideStrongPassword: function (pwd)
25936     {
25937         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25938     },
25939     // private
25940     ClientSideMediumPassword: function (pwd)
25941     {
25942         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25943     },
25944     // private
25945     ClientSideWeakPassword: function (pwd)
25946     {
25947         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25948     }
25949           
25950 })//<script type="text/javascript">
25951
25952 /*
25953  * Based  Ext JS Library 1.1.1
25954  * Copyright(c) 2006-2007, Ext JS, LLC.
25955  * LGPL
25956  *
25957  */
25958  
25959 /**
25960  * @class Roo.HtmlEditorCore
25961  * @extends Roo.Component
25962  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25963  *
25964  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25965  */
25966
25967 Roo.HtmlEditorCore = function(config){
25968     
25969     
25970     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25971     
25972     
25973     this.addEvents({
25974         /**
25975          * @event initialize
25976          * Fires when the editor is fully initialized (including the iframe)
25977          * @param {Roo.HtmlEditorCore} this
25978          */
25979         initialize: true,
25980         /**
25981          * @event activate
25982          * Fires when the editor is first receives the focus. Any insertion must wait
25983          * until after this event.
25984          * @param {Roo.HtmlEditorCore} this
25985          */
25986         activate: true,
25987          /**
25988          * @event beforesync
25989          * Fires before the textarea is updated with content from the editor iframe. Return false
25990          * to cancel the sync.
25991          * @param {Roo.HtmlEditorCore} this
25992          * @param {String} html
25993          */
25994         beforesync: true,
25995          /**
25996          * @event beforepush
25997          * Fires before the iframe editor is updated with content from the textarea. Return false
25998          * to cancel the push.
25999          * @param {Roo.HtmlEditorCore} this
26000          * @param {String} html
26001          */
26002         beforepush: true,
26003          /**
26004          * @event sync
26005          * Fires when the textarea is updated with content from the editor iframe.
26006          * @param {Roo.HtmlEditorCore} this
26007          * @param {String} html
26008          */
26009         sync: true,
26010          /**
26011          * @event push
26012          * Fires when the iframe editor is updated with content from the textarea.
26013          * @param {Roo.HtmlEditorCore} this
26014          * @param {String} html
26015          */
26016         push: true,
26017         
26018         /**
26019          * @event editorevent
26020          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26021          * @param {Roo.HtmlEditorCore} this
26022          */
26023         editorevent: true
26024         
26025     });
26026     
26027     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26028     
26029     // defaults : white / black...
26030     this.applyBlacklists();
26031     
26032     
26033     
26034 };
26035
26036
26037 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26038
26039
26040      /**
26041      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26042      */
26043     
26044     owner : false,
26045     
26046      /**
26047      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26048      *                        Roo.resizable.
26049      */
26050     resizable : false,
26051      /**
26052      * @cfg {Number} height (in pixels)
26053      */   
26054     height: 300,
26055    /**
26056      * @cfg {Number} width (in pixels)
26057      */   
26058     width: 500,
26059     
26060     /**
26061      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26062      * 
26063      */
26064     stylesheets: false,
26065     
26066     /**
26067      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26068      */
26069     allowComments: false,
26070     // id of frame..
26071     frameId: false,
26072     
26073     // private properties
26074     validationEvent : false,
26075     deferHeight: true,
26076     initialized : false,
26077     activated : false,
26078     sourceEditMode : false,
26079     onFocus : Roo.emptyFn,
26080     iframePad:3,
26081     hideMode:'offsets',
26082     
26083     clearUp: true,
26084     
26085     // blacklist + whitelisted elements..
26086     black: false,
26087     white: false,
26088      
26089     bodyCls : '',
26090
26091     /**
26092      * Protected method that will not generally be called directly. It
26093      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26094      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26095      */
26096     getDocMarkup : function(){
26097         // body styles..
26098         var st = '';
26099         
26100         // inherit styels from page...?? 
26101         if (this.stylesheets === false) {
26102             
26103             Roo.get(document.head).select('style').each(function(node) {
26104                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26105             });
26106             
26107             Roo.get(document.head).select('link').each(function(node) { 
26108                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26109             });
26110             
26111         } else if (!this.stylesheets.length) {
26112                 // simple..
26113                 st = '<style type="text/css">' +
26114                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26115                    '</style>';
26116         } else {
26117             for (var i in this.stylesheets) { 
26118                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26119             }
26120             
26121         }
26122         
26123         st +=  '<style type="text/css">' +
26124             'IMG { cursor: pointer } ' +
26125         '</style>';
26126
26127         var cls = 'roo-htmleditor-body';
26128         
26129         if(this.bodyCls.length){
26130             cls += ' ' + this.bodyCls;
26131         }
26132         
26133         return '<html><head>' + st  +
26134             //<style type="text/css">' +
26135             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26136             //'</style>' +
26137             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26138     },
26139
26140     // private
26141     onRender : function(ct, position)
26142     {
26143         var _t = this;
26144         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26145         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26146         
26147         
26148         this.el.dom.style.border = '0 none';
26149         this.el.dom.setAttribute('tabIndex', -1);
26150         this.el.addClass('x-hidden hide');
26151         
26152         
26153         
26154         if(Roo.isIE){ // fix IE 1px bogus margin
26155             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26156         }
26157        
26158         
26159         this.frameId = Roo.id();
26160         
26161          
26162         
26163         var iframe = this.owner.wrap.createChild({
26164             tag: 'iframe',
26165             cls: 'form-control', // bootstrap..
26166             id: this.frameId,
26167             name: this.frameId,
26168             frameBorder : 'no',
26169             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26170         }, this.el
26171         );
26172         
26173         
26174         this.iframe = iframe.dom;
26175
26176          this.assignDocWin();
26177         
26178         this.doc.designMode = 'on';
26179        
26180         this.doc.open();
26181         this.doc.write(this.getDocMarkup());
26182         this.doc.close();
26183
26184         
26185         var task = { // must defer to wait for browser to be ready
26186             run : function(){
26187                 //console.log("run task?" + this.doc.readyState);
26188                 this.assignDocWin();
26189                 if(this.doc.body || this.doc.readyState == 'complete'){
26190                     try {
26191                         this.doc.designMode="on";
26192                     } catch (e) {
26193                         return;
26194                     }
26195                     Roo.TaskMgr.stop(task);
26196                     this.initEditor.defer(10, this);
26197                 }
26198             },
26199             interval : 10,
26200             duration: 10000,
26201             scope: this
26202         };
26203         Roo.TaskMgr.start(task);
26204
26205     },
26206
26207     // private
26208     onResize : function(w, h)
26209     {
26210          Roo.log('resize: ' +w + ',' + h );
26211         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26212         if(!this.iframe){
26213             return;
26214         }
26215         if(typeof w == 'number'){
26216             
26217             this.iframe.style.width = w + 'px';
26218         }
26219         if(typeof h == 'number'){
26220             
26221             this.iframe.style.height = h + 'px';
26222             if(this.doc){
26223                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26224             }
26225         }
26226         
26227     },
26228
26229     /**
26230      * Toggles the editor between standard and source edit mode.
26231      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26232      */
26233     toggleSourceEdit : function(sourceEditMode){
26234         
26235         this.sourceEditMode = sourceEditMode === true;
26236         
26237         if(this.sourceEditMode){
26238  
26239             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26240             
26241         }else{
26242             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26243             //this.iframe.className = '';
26244             this.deferFocus();
26245         }
26246         //this.setSize(this.owner.wrap.getSize());
26247         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26248     },
26249
26250     
26251   
26252
26253     /**
26254      * Protected method that will not generally be called directly. If you need/want
26255      * custom HTML cleanup, this is the method you should override.
26256      * @param {String} html The HTML to be cleaned
26257      * return {String} The cleaned HTML
26258      */
26259     cleanHtml : function(html){
26260         html = String(html);
26261         if(html.length > 5){
26262             if(Roo.isSafari){ // strip safari nonsense
26263                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26264             }
26265         }
26266         if(html == '&nbsp;'){
26267             html = '';
26268         }
26269         return html;
26270     },
26271
26272     /**
26273      * HTML Editor -> Textarea
26274      * Protected method that will not generally be called directly. Syncs the contents
26275      * of the editor iframe with the textarea.
26276      */
26277     syncValue : function(){
26278         if(this.initialized){
26279             var bd = (this.doc.body || this.doc.documentElement);
26280             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26281             var html = bd.innerHTML;
26282             if(Roo.isSafari){
26283                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26284                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26285                 if(m && m[1]){
26286                     html = '<div style="'+m[0]+'">' + html + '</div>';
26287                 }
26288             }
26289             html = this.cleanHtml(html);
26290             // fix up the special chars.. normaly like back quotes in word...
26291             // however we do not want to do this with chinese..
26292             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26293                 
26294                 var cc = match.charCodeAt();
26295
26296                 // Get the character value, handling surrogate pairs
26297                 if (match.length == 2) {
26298                     // It's a surrogate pair, calculate the Unicode code point
26299                     var high = match.charCodeAt(0) - 0xD800;
26300                     var low  = match.charCodeAt(1) - 0xDC00;
26301                     cc = (high * 0x400) + low + 0x10000;
26302                 }  else if (
26303                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26304                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26305                     (cc >= 0xf900 && cc < 0xfb00 )
26306                 ) {
26307                         return match;
26308                 }  
26309          
26310                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26311                 return "&#" + cc + ";";
26312                 
26313                 
26314             });
26315             
26316             
26317              
26318             if(this.owner.fireEvent('beforesync', this, html) !== false){
26319                 this.el.dom.value = html;
26320                 this.owner.fireEvent('sync', this, html);
26321             }
26322         }
26323     },
26324
26325     /**
26326      * Protected method that will not generally be called directly. Pushes the value of the textarea
26327      * into the iframe editor.
26328      */
26329     pushValue : function(){
26330         if(this.initialized){
26331             var v = this.el.dom.value.trim();
26332             
26333 //            if(v.length < 1){
26334 //                v = '&#160;';
26335 //            }
26336             
26337             if(this.owner.fireEvent('beforepush', this, v) !== false){
26338                 var d = (this.doc.body || this.doc.documentElement);
26339                 d.innerHTML = v;
26340                 this.cleanUpPaste();
26341                 this.el.dom.value = d.innerHTML;
26342                 this.owner.fireEvent('push', this, v);
26343             }
26344         }
26345     },
26346
26347     // private
26348     deferFocus : function(){
26349         this.focus.defer(10, this);
26350     },
26351
26352     // doc'ed in Field
26353     focus : function(){
26354         if(this.win && !this.sourceEditMode){
26355             this.win.focus();
26356         }else{
26357             this.el.focus();
26358         }
26359     },
26360     
26361     assignDocWin: function()
26362     {
26363         var iframe = this.iframe;
26364         
26365          if(Roo.isIE){
26366             this.doc = iframe.contentWindow.document;
26367             this.win = iframe.contentWindow;
26368         } else {
26369 //            if (!Roo.get(this.frameId)) {
26370 //                return;
26371 //            }
26372 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26373 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26374             
26375             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26376                 return;
26377             }
26378             
26379             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26380             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26381         }
26382     },
26383     
26384     // private
26385     initEditor : function(){
26386         //console.log("INIT EDITOR");
26387         this.assignDocWin();
26388         
26389         
26390         
26391         this.doc.designMode="on";
26392         this.doc.open();
26393         this.doc.write(this.getDocMarkup());
26394         this.doc.close();
26395         
26396         var dbody = (this.doc.body || this.doc.documentElement);
26397         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26398         // this copies styles from the containing element into thsi one..
26399         // not sure why we need all of this..
26400         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26401         
26402         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26403         //ss['background-attachment'] = 'fixed'; // w3c
26404         dbody.bgProperties = 'fixed'; // ie
26405         //Roo.DomHelper.applyStyles(dbody, ss);
26406         Roo.EventManager.on(this.doc, {
26407             //'mousedown': this.onEditorEvent,
26408             'mouseup': this.onEditorEvent,
26409             'dblclick': this.onEditorEvent,
26410             'click': this.onEditorEvent,
26411             'keyup': this.onEditorEvent,
26412             buffer:100,
26413             scope: this
26414         });
26415         if(Roo.isGecko){
26416             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26417         }
26418         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26419             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26420         }
26421         this.initialized = true;
26422
26423         this.owner.fireEvent('initialize', this);
26424         this.pushValue();
26425     },
26426
26427     // private
26428     onDestroy : function(){
26429         
26430         
26431         
26432         if(this.rendered){
26433             
26434             //for (var i =0; i < this.toolbars.length;i++) {
26435             //    // fixme - ask toolbars for heights?
26436             //    this.toolbars[i].onDestroy();
26437            // }
26438             
26439             //this.wrap.dom.innerHTML = '';
26440             //this.wrap.remove();
26441         }
26442     },
26443
26444     // private
26445     onFirstFocus : function(){
26446         
26447         this.assignDocWin();
26448         
26449         
26450         this.activated = true;
26451          
26452     
26453         if(Roo.isGecko){ // prevent silly gecko errors
26454             this.win.focus();
26455             var s = this.win.getSelection();
26456             if(!s.focusNode || s.focusNode.nodeType != 3){
26457                 var r = s.getRangeAt(0);
26458                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26459                 r.collapse(true);
26460                 this.deferFocus();
26461             }
26462             try{
26463                 this.execCmd('useCSS', true);
26464                 this.execCmd('styleWithCSS', false);
26465             }catch(e){}
26466         }
26467         this.owner.fireEvent('activate', this);
26468     },
26469
26470     // private
26471     adjustFont: function(btn){
26472         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26473         //if(Roo.isSafari){ // safari
26474         //    adjust *= 2;
26475        // }
26476         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26477         if(Roo.isSafari){ // safari
26478             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26479             v =  (v < 10) ? 10 : v;
26480             v =  (v > 48) ? 48 : v;
26481             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26482             
26483         }
26484         
26485         
26486         v = Math.max(1, v+adjust);
26487         
26488         this.execCmd('FontSize', v  );
26489     },
26490
26491     onEditorEvent : function(e)
26492     {
26493         this.owner.fireEvent('editorevent', this, e);
26494       //  this.updateToolbar();
26495         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26496     },
26497
26498     insertTag : function(tg)
26499     {
26500         // could be a bit smarter... -> wrap the current selected tRoo..
26501         if (tg.toLowerCase() == 'span' ||
26502             tg.toLowerCase() == 'code' ||
26503             tg.toLowerCase() == 'sup' ||
26504             tg.toLowerCase() == 'sub' 
26505             ) {
26506             
26507             range = this.createRange(this.getSelection());
26508             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26509             wrappingNode.appendChild(range.extractContents());
26510             range.insertNode(wrappingNode);
26511
26512             return;
26513             
26514             
26515             
26516         }
26517         this.execCmd("formatblock",   tg);
26518         
26519     },
26520     
26521     insertText : function(txt)
26522     {
26523         
26524         
26525         var range = this.createRange();
26526         range.deleteContents();
26527                //alert(Sender.getAttribute('label'));
26528                
26529         range.insertNode(this.doc.createTextNode(txt));
26530     } ,
26531     
26532      
26533
26534     /**
26535      * Executes a Midas editor command on the editor document and performs necessary focus and
26536      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26537      * @param {String} cmd The Midas command
26538      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26539      */
26540     relayCmd : function(cmd, value){
26541         this.win.focus();
26542         this.execCmd(cmd, value);
26543         this.owner.fireEvent('editorevent', this);
26544         //this.updateToolbar();
26545         this.owner.deferFocus();
26546     },
26547
26548     /**
26549      * Executes a Midas editor command directly on the editor document.
26550      * For visual commands, you should use {@link #relayCmd} instead.
26551      * <b>This should only be called after the editor is initialized.</b>
26552      * @param {String} cmd The Midas command
26553      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26554      */
26555     execCmd : function(cmd, value){
26556         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26557         this.syncValue();
26558     },
26559  
26560  
26561    
26562     /**
26563      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26564      * to insert tRoo.
26565      * @param {String} text | dom node.. 
26566      */
26567     insertAtCursor : function(text)
26568     {
26569         
26570         if(!this.activated){
26571             return;
26572         }
26573         /*
26574         if(Roo.isIE){
26575             this.win.focus();
26576             var r = this.doc.selection.createRange();
26577             if(r){
26578                 r.collapse(true);
26579                 r.pasteHTML(text);
26580                 this.syncValue();
26581                 this.deferFocus();
26582             
26583             }
26584             return;
26585         }
26586         */
26587         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26588             this.win.focus();
26589             
26590             
26591             // from jquery ui (MIT licenced)
26592             var range, node;
26593             var win = this.win;
26594             
26595             if (win.getSelection && win.getSelection().getRangeAt) {
26596                 range = win.getSelection().getRangeAt(0);
26597                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26598                 range.insertNode(node);
26599             } else if (win.document.selection && win.document.selection.createRange) {
26600                 // no firefox support
26601                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26602                 win.document.selection.createRange().pasteHTML(txt);
26603             } else {
26604                 // no firefox support
26605                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26606                 this.execCmd('InsertHTML', txt);
26607             } 
26608             
26609             this.syncValue();
26610             
26611             this.deferFocus();
26612         }
26613     },
26614  // private
26615     mozKeyPress : function(e){
26616         if(e.ctrlKey){
26617             var c = e.getCharCode(), cmd;
26618           
26619             if(c > 0){
26620                 c = String.fromCharCode(c).toLowerCase();
26621                 switch(c){
26622                     case 'b':
26623                         cmd = 'bold';
26624                         break;
26625                     case 'i':
26626                         cmd = 'italic';
26627                         break;
26628                     
26629                     case 'u':
26630                         cmd = 'underline';
26631                         break;
26632                     
26633                     case 'v':
26634                         this.cleanUpPaste.defer(100, this);
26635                         return;
26636                         
26637                 }
26638                 if(cmd){
26639                     this.win.focus();
26640                     this.execCmd(cmd);
26641                     this.deferFocus();
26642                     e.preventDefault();
26643                 }
26644                 
26645             }
26646         }
26647     },
26648
26649     // private
26650     fixKeys : function(){ // load time branching for fastest keydown performance
26651         if(Roo.isIE){
26652             return function(e){
26653                 var k = e.getKey(), r;
26654                 if(k == e.TAB){
26655                     e.stopEvent();
26656                     r = this.doc.selection.createRange();
26657                     if(r){
26658                         r.collapse(true);
26659                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26660                         this.deferFocus();
26661                     }
26662                     return;
26663                 }
26664                 
26665                 if(k == e.ENTER){
26666                     r = this.doc.selection.createRange();
26667                     if(r){
26668                         var target = r.parentElement();
26669                         if(!target || target.tagName.toLowerCase() != 'li'){
26670                             e.stopEvent();
26671                             r.pasteHTML('<br />');
26672                             r.collapse(false);
26673                             r.select();
26674                         }
26675                     }
26676                 }
26677                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26678                     this.cleanUpPaste.defer(100, this);
26679                     return;
26680                 }
26681                 
26682                 
26683             };
26684         }else if(Roo.isOpera){
26685             return function(e){
26686                 var k = e.getKey();
26687                 if(k == e.TAB){
26688                     e.stopEvent();
26689                     this.win.focus();
26690                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26691                     this.deferFocus();
26692                 }
26693                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26694                     this.cleanUpPaste.defer(100, this);
26695                     return;
26696                 }
26697                 
26698             };
26699         }else if(Roo.isSafari){
26700             return function(e){
26701                 var k = e.getKey();
26702                 
26703                 if(k == e.TAB){
26704                     e.stopEvent();
26705                     this.execCmd('InsertText','\t');
26706                     this.deferFocus();
26707                     return;
26708                 }
26709                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26710                     this.cleanUpPaste.defer(100, this);
26711                     return;
26712                 }
26713                 
26714              };
26715         }
26716     }(),
26717     
26718     getAllAncestors: function()
26719     {
26720         var p = this.getSelectedNode();
26721         var a = [];
26722         if (!p) {
26723             a.push(p); // push blank onto stack..
26724             p = this.getParentElement();
26725         }
26726         
26727         
26728         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26729             a.push(p);
26730             p = p.parentNode;
26731         }
26732         a.push(this.doc.body);
26733         return a;
26734     },
26735     lastSel : false,
26736     lastSelNode : false,
26737     
26738     
26739     getSelection : function() 
26740     {
26741         this.assignDocWin();
26742         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26743     },
26744     
26745     getSelectedNode: function() 
26746     {
26747         // this may only work on Gecko!!!
26748         
26749         // should we cache this!!!!
26750         
26751         
26752         
26753          
26754         var range = this.createRange(this.getSelection()).cloneRange();
26755         
26756         if (Roo.isIE) {
26757             var parent = range.parentElement();
26758             while (true) {
26759                 var testRange = range.duplicate();
26760                 testRange.moveToElementText(parent);
26761                 if (testRange.inRange(range)) {
26762                     break;
26763                 }
26764                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26765                     break;
26766                 }
26767                 parent = parent.parentElement;
26768             }
26769             return parent;
26770         }
26771         
26772         // is ancestor a text element.
26773         var ac =  range.commonAncestorContainer;
26774         if (ac.nodeType == 3) {
26775             ac = ac.parentNode;
26776         }
26777         
26778         var ar = ac.childNodes;
26779          
26780         var nodes = [];
26781         var other_nodes = [];
26782         var has_other_nodes = false;
26783         for (var i=0;i<ar.length;i++) {
26784             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26785                 continue;
26786             }
26787             // fullly contained node.
26788             
26789             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26790                 nodes.push(ar[i]);
26791                 continue;
26792             }
26793             
26794             // probably selected..
26795             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26796                 other_nodes.push(ar[i]);
26797                 continue;
26798             }
26799             // outer..
26800             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26801                 continue;
26802             }
26803             
26804             
26805             has_other_nodes = true;
26806         }
26807         if (!nodes.length && other_nodes.length) {
26808             nodes= other_nodes;
26809         }
26810         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26811             return false;
26812         }
26813         
26814         return nodes[0];
26815     },
26816     createRange: function(sel)
26817     {
26818         // this has strange effects when using with 
26819         // top toolbar - not sure if it's a great idea.
26820         //this.editor.contentWindow.focus();
26821         if (typeof sel != "undefined") {
26822             try {
26823                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26824             } catch(e) {
26825                 return this.doc.createRange();
26826             }
26827         } else {
26828             return this.doc.createRange();
26829         }
26830     },
26831     getParentElement: function()
26832     {
26833         
26834         this.assignDocWin();
26835         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26836         
26837         var range = this.createRange(sel);
26838          
26839         try {
26840             var p = range.commonAncestorContainer;
26841             while (p.nodeType == 3) { // text node
26842                 p = p.parentNode;
26843             }
26844             return p;
26845         } catch (e) {
26846             return null;
26847         }
26848     
26849     },
26850     /***
26851      *
26852      * Range intersection.. the hard stuff...
26853      *  '-1' = before
26854      *  '0' = hits..
26855      *  '1' = after.
26856      *         [ -- selected range --- ]
26857      *   [fail]                        [fail]
26858      *
26859      *    basically..
26860      *      if end is before start or  hits it. fail.
26861      *      if start is after end or hits it fail.
26862      *
26863      *   if either hits (but other is outside. - then it's not 
26864      *   
26865      *    
26866      **/
26867     
26868     
26869     // @see http://www.thismuchiknow.co.uk/?p=64.
26870     rangeIntersectsNode : function(range, node)
26871     {
26872         var nodeRange = node.ownerDocument.createRange();
26873         try {
26874             nodeRange.selectNode(node);
26875         } catch (e) {
26876             nodeRange.selectNodeContents(node);
26877         }
26878     
26879         var rangeStartRange = range.cloneRange();
26880         rangeStartRange.collapse(true);
26881     
26882         var rangeEndRange = range.cloneRange();
26883         rangeEndRange.collapse(false);
26884     
26885         var nodeStartRange = nodeRange.cloneRange();
26886         nodeStartRange.collapse(true);
26887     
26888         var nodeEndRange = nodeRange.cloneRange();
26889         nodeEndRange.collapse(false);
26890     
26891         return rangeStartRange.compareBoundaryPoints(
26892                  Range.START_TO_START, nodeEndRange) == -1 &&
26893                rangeEndRange.compareBoundaryPoints(
26894                  Range.START_TO_START, nodeStartRange) == 1;
26895         
26896          
26897     },
26898     rangeCompareNode : function(range, node)
26899     {
26900         var nodeRange = node.ownerDocument.createRange();
26901         try {
26902             nodeRange.selectNode(node);
26903         } catch (e) {
26904             nodeRange.selectNodeContents(node);
26905         }
26906         
26907         
26908         range.collapse(true);
26909     
26910         nodeRange.collapse(true);
26911      
26912         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26913         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26914          
26915         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26916         
26917         var nodeIsBefore   =  ss == 1;
26918         var nodeIsAfter    = ee == -1;
26919         
26920         if (nodeIsBefore && nodeIsAfter) {
26921             return 0; // outer
26922         }
26923         if (!nodeIsBefore && nodeIsAfter) {
26924             return 1; //right trailed.
26925         }
26926         
26927         if (nodeIsBefore && !nodeIsAfter) {
26928             return 2;  // left trailed.
26929         }
26930         // fully contined.
26931         return 3;
26932     },
26933
26934     // private? - in a new class?
26935     cleanUpPaste :  function()
26936     {
26937         // cleans up the whole document..
26938         Roo.log('cleanuppaste');
26939         
26940         this.cleanUpChildren(this.doc.body);
26941         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26942         if (clean != this.doc.body.innerHTML) {
26943             this.doc.body.innerHTML = clean;
26944         }
26945         
26946     },
26947     
26948     cleanWordChars : function(input) {// change the chars to hex code
26949         var he = Roo.HtmlEditorCore;
26950         
26951         var output = input;
26952         Roo.each(he.swapCodes, function(sw) { 
26953             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26954             
26955             output = output.replace(swapper, sw[1]);
26956         });
26957         
26958         return output;
26959     },
26960     
26961     
26962     cleanUpChildren : function (n)
26963     {
26964         if (!n.childNodes.length) {
26965             return;
26966         }
26967         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26968            this.cleanUpChild(n.childNodes[i]);
26969         }
26970     },
26971     
26972     
26973         
26974     
26975     cleanUpChild : function (node)
26976     {
26977         var ed = this;
26978         //console.log(node);
26979         if (node.nodeName == "#text") {
26980             // clean up silly Windows -- stuff?
26981             return; 
26982         }
26983         if (node.nodeName == "#comment") {
26984             if (!this.allowComments) {
26985                 node.parentNode.removeChild(node);
26986             }
26987             // clean up silly Windows -- stuff?
26988             return; 
26989         }
26990         var lcname = node.tagName.toLowerCase();
26991         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26992         // whitelist of tags..
26993         
26994         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26995             // remove node.
26996             node.parentNode.removeChild(node);
26997             return;
26998             
26999         }
27000         
27001         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27002         
27003         // spans with no attributes - just remove them..
27004         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27005             remove_keep_children = true;
27006         }
27007         
27008         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27009         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27010         
27011         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27012         //    remove_keep_children = true;
27013         //}
27014         
27015         if (remove_keep_children) {
27016             this.cleanUpChildren(node);
27017             // inserts everything just before this node...
27018             while (node.childNodes.length) {
27019                 var cn = node.childNodes[0];
27020                 node.removeChild(cn);
27021                 node.parentNode.insertBefore(cn, node);
27022             }
27023             node.parentNode.removeChild(node);
27024             return;
27025         }
27026         
27027         if (!node.attributes || !node.attributes.length) {
27028             
27029           
27030             
27031             
27032             this.cleanUpChildren(node);
27033             return;
27034         }
27035         
27036         function cleanAttr(n,v)
27037         {
27038             
27039             if (v.match(/^\./) || v.match(/^\//)) {
27040                 return;
27041             }
27042             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27043                 return;
27044             }
27045             if (v.match(/^#/)) {
27046                 return;
27047             }
27048             if (v.match(/^\{/)) { // allow template editing.
27049                 return;
27050             }
27051 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27052             node.removeAttribute(n);
27053             
27054         }
27055         
27056         var cwhite = this.cwhite;
27057         var cblack = this.cblack;
27058             
27059         function cleanStyle(n,v)
27060         {
27061             if (v.match(/expression/)) { //XSS?? should we even bother..
27062                 node.removeAttribute(n);
27063                 return;
27064             }
27065             
27066             var parts = v.split(/;/);
27067             var clean = [];
27068             
27069             Roo.each(parts, function(p) {
27070                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27071                 if (!p.length) {
27072                     return true;
27073                 }
27074                 var l = p.split(':').shift().replace(/\s+/g,'');
27075                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27076                 
27077                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27078 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27079                     //node.removeAttribute(n);
27080                     return true;
27081                 }
27082                 //Roo.log()
27083                 // only allow 'c whitelisted system attributes'
27084                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27085 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27086                     //node.removeAttribute(n);
27087                     return true;
27088                 }
27089                 
27090                 
27091                  
27092                 
27093                 clean.push(p);
27094                 return true;
27095             });
27096             if (clean.length) { 
27097                 node.setAttribute(n, clean.join(';'));
27098             } else {
27099                 node.removeAttribute(n);
27100             }
27101             
27102         }
27103         
27104         
27105         for (var i = node.attributes.length-1; i > -1 ; i--) {
27106             var a = node.attributes[i];
27107             //console.log(a);
27108             
27109             if (a.name.toLowerCase().substr(0,2)=='on')  {
27110                 node.removeAttribute(a.name);
27111                 continue;
27112             }
27113             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27114                 node.removeAttribute(a.name);
27115                 continue;
27116             }
27117             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27118                 cleanAttr(a.name,a.value); // fixme..
27119                 continue;
27120             }
27121             if (a.name == 'style') {
27122                 cleanStyle(a.name,a.value);
27123                 continue;
27124             }
27125             /// clean up MS crap..
27126             // tecnically this should be a list of valid class'es..
27127             
27128             
27129             if (a.name == 'class') {
27130                 if (a.value.match(/^Mso/)) {
27131                     node.removeAttribute('class');
27132                 }
27133                 
27134                 if (a.value.match(/^body$/)) {
27135                     node.removeAttribute('class');
27136                 }
27137                 continue;
27138             }
27139             
27140             // style cleanup!?
27141             // class cleanup?
27142             
27143         }
27144         
27145         
27146         this.cleanUpChildren(node);
27147         
27148         
27149     },
27150     
27151     /**
27152      * Clean up MS wordisms...
27153      */
27154     cleanWord : function(node)
27155     {
27156         if (!node) {
27157             this.cleanWord(this.doc.body);
27158             return;
27159         }
27160         
27161         if(
27162                 node.nodeName == 'SPAN' &&
27163                 !node.hasAttributes() &&
27164                 node.childNodes.length == 1 &&
27165                 node.firstChild.nodeName == "#text"  
27166         ) {
27167             var textNode = node.firstChild;
27168             node.removeChild(textNode);
27169             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27170                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27171             }
27172             node.parentNode.insertBefore(textNode, node);
27173             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27174                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27175             }
27176             node.parentNode.removeChild(node);
27177         }
27178         
27179         if (node.nodeName == "#text") {
27180             // clean up silly Windows -- stuff?
27181             return; 
27182         }
27183         if (node.nodeName == "#comment") {
27184             node.parentNode.removeChild(node);
27185             // clean up silly Windows -- stuff?
27186             return; 
27187         }
27188         
27189         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27190             node.parentNode.removeChild(node);
27191             return;
27192         }
27193         //Roo.log(node.tagName);
27194         // remove - but keep children..
27195         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27196             //Roo.log('-- removed');
27197             while (node.childNodes.length) {
27198                 var cn = node.childNodes[0];
27199                 node.removeChild(cn);
27200                 node.parentNode.insertBefore(cn, node);
27201                 // move node to parent - and clean it..
27202                 this.cleanWord(cn);
27203             }
27204             node.parentNode.removeChild(node);
27205             /// no need to iterate chidlren = it's got none..
27206             //this.iterateChildren(node, this.cleanWord);
27207             return;
27208         }
27209         // clean styles
27210         if (node.className.length) {
27211             
27212             var cn = node.className.split(/\W+/);
27213             var cna = [];
27214             Roo.each(cn, function(cls) {
27215                 if (cls.match(/Mso[a-zA-Z]+/)) {
27216                     return;
27217                 }
27218                 cna.push(cls);
27219             });
27220             node.className = cna.length ? cna.join(' ') : '';
27221             if (!cna.length) {
27222                 node.removeAttribute("class");
27223             }
27224         }
27225         
27226         if (node.hasAttribute("lang")) {
27227             node.removeAttribute("lang");
27228         }
27229         
27230         if (node.hasAttribute("style")) {
27231             
27232             var styles = node.getAttribute("style").split(";");
27233             var nstyle = [];
27234             Roo.each(styles, function(s) {
27235                 if (!s.match(/:/)) {
27236                     return;
27237                 }
27238                 var kv = s.split(":");
27239                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27240                     return;
27241                 }
27242                 // what ever is left... we allow.
27243                 nstyle.push(s);
27244             });
27245             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27246             if (!nstyle.length) {
27247                 node.removeAttribute('style');
27248             }
27249         }
27250         this.iterateChildren(node, this.cleanWord);
27251         
27252         
27253         
27254     },
27255     /**
27256      * iterateChildren of a Node, calling fn each time, using this as the scole..
27257      * @param {DomNode} node node to iterate children of.
27258      * @param {Function} fn method of this class to call on each item.
27259      */
27260     iterateChildren : function(node, fn)
27261     {
27262         if (!node.childNodes.length) {
27263                 return;
27264         }
27265         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27266            fn.call(this, node.childNodes[i])
27267         }
27268     },
27269     
27270     
27271     /**
27272      * cleanTableWidths.
27273      *
27274      * Quite often pasting from word etc.. results in tables with column and widths.
27275      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27276      *
27277      */
27278     cleanTableWidths : function(node)
27279     {
27280          
27281          
27282         if (!node) {
27283             this.cleanTableWidths(this.doc.body);
27284             return;
27285         }
27286         
27287         // ignore list...
27288         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27289             return; 
27290         }
27291         Roo.log(node.tagName);
27292         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27293             this.iterateChildren(node, this.cleanTableWidths);
27294             return;
27295         }
27296         if (node.hasAttribute('width')) {
27297             node.removeAttribute('width');
27298         }
27299         
27300          
27301         if (node.hasAttribute("style")) {
27302             // pretty basic...
27303             
27304             var styles = node.getAttribute("style").split(";");
27305             var nstyle = [];
27306             Roo.each(styles, function(s) {
27307                 if (!s.match(/:/)) {
27308                     return;
27309                 }
27310                 var kv = s.split(":");
27311                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27312                     return;
27313                 }
27314                 // what ever is left... we allow.
27315                 nstyle.push(s);
27316             });
27317             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27318             if (!nstyle.length) {
27319                 node.removeAttribute('style');
27320             }
27321         }
27322         
27323         this.iterateChildren(node, this.cleanTableWidths);
27324         
27325         
27326     },
27327     
27328     
27329     
27330     
27331     domToHTML : function(currentElement, depth, nopadtext) {
27332         
27333         depth = depth || 0;
27334         nopadtext = nopadtext || false;
27335     
27336         if (!currentElement) {
27337             return this.domToHTML(this.doc.body);
27338         }
27339         
27340         //Roo.log(currentElement);
27341         var j;
27342         var allText = false;
27343         var nodeName = currentElement.nodeName;
27344         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27345         
27346         if  (nodeName == '#text') {
27347             
27348             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27349         }
27350         
27351         
27352         var ret = '';
27353         if (nodeName != 'BODY') {
27354              
27355             var i = 0;
27356             // Prints the node tagName, such as <A>, <IMG>, etc
27357             if (tagName) {
27358                 var attr = [];
27359                 for(i = 0; i < currentElement.attributes.length;i++) {
27360                     // quoting?
27361                     var aname = currentElement.attributes.item(i).name;
27362                     if (!currentElement.attributes.item(i).value.length) {
27363                         continue;
27364                     }
27365                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27366                 }
27367                 
27368                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27369             } 
27370             else {
27371                 
27372                 // eack
27373             }
27374         } else {
27375             tagName = false;
27376         }
27377         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27378             return ret;
27379         }
27380         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27381             nopadtext = true;
27382         }
27383         
27384         
27385         // Traverse the tree
27386         i = 0;
27387         var currentElementChild = currentElement.childNodes.item(i);
27388         var allText = true;
27389         var innerHTML  = '';
27390         lastnode = '';
27391         while (currentElementChild) {
27392             // Formatting code (indent the tree so it looks nice on the screen)
27393             var nopad = nopadtext;
27394             if (lastnode == 'SPAN') {
27395                 nopad  = true;
27396             }
27397             // text
27398             if  (currentElementChild.nodeName == '#text') {
27399                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27400                 toadd = nopadtext ? toadd : toadd.trim();
27401                 if (!nopad && toadd.length > 80) {
27402                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27403                 }
27404                 innerHTML  += toadd;
27405                 
27406                 i++;
27407                 currentElementChild = currentElement.childNodes.item(i);
27408                 lastNode = '';
27409                 continue;
27410             }
27411             allText = false;
27412             
27413             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27414                 
27415             // Recursively traverse the tree structure of the child node
27416             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27417             lastnode = currentElementChild.nodeName;
27418             i++;
27419             currentElementChild=currentElement.childNodes.item(i);
27420         }
27421         
27422         ret += innerHTML;
27423         
27424         if (!allText) {
27425                 // The remaining code is mostly for formatting the tree
27426             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27427         }
27428         
27429         
27430         if (tagName) {
27431             ret+= "</"+tagName+">";
27432         }
27433         return ret;
27434         
27435     },
27436         
27437     applyBlacklists : function()
27438     {
27439         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27440         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27441         
27442         this.white = [];
27443         this.black = [];
27444         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27445             if (b.indexOf(tag) > -1) {
27446                 return;
27447             }
27448             this.white.push(tag);
27449             
27450         }, this);
27451         
27452         Roo.each(w, function(tag) {
27453             if (b.indexOf(tag) > -1) {
27454                 return;
27455             }
27456             if (this.white.indexOf(tag) > -1) {
27457                 return;
27458             }
27459             this.white.push(tag);
27460             
27461         }, this);
27462         
27463         
27464         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27465             if (w.indexOf(tag) > -1) {
27466                 return;
27467             }
27468             this.black.push(tag);
27469             
27470         }, this);
27471         
27472         Roo.each(b, function(tag) {
27473             if (w.indexOf(tag) > -1) {
27474                 return;
27475             }
27476             if (this.black.indexOf(tag) > -1) {
27477                 return;
27478             }
27479             this.black.push(tag);
27480             
27481         }, this);
27482         
27483         
27484         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27485         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27486         
27487         this.cwhite = [];
27488         this.cblack = [];
27489         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27490             if (b.indexOf(tag) > -1) {
27491                 return;
27492             }
27493             this.cwhite.push(tag);
27494             
27495         }, this);
27496         
27497         Roo.each(w, function(tag) {
27498             if (b.indexOf(tag) > -1) {
27499                 return;
27500             }
27501             if (this.cwhite.indexOf(tag) > -1) {
27502                 return;
27503             }
27504             this.cwhite.push(tag);
27505             
27506         }, this);
27507         
27508         
27509         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27510             if (w.indexOf(tag) > -1) {
27511                 return;
27512             }
27513             this.cblack.push(tag);
27514             
27515         }, this);
27516         
27517         Roo.each(b, function(tag) {
27518             if (w.indexOf(tag) > -1) {
27519                 return;
27520             }
27521             if (this.cblack.indexOf(tag) > -1) {
27522                 return;
27523             }
27524             this.cblack.push(tag);
27525             
27526         }, this);
27527     },
27528     
27529     setStylesheets : function(stylesheets)
27530     {
27531         if(typeof(stylesheets) == 'string'){
27532             Roo.get(this.iframe.contentDocument.head).createChild({
27533                 tag : 'link',
27534                 rel : 'stylesheet',
27535                 type : 'text/css',
27536                 href : stylesheets
27537             });
27538             
27539             return;
27540         }
27541         var _this = this;
27542      
27543         Roo.each(stylesheets, function(s) {
27544             if(!s.length){
27545                 return;
27546             }
27547             
27548             Roo.get(_this.iframe.contentDocument.head).createChild({
27549                 tag : 'link',
27550                 rel : 'stylesheet',
27551                 type : 'text/css',
27552                 href : s
27553             });
27554         });
27555
27556         
27557     },
27558     
27559     removeStylesheets : function()
27560     {
27561         var _this = this;
27562         
27563         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27564             s.remove();
27565         });
27566     },
27567     
27568     setStyle : function(style)
27569     {
27570         Roo.get(this.iframe.contentDocument.head).createChild({
27571             tag : 'style',
27572             type : 'text/css',
27573             html : style
27574         });
27575
27576         return;
27577     }
27578     
27579     // hide stuff that is not compatible
27580     /**
27581      * @event blur
27582      * @hide
27583      */
27584     /**
27585      * @event change
27586      * @hide
27587      */
27588     /**
27589      * @event focus
27590      * @hide
27591      */
27592     /**
27593      * @event specialkey
27594      * @hide
27595      */
27596     /**
27597      * @cfg {String} fieldClass @hide
27598      */
27599     /**
27600      * @cfg {String} focusClass @hide
27601      */
27602     /**
27603      * @cfg {String} autoCreate @hide
27604      */
27605     /**
27606      * @cfg {String} inputType @hide
27607      */
27608     /**
27609      * @cfg {String} invalidClass @hide
27610      */
27611     /**
27612      * @cfg {String} invalidText @hide
27613      */
27614     /**
27615      * @cfg {String} msgFx @hide
27616      */
27617     /**
27618      * @cfg {String} validateOnBlur @hide
27619      */
27620 });
27621
27622 Roo.HtmlEditorCore.white = [
27623         'area', 'br', 'img', 'input', 'hr', 'wbr',
27624         
27625        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27626        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27627        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27628        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27629        'table',   'ul',         'xmp', 
27630        
27631        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27632       'thead',   'tr', 
27633      
27634       'dir', 'menu', 'ol', 'ul', 'dl',
27635        
27636       'embed',  'object'
27637 ];
27638
27639
27640 Roo.HtmlEditorCore.black = [
27641     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27642         'applet', // 
27643         'base',   'basefont', 'bgsound', 'blink',  'body', 
27644         'frame',  'frameset', 'head',    'html',   'ilayer', 
27645         'iframe', 'layer',  'link',     'meta',    'object',   
27646         'script', 'style' ,'title',  'xml' // clean later..
27647 ];
27648 Roo.HtmlEditorCore.clean = [
27649     'script', 'style', 'title', 'xml'
27650 ];
27651 Roo.HtmlEditorCore.remove = [
27652     'font'
27653 ];
27654 // attributes..
27655
27656 Roo.HtmlEditorCore.ablack = [
27657     'on'
27658 ];
27659     
27660 Roo.HtmlEditorCore.aclean = [ 
27661     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27662 ];
27663
27664 // protocols..
27665 Roo.HtmlEditorCore.pwhite= [
27666         'http',  'https',  'mailto'
27667 ];
27668
27669 // white listed style attributes.
27670 Roo.HtmlEditorCore.cwhite= [
27671       //  'text-align', /// default is to allow most things..
27672       
27673          
27674 //        'font-size'//??
27675 ];
27676
27677 // black listed style attributes.
27678 Roo.HtmlEditorCore.cblack= [
27679       //  'font-size' -- this can be set by the project 
27680 ];
27681
27682
27683 Roo.HtmlEditorCore.swapCodes   =[ 
27684     [    8211, "&#8211;" ], 
27685     [    8212, "&#8212;" ], 
27686     [    8216,  "'" ],  
27687     [    8217, "'" ],  
27688     [    8220, '"' ],  
27689     [    8221, '"' ],  
27690     [    8226, "*" ],  
27691     [    8230, "..." ]
27692 ]; 
27693
27694     /*
27695  * - LGPL
27696  *
27697  * HtmlEditor
27698  * 
27699  */
27700
27701 /**
27702  * @class Roo.bootstrap.form.HtmlEditor
27703  * @extends Roo.bootstrap.form.TextArea
27704  * Bootstrap HtmlEditor class
27705
27706  * @constructor
27707  * Create a new HtmlEditor
27708  * @param {Object} config The config object
27709  */
27710
27711 Roo.bootstrap.form.HtmlEditor = function(config){
27712     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27713     if (!this.toolbars) {
27714         this.toolbars = [];
27715     }
27716     
27717     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27718     this.addEvents({
27719             /**
27720              * @event initialize
27721              * Fires when the editor is fully initialized (including the iframe)
27722              * @param {HtmlEditor} this
27723              */
27724             initialize: true,
27725             /**
27726              * @event activate
27727              * Fires when the editor is first receives the focus. Any insertion must wait
27728              * until after this event.
27729              * @param {HtmlEditor} this
27730              */
27731             activate: true,
27732              /**
27733              * @event beforesync
27734              * Fires before the textarea is updated with content from the editor iframe. Return false
27735              * to cancel the sync.
27736              * @param {HtmlEditor} this
27737              * @param {String} html
27738              */
27739             beforesync: true,
27740              /**
27741              * @event beforepush
27742              * Fires before the iframe editor is updated with content from the textarea. Return false
27743              * to cancel the push.
27744              * @param {HtmlEditor} this
27745              * @param {String} html
27746              */
27747             beforepush: true,
27748              /**
27749              * @event sync
27750              * Fires when the textarea is updated with content from the editor iframe.
27751              * @param {HtmlEditor} this
27752              * @param {String} html
27753              */
27754             sync: true,
27755              /**
27756              * @event push
27757              * Fires when the iframe editor is updated with content from the textarea.
27758              * @param {HtmlEditor} this
27759              * @param {String} html
27760              */
27761             push: true,
27762              /**
27763              * @event editmodechange
27764              * Fires when the editor switches edit modes
27765              * @param {HtmlEditor} this
27766              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27767              */
27768             editmodechange: true,
27769             /**
27770              * @event editorevent
27771              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27772              * @param {HtmlEditor} this
27773              */
27774             editorevent: true,
27775             /**
27776              * @event firstfocus
27777              * Fires when on first focus - needed by toolbars..
27778              * @param {HtmlEditor} this
27779              */
27780             firstfocus: true,
27781             /**
27782              * @event autosave
27783              * Auto save the htmlEditor value as a file into Events
27784              * @param {HtmlEditor} this
27785              */
27786             autosave: true,
27787             /**
27788              * @event savedpreview
27789              * preview the saved version of htmlEditor
27790              * @param {HtmlEditor} this
27791              */
27792             savedpreview: true
27793         });
27794 };
27795
27796
27797 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27798     
27799     
27800       /**
27801      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27802      */
27803     toolbars : false,
27804     
27805      /**
27806     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27807     */
27808     btns : [],
27809    
27810      /**
27811      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27812      *                        Roo.resizable.
27813      */
27814     resizable : false,
27815      /**
27816      * @cfg {Number} height (in pixels)
27817      */   
27818     height: 300,
27819    /**
27820      * @cfg {Number} width (in pixels)
27821      */   
27822     width: false,
27823     
27824     /**
27825      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27826      * 
27827      */
27828     stylesheets: false,
27829     
27830     // id of frame..
27831     frameId: false,
27832     
27833     // private properties
27834     validationEvent : false,
27835     deferHeight: true,
27836     initialized : false,
27837     activated : false,
27838     
27839     onFocus : Roo.emptyFn,
27840     iframePad:3,
27841     hideMode:'offsets',
27842     
27843     tbContainer : false,
27844     
27845     bodyCls : '',
27846     
27847     toolbarContainer :function() {
27848         return this.wrap.select('.x-html-editor-tb',true).first();
27849     },
27850
27851     /**
27852      * Protected method that will not generally be called directly. It
27853      * is called when the editor creates its toolbar. Override this method if you need to
27854      * add custom toolbar buttons.
27855      * @param {HtmlEditor} editor
27856      */
27857     createToolbar : function(){
27858         Roo.log('renewing');
27859         Roo.log("create toolbars");
27860         
27861         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27862         this.toolbars[0].render(this.toolbarContainer());
27863         
27864         return;
27865         
27866 //        if (!editor.toolbars || !editor.toolbars.length) {
27867 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27868 //        }
27869 //        
27870 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27871 //            editor.toolbars[i] = Roo.factory(
27872 //                    typeof(editor.toolbars[i]) == 'string' ?
27873 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27874 //                Roo.bootstrap.form.HtmlEditor);
27875 //            editor.toolbars[i].init(editor);
27876 //        }
27877     },
27878
27879      
27880     // private
27881     onRender : function(ct, position)
27882     {
27883        // Roo.log("Call onRender: " + this.xtype);
27884         var _t = this;
27885         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27886       
27887         this.wrap = this.inputEl().wrap({
27888             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27889         });
27890         
27891         this.editorcore.onRender(ct, position);
27892          
27893         if (this.resizable) {
27894             this.resizeEl = new Roo.Resizable(this.wrap, {
27895                 pinned : true,
27896                 wrap: true,
27897                 dynamic : true,
27898                 minHeight : this.height,
27899                 height: this.height,
27900                 handles : this.resizable,
27901                 width: this.width,
27902                 listeners : {
27903                     resize : function(r, w, h) {
27904                         _t.onResize(w,h); // -something
27905                     }
27906                 }
27907             });
27908             
27909         }
27910         this.createToolbar(this);
27911        
27912         
27913         if(!this.width && this.resizable){
27914             this.setSize(this.wrap.getSize());
27915         }
27916         if (this.resizeEl) {
27917             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27918             // should trigger onReize..
27919         }
27920         
27921     },
27922
27923     // private
27924     onResize : function(w, h)
27925     {
27926         Roo.log('resize: ' +w + ',' + h );
27927         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27928         var ew = false;
27929         var eh = false;
27930         
27931         if(this.inputEl() ){
27932             if(typeof w == 'number'){
27933                 var aw = w - this.wrap.getFrameWidth('lr');
27934                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27935                 ew = aw;
27936             }
27937             if(typeof h == 'number'){
27938                  var tbh = -11;  // fixme it needs to tool bar size!
27939                 for (var i =0; i < this.toolbars.length;i++) {
27940                     // fixme - ask toolbars for heights?
27941                     tbh += this.toolbars[i].el.getHeight();
27942                     //if (this.toolbars[i].footer) {
27943                     //    tbh += this.toolbars[i].footer.el.getHeight();
27944                     //}
27945                 }
27946               
27947                 
27948                 
27949                 
27950                 
27951                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27952                 ah -= 5; // knock a few pixes off for look..
27953                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27954                 var eh = ah;
27955             }
27956         }
27957         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27958         this.editorcore.onResize(ew,eh);
27959         
27960     },
27961
27962     /**
27963      * Toggles the editor between standard and source edit mode.
27964      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27965      */
27966     toggleSourceEdit : function(sourceEditMode)
27967     {
27968         this.editorcore.toggleSourceEdit(sourceEditMode);
27969         
27970         if(this.editorcore.sourceEditMode){
27971             Roo.log('editor - showing textarea');
27972             
27973 //            Roo.log('in');
27974 //            Roo.log(this.syncValue());
27975             this.syncValue();
27976             this.inputEl().removeClass(['hide', 'x-hidden']);
27977             this.inputEl().dom.removeAttribute('tabIndex');
27978             this.inputEl().focus();
27979         }else{
27980             Roo.log('editor - hiding textarea');
27981 //            Roo.log('out')
27982 //            Roo.log(this.pushValue()); 
27983             this.pushValue();
27984             
27985             this.inputEl().addClass(['hide', 'x-hidden']);
27986             this.inputEl().dom.setAttribute('tabIndex', -1);
27987             //this.deferFocus();
27988         }
27989          
27990         if(this.resizable){
27991             this.setSize(this.wrap.getSize());
27992         }
27993         
27994         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27995     },
27996  
27997     // private (for BoxComponent)
27998     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27999
28000     // private (for BoxComponent)
28001     getResizeEl : function(){
28002         return this.wrap;
28003     },
28004
28005     // private (for BoxComponent)
28006     getPositionEl : function(){
28007         return this.wrap;
28008     },
28009
28010     // private
28011     initEvents : function(){
28012         this.originalValue = this.getValue();
28013     },
28014
28015 //    /**
28016 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28017 //     * @method
28018 //     */
28019 //    markInvalid : Roo.emptyFn,
28020 //    /**
28021 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28022 //     * @method
28023 //     */
28024 //    clearInvalid : Roo.emptyFn,
28025
28026     setValue : function(v){
28027         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28028         this.editorcore.pushValue();
28029     },
28030
28031      
28032     // private
28033     deferFocus : function(){
28034         this.focus.defer(10, this);
28035     },
28036
28037     // doc'ed in Field
28038     focus : function(){
28039         this.editorcore.focus();
28040         
28041     },
28042       
28043
28044     // private
28045     onDestroy : function(){
28046         
28047         
28048         
28049         if(this.rendered){
28050             
28051             for (var i =0; i < this.toolbars.length;i++) {
28052                 // fixme - ask toolbars for heights?
28053                 this.toolbars[i].onDestroy();
28054             }
28055             
28056             this.wrap.dom.innerHTML = '';
28057             this.wrap.remove();
28058         }
28059     },
28060
28061     // private
28062     onFirstFocus : function(){
28063         //Roo.log("onFirstFocus");
28064         this.editorcore.onFirstFocus();
28065          for (var i =0; i < this.toolbars.length;i++) {
28066             this.toolbars[i].onFirstFocus();
28067         }
28068         
28069     },
28070     
28071     // private
28072     syncValue : function()
28073     {   
28074         this.editorcore.syncValue();
28075     },
28076     
28077     pushValue : function()
28078     {   
28079         this.editorcore.pushValue();
28080     }
28081      
28082     
28083     // hide stuff that is not compatible
28084     /**
28085      * @event blur
28086      * @hide
28087      */
28088     /**
28089      * @event change
28090      * @hide
28091      */
28092     /**
28093      * @event focus
28094      * @hide
28095      */
28096     /**
28097      * @event specialkey
28098      * @hide
28099      */
28100     /**
28101      * @cfg {String} fieldClass @hide
28102      */
28103     /**
28104      * @cfg {String} focusClass @hide
28105      */
28106     /**
28107      * @cfg {String} autoCreate @hide
28108      */
28109     /**
28110      * @cfg {String} inputType @hide
28111      */
28112      
28113     /**
28114      * @cfg {String} invalidText @hide
28115      */
28116     /**
28117      * @cfg {String} msgFx @hide
28118      */
28119     /**
28120      * @cfg {String} validateOnBlur @hide
28121      */
28122 });
28123  
28124     
28125    
28126    
28127    
28128       
28129 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28130 /**
28131  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28132  * @parent Roo.bootstrap.form.HtmlEditor
28133  * @extends Roo.bootstrap.nav.Simplebar
28134  * Basic Toolbar
28135  * 
28136  * @example
28137  * Usage:
28138  *
28139  new Roo.bootstrap.form.HtmlEditor({
28140     ....
28141     toolbars : [
28142         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28143             disable : { fonts: 1 , format: 1, ..., ... , ...],
28144             btns : [ .... ]
28145         })
28146     }
28147      
28148  * 
28149  * @cfg {Object} disable List of elements to disable..
28150  * @cfg {Array} btns List of additional buttons.
28151  * 
28152  * 
28153  * NEEDS Extra CSS? 
28154  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28155  */
28156  
28157 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28158 {
28159     
28160     Roo.apply(this, config);
28161     
28162     // default disabled, based on 'good practice'..
28163     this.disable = this.disable || {};
28164     Roo.applyIf(this.disable, {
28165         fontSize : true,
28166         colors : true,
28167         specialElements : true
28168     });
28169     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28170     
28171     this.editor = config.editor;
28172     this.editorcore = config.editor.editorcore;
28173     
28174     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28175     
28176     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28177     // dont call parent... till later.
28178 }
28179 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28180      
28181     bar : true,
28182     
28183     editor : false,
28184     editorcore : false,
28185     
28186     
28187     formats : [
28188         "p" ,  
28189         "h1","h2","h3","h4","h5","h6", 
28190         "pre", "code", 
28191         "abbr", "acronym", "address", "cite", "samp", "var",
28192         'div','span'
28193     ],
28194     
28195     onRender : function(ct, position)
28196     {
28197        // Roo.log("Call onRender: " + this.xtype);
28198         
28199        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28200        Roo.log(this.el);
28201        this.el.dom.style.marginBottom = '0';
28202        var _this = this;
28203        var editorcore = this.editorcore;
28204        var editor= this.editor;
28205        
28206        var children = [];
28207        var btn = function(id,cmd , toggle, handler, html){
28208        
28209             var  event = toggle ? 'toggle' : 'click';
28210        
28211             var a = {
28212                 size : 'sm',
28213                 xtype: 'Button',
28214                 xns: Roo.bootstrap,
28215                 //glyphicon : id,
28216                 fa: id,
28217                 cmd : id || cmd,
28218                 enableToggle:toggle !== false,
28219                 html : html || '',
28220                 pressed : toggle ? false : null,
28221                 listeners : {}
28222             };
28223             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28224                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28225             };
28226             children.push(a);
28227             return a;
28228        }
28229        
28230     //    var cb_box = function...
28231         
28232         var style = {
28233                 xtype: 'Button',
28234                 size : 'sm',
28235                 xns: Roo.bootstrap,
28236                 fa : 'font',
28237                 //html : 'submit'
28238                 menu : {
28239                     xtype: 'Menu',
28240                     xns: Roo.bootstrap,
28241                     items:  []
28242                 }
28243         };
28244         Roo.each(this.formats, function(f) {
28245             style.menu.items.push({
28246                 xtype :'MenuItem',
28247                 xns: Roo.bootstrap,
28248                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28249                 tagname : f,
28250                 listeners : {
28251                     click : function()
28252                     {
28253                         editorcore.insertTag(this.tagname);
28254                         editor.focus();
28255                     }
28256                 }
28257                 
28258             });
28259         });
28260         children.push(style);   
28261         
28262         btn('bold',false,true);
28263         btn('italic',false,true);
28264         btn('align-left', 'justifyleft',true);
28265         btn('align-center', 'justifycenter',true);
28266         btn('align-right' , 'justifyright',true);
28267         btn('link', false, false, function(btn) {
28268             //Roo.log("create link?");
28269             var url = prompt(this.createLinkText, this.defaultLinkValue);
28270             if(url && url != 'http:/'+'/'){
28271                 this.editorcore.relayCmd('createlink', url);
28272             }
28273         }),
28274         btn('list','insertunorderedlist',true);
28275         btn('pencil', false,true, function(btn){
28276                 Roo.log(this);
28277                 this.toggleSourceEdit(btn.pressed);
28278         });
28279         
28280         if (this.editor.btns.length > 0) {
28281             for (var i = 0; i<this.editor.btns.length; i++) {
28282                 children.push(this.editor.btns[i]);
28283             }
28284         }
28285         
28286         /*
28287         var cog = {
28288                 xtype: 'Button',
28289                 size : 'sm',
28290                 xns: Roo.bootstrap,
28291                 glyphicon : 'cog',
28292                 //html : 'submit'
28293                 menu : {
28294                     xtype: 'Menu',
28295                     xns: Roo.bootstrap,
28296                     items:  []
28297                 }
28298         };
28299         
28300         cog.menu.items.push({
28301             xtype :'MenuItem',
28302             xns: Roo.bootstrap,
28303             html : Clean styles,
28304             tagname : f,
28305             listeners : {
28306                 click : function()
28307                 {
28308                     editorcore.insertTag(this.tagname);
28309                     editor.focus();
28310                 }
28311             }
28312             
28313         });
28314        */
28315         
28316          
28317        this.xtype = 'NavSimplebar';
28318         
28319         for(var i=0;i< children.length;i++) {
28320             
28321             this.buttons.add(this.addxtypeChild(children[i]));
28322             
28323         }
28324         
28325         editor.on('editorevent', this.updateToolbar, this);
28326     },
28327     onBtnClick : function(id)
28328     {
28329        this.editorcore.relayCmd(id);
28330        this.editorcore.focus();
28331     },
28332     
28333     /**
28334      * Protected method that will not generally be called directly. It triggers
28335      * a toolbar update by reading the markup state of the current selection in the editor.
28336      */
28337     updateToolbar: function(){
28338
28339         if(!this.editorcore.activated){
28340             this.editor.onFirstFocus(); // is this neeed?
28341             return;
28342         }
28343
28344         var btns = this.buttons; 
28345         var doc = this.editorcore.doc;
28346         btns.get('bold').setActive(doc.queryCommandState('bold'));
28347         btns.get('italic').setActive(doc.queryCommandState('italic'));
28348         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28349         
28350         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28351         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28352         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28353         
28354         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28355         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28356          /*
28357         
28358         var ans = this.editorcore.getAllAncestors();
28359         if (this.formatCombo) {
28360             
28361             
28362             var store = this.formatCombo.store;
28363             this.formatCombo.setValue("");
28364             for (var i =0; i < ans.length;i++) {
28365                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28366                     // select it..
28367                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28368                     break;
28369                 }
28370             }
28371         }
28372         
28373         
28374         
28375         // hides menus... - so this cant be on a menu...
28376         Roo.bootstrap.MenuMgr.hideAll();
28377         */
28378         Roo.bootstrap.menu.Manager.hideAll();
28379         //this.editorsyncValue();
28380     },
28381     onFirstFocus: function() {
28382         this.buttons.each(function(item){
28383            item.enable();
28384         });
28385     },
28386     toggleSourceEdit : function(sourceEditMode){
28387         
28388           
28389         if(sourceEditMode){
28390             Roo.log("disabling buttons");
28391            this.buttons.each( function(item){
28392                 if(item.cmd != 'pencil'){
28393                     item.disable();
28394                 }
28395             });
28396           
28397         }else{
28398             Roo.log("enabling buttons");
28399             if(this.editorcore.initialized){
28400                 this.buttons.each( function(item){
28401                     item.enable();
28402                 });
28403             }
28404             
28405         }
28406         Roo.log("calling toggole on editor");
28407         // tell the editor that it's been pressed..
28408         this.editor.toggleSourceEdit(sourceEditMode);
28409        
28410     }
28411 });
28412
28413
28414
28415
28416  
28417 /*
28418  * - LGPL
28419  */
28420
28421 /**
28422  * @class Roo.bootstrap.form.Markdown
28423  * @extends Roo.bootstrap.form.TextArea
28424  * Bootstrap Showdown editable area
28425  * @cfg {string} content
28426  * 
28427  * @constructor
28428  * Create a new Showdown
28429  */
28430
28431 Roo.bootstrap.form.Markdown = function(config){
28432     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28433    
28434 };
28435
28436 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28437     
28438     editing :false,
28439     
28440     initEvents : function()
28441     {
28442         
28443         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28444         this.markdownEl = this.el.createChild({
28445             cls : 'roo-markdown-area'
28446         });
28447         this.inputEl().addClass('d-none');
28448         if (this.getValue() == '') {
28449             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28450             
28451         } else {
28452             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28453         }
28454         this.markdownEl.on('click', this.toggleTextEdit, this);
28455         this.on('blur', this.toggleTextEdit, this);
28456         this.on('specialkey', this.resizeTextArea, this);
28457     },
28458     
28459     toggleTextEdit : function()
28460     {
28461         var sh = this.markdownEl.getHeight();
28462         this.inputEl().addClass('d-none');
28463         this.markdownEl.addClass('d-none');
28464         if (!this.editing) {
28465             // show editor?
28466             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28467             this.inputEl().removeClass('d-none');
28468             this.inputEl().focus();
28469             this.editing = true;
28470             return;
28471         }
28472         // show showdown...
28473         this.updateMarkdown();
28474         this.markdownEl.removeClass('d-none');
28475         this.editing = false;
28476         return;
28477     },
28478     updateMarkdown : function()
28479     {
28480         if (this.getValue() == '') {
28481             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28482             return;
28483         }
28484  
28485         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28486     },
28487     
28488     resizeTextArea: function () {
28489         
28490         var sh = 100;
28491         Roo.log([sh, this.getValue().split("\n").length * 30]);
28492         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28493     },
28494     setValue : function(val)
28495     {
28496         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28497         if (!this.editing) {
28498             this.updateMarkdown();
28499         }
28500         
28501     },
28502     focus : function()
28503     {
28504         if (!this.editing) {
28505             this.toggleTextEdit();
28506         }
28507         
28508     }
28509
28510
28511 });/*
28512  * Based on:
28513  * Ext JS Library 1.1.1
28514  * Copyright(c) 2006-2007, Ext JS, LLC.
28515  *
28516  * Originally Released Under LGPL - original licence link has changed is not relivant.
28517  *
28518  * Fork - LGPL
28519  * <script type="text/javascript">
28520  */
28521  
28522 /**
28523  * @class Roo.bootstrap.PagingToolbar
28524  * @extends Roo.bootstrap.nav.Simplebar
28525  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28526  * @constructor
28527  * Create a new PagingToolbar
28528  * @param {Object} config The config object
28529  * @param {Roo.data.Store} store
28530  */
28531 Roo.bootstrap.PagingToolbar = function(config)
28532 {
28533     // old args format still supported... - xtype is prefered..
28534         // created from xtype...
28535     
28536     this.ds = config.dataSource;
28537     
28538     if (config.store && !this.ds) {
28539         this.store= Roo.factory(config.store, Roo.data);
28540         this.ds = this.store;
28541         this.ds.xmodule = this.xmodule || false;
28542     }
28543     
28544     this.toolbarItems = [];
28545     if (config.items) {
28546         this.toolbarItems = config.items;
28547     }
28548     
28549     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28550     
28551     this.cursor = 0;
28552     
28553     if (this.ds) { 
28554         this.bind(this.ds);
28555     }
28556     
28557     if (Roo.bootstrap.version == 4) {
28558         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28559     } else {
28560         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28561     }
28562     
28563 };
28564
28565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28566     /**
28567      * @cfg {Roo.bootstrap.Button} buttons[]
28568      * Buttons for the toolbar
28569      */
28570      /**
28571      * @cfg {Roo.data.Store} store
28572      * The underlying data store providing the paged data
28573      */
28574     /**
28575      * @cfg {String/HTMLElement/Element} container
28576      * container The id or element that will contain the toolbar
28577      */
28578     /**
28579      * @cfg {Boolean} displayInfo
28580      * True to display the displayMsg (defaults to false)
28581      */
28582     /**
28583      * @cfg {Number} pageSize
28584      * The number of records to display per page (defaults to 20)
28585      */
28586     pageSize: 20,
28587     /**
28588      * @cfg {String} displayMsg
28589      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28590      */
28591     displayMsg : 'Displaying {0} - {1} of {2}',
28592     /**
28593      * @cfg {String} emptyMsg
28594      * The message to display when no records are found (defaults to "No data to display")
28595      */
28596     emptyMsg : 'No data to display',
28597     /**
28598      * Customizable piece of the default paging text (defaults to "Page")
28599      * @type String
28600      */
28601     beforePageText : "Page",
28602     /**
28603      * Customizable piece of the default paging text (defaults to "of %0")
28604      * @type String
28605      */
28606     afterPageText : "of {0}",
28607     /**
28608      * Customizable piece of the default paging text (defaults to "First Page")
28609      * @type String
28610      */
28611     firstText : "First Page",
28612     /**
28613      * Customizable piece of the default paging text (defaults to "Previous Page")
28614      * @type String
28615      */
28616     prevText : "Previous Page",
28617     /**
28618      * Customizable piece of the default paging text (defaults to "Next Page")
28619      * @type String
28620      */
28621     nextText : "Next Page",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "Last Page")
28624      * @type String
28625      */
28626     lastText : "Last Page",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "Refresh")
28629      * @type String
28630      */
28631     refreshText : "Refresh",
28632
28633     buttons : false,
28634     // private
28635     onRender : function(ct, position) 
28636     {
28637         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28638         this.navgroup.parentId = this.id;
28639         this.navgroup.onRender(this.el, null);
28640         // add the buttons to the navgroup
28641         
28642         if(this.displayInfo){
28643             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28644             this.displayEl = this.el.select('.x-paging-info', true).first();
28645 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28646 //            this.displayEl = navel.el.select('span',true).first();
28647         }
28648         
28649         var _this = this;
28650         
28651         if(this.buttons){
28652             Roo.each(_this.buttons, function(e){ // this might need to use render????
28653                Roo.factory(e).render(_this.el);
28654             });
28655         }
28656             
28657         Roo.each(_this.toolbarItems, function(e) {
28658             _this.navgroup.addItem(e);
28659         });
28660         
28661         
28662         this.first = this.navgroup.addItem({
28663             tooltip: this.firstText,
28664             cls: "prev btn-outline-secondary",
28665             html : ' <i class="fa fa-step-backward"></i>',
28666             disabled: true,
28667             preventDefault: true,
28668             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28669         });
28670         
28671         this.prev =  this.navgroup.addItem({
28672             tooltip: this.prevText,
28673             cls: "prev btn-outline-secondary",
28674             html : ' <i class="fa fa-backward"></i>',
28675             disabled: true,
28676             preventDefault: true,
28677             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28678         });
28679     //this.addSeparator();
28680         
28681         
28682         var field = this.navgroup.addItem( {
28683             tagtype : 'span',
28684             cls : 'x-paging-position  btn-outline-secondary',
28685              disabled: true,
28686             html : this.beforePageText  +
28687                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28688                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28689          } ); //?? escaped?
28690         
28691         this.field = field.el.select('input', true).first();
28692         this.field.on("keydown", this.onPagingKeydown, this);
28693         this.field.on("focus", function(){this.dom.select();});
28694     
28695     
28696         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28697         //this.field.setHeight(18);
28698         //this.addSeparator();
28699         this.next = this.navgroup.addItem({
28700             tooltip: this.nextText,
28701             cls: "next btn-outline-secondary",
28702             html : ' <i class="fa fa-forward"></i>',
28703             disabled: true,
28704             preventDefault: true,
28705             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28706         });
28707         this.last = this.navgroup.addItem({
28708             tooltip: this.lastText,
28709             html : ' <i class="fa fa-step-forward"></i>',
28710             cls: "next btn-outline-secondary",
28711             disabled: true,
28712             preventDefault: true,
28713             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28714         });
28715     //this.addSeparator();
28716         this.loading = this.navgroup.addItem({
28717             tooltip: this.refreshText,
28718             cls: "btn-outline-secondary",
28719             html : ' <i class="fa fa-refresh"></i>',
28720             preventDefault: true,
28721             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28722         });
28723         
28724     },
28725
28726     // private
28727     updateInfo : function(){
28728         if(this.displayEl){
28729             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28730             var msg = count == 0 ?
28731                 this.emptyMsg :
28732                 String.format(
28733                     this.displayMsg,
28734                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28735                 );
28736             this.displayEl.update(msg);
28737         }
28738     },
28739
28740     // private
28741     onLoad : function(ds, r, o)
28742     {
28743         this.cursor = o.params && o.params.start ? o.params.start : 0;
28744         
28745         var d = this.getPageData(),
28746             ap = d.activePage,
28747             ps = d.pages;
28748         
28749         
28750         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28751         this.field.dom.value = ap;
28752         this.first.setDisabled(ap == 1);
28753         this.prev.setDisabled(ap == 1);
28754         this.next.setDisabled(ap == ps);
28755         this.last.setDisabled(ap == ps);
28756         this.loading.enable();
28757         this.updateInfo();
28758     },
28759
28760     // private
28761     getPageData : function(){
28762         var total = this.ds.getTotalCount();
28763         return {
28764             total : total,
28765             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28766             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28767         };
28768     },
28769
28770     // private
28771     onLoadError : function(){
28772         this.loading.enable();
28773     },
28774
28775     // private
28776     onPagingKeydown : function(e){
28777         var k = e.getKey();
28778         var d = this.getPageData();
28779         if(k == e.RETURN){
28780             var v = this.field.dom.value, pageNum;
28781             if(!v || isNaN(pageNum = parseInt(v, 10))){
28782                 this.field.dom.value = d.activePage;
28783                 return;
28784             }
28785             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28786             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28787             e.stopEvent();
28788         }
28789         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))
28790         {
28791           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28792           this.field.dom.value = pageNum;
28793           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28794           e.stopEvent();
28795         }
28796         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28797         {
28798           var v = this.field.dom.value, pageNum; 
28799           var increment = (e.shiftKey) ? 10 : 1;
28800           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28801                 increment *= -1;
28802           }
28803           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28804             this.field.dom.value = d.activePage;
28805             return;
28806           }
28807           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28808           {
28809             this.field.dom.value = parseInt(v, 10) + increment;
28810             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28811             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28812           }
28813           e.stopEvent();
28814         }
28815     },
28816
28817     // private
28818     beforeLoad : function(){
28819         if(this.loading){
28820             this.loading.disable();
28821         }
28822     },
28823
28824     // private
28825     onClick : function(which){
28826         
28827         var ds = this.ds;
28828         if (!ds) {
28829             return;
28830         }
28831         
28832         switch(which){
28833             case "first":
28834                 ds.load({params:{start: 0, limit: this.pageSize}});
28835             break;
28836             case "prev":
28837                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28838             break;
28839             case "next":
28840                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28841             break;
28842             case "last":
28843                 var total = ds.getTotalCount();
28844                 var extra = total % this.pageSize;
28845                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28846                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28847             break;
28848             case "refresh":
28849                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28850             break;
28851         }
28852     },
28853
28854     /**
28855      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28856      * @param {Roo.data.Store} store The data store to unbind
28857      */
28858     unbind : function(ds){
28859         ds.un("beforeload", this.beforeLoad, this);
28860         ds.un("load", this.onLoad, this);
28861         ds.un("loadexception", this.onLoadError, this);
28862         ds.un("remove", this.updateInfo, this);
28863         ds.un("add", this.updateInfo, this);
28864         this.ds = undefined;
28865     },
28866
28867     /**
28868      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28869      * @param {Roo.data.Store} store The data store to bind
28870      */
28871     bind : function(ds){
28872         ds.on("beforeload", this.beforeLoad, this);
28873         ds.on("load", this.onLoad, this);
28874         ds.on("loadexception", this.onLoadError, this);
28875         ds.on("remove", this.updateInfo, this);
28876         ds.on("add", this.updateInfo, this);
28877         this.ds = ds;
28878     }
28879 });/*
28880  * - LGPL
28881  *
28882  * element
28883  * 
28884  */
28885
28886 /**
28887  * @class Roo.bootstrap.MessageBar
28888  * @extends Roo.bootstrap.Component
28889  * Bootstrap MessageBar class
28890  * @cfg {String} html contents of the MessageBar
28891  * @cfg {String} weight (info | success | warning | danger) default info
28892  * @cfg {String} beforeClass insert the bar before the given class
28893  * @cfg {Boolean} closable (true | false) default false
28894  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28895  * 
28896  * @constructor
28897  * Create a new Element
28898  * @param {Object} config The config object
28899  */
28900
28901 Roo.bootstrap.MessageBar = function(config){
28902     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28903 };
28904
28905 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28906     
28907     html: '',
28908     weight: 'info',
28909     closable: false,
28910     fixed: false,
28911     beforeClass: 'bootstrap-sticky-wrap',
28912     
28913     getAutoCreate : function(){
28914         
28915         var cfg = {
28916             tag: 'div',
28917             cls: 'alert alert-dismissable alert-' + this.weight,
28918             cn: [
28919                 {
28920                     tag: 'span',
28921                     cls: 'message',
28922                     html: this.html || ''
28923                 }
28924             ]
28925         };
28926         
28927         if(this.fixed){
28928             cfg.cls += ' alert-messages-fixed';
28929         }
28930         
28931         if(this.closable){
28932             cfg.cn.push({
28933                 tag: 'button',
28934                 cls: 'close',
28935                 html: 'x'
28936             });
28937         }
28938         
28939         return cfg;
28940     },
28941     
28942     onRender : function(ct, position)
28943     {
28944         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28945         
28946         if(!this.el){
28947             var cfg = Roo.apply({},  this.getAutoCreate());
28948             cfg.id = Roo.id();
28949             
28950             if (this.cls) {
28951                 cfg.cls += ' ' + this.cls;
28952             }
28953             if (this.style) {
28954                 cfg.style = this.style;
28955             }
28956             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28957             
28958             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28959         }
28960         
28961         this.el.select('>button.close').on('click', this.hide, this);
28962         
28963     },
28964     
28965     show : function()
28966     {
28967         if (!this.rendered) {
28968             this.render();
28969         }
28970         
28971         this.el.show();
28972         
28973         this.fireEvent('show', this);
28974         
28975     },
28976     
28977     hide : function()
28978     {
28979         if (!this.rendered) {
28980             this.render();
28981         }
28982         
28983         this.el.hide();
28984         
28985         this.fireEvent('hide', this);
28986     },
28987     
28988     update : function()
28989     {
28990 //        var e = this.el.dom.firstChild;
28991 //        
28992 //        if(this.closable){
28993 //            e = e.nextSibling;
28994 //        }
28995 //        
28996 //        e.data = this.html || '';
28997
28998         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28999     }
29000    
29001 });
29002
29003  
29004
29005      /*
29006  * - LGPL
29007  *
29008  * Graph
29009  * 
29010  */
29011
29012
29013 /**
29014  * @class Roo.bootstrap.Graph
29015  * @extends Roo.bootstrap.Component
29016  * Bootstrap Graph class
29017 > Prameters
29018  -sm {number} sm 4
29019  -md {number} md 5
29020  @cfg {String} graphtype  bar | vbar | pie
29021  @cfg {number} g_x coodinator | centre x (pie)
29022  @cfg {number} g_y coodinator | centre y (pie)
29023  @cfg {number} g_r radius (pie)
29024  @cfg {number} g_height height of the chart (respected by all elements in the set)
29025  @cfg {number} g_width width of the chart (respected by all elements in the set)
29026  @cfg {Object} title The title of the chart
29027     
29028  -{Array}  values
29029  -opts (object) options for the chart 
29030      o {
29031      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29032      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29033      o vgutter (number)
29034      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.
29035      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29036      o to
29037      o stretch (boolean)
29038      o }
29039  -opts (object) options for the pie
29040      o{
29041      o cut
29042      o startAngle (number)
29043      o endAngle (number)
29044      } 
29045  *
29046  * @constructor
29047  * Create a new Input
29048  * @param {Object} config The config object
29049  */
29050
29051 Roo.bootstrap.Graph = function(config){
29052     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29053     
29054     this.addEvents({
29055         // img events
29056         /**
29057          * @event click
29058          * The img click event for the img.
29059          * @param {Roo.EventObject} e
29060          */
29061         "click" : true
29062     });
29063 };
29064
29065 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29066     
29067     sm: 4,
29068     md: 5,
29069     graphtype: 'bar',
29070     g_height: 250,
29071     g_width: 400,
29072     g_x: 50,
29073     g_y: 50,
29074     g_r: 30,
29075     opts:{
29076         //g_colors: this.colors,
29077         g_type: 'soft',
29078         g_gutter: '20%'
29079
29080     },
29081     title : false,
29082
29083     getAutoCreate : function(){
29084         
29085         var cfg = {
29086             tag: 'div',
29087             html : null
29088         };
29089         
29090         
29091         return  cfg;
29092     },
29093
29094     onRender : function(ct,position){
29095         
29096         
29097         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29098         
29099         if (typeof(Raphael) == 'undefined') {
29100             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29101             return;
29102         }
29103         
29104         this.raphael = Raphael(this.el.dom);
29105         
29106                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29107                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29110                 /*
29111                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29112                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29113                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29114                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29115                 
29116                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29117                 r.barchart(330, 10, 300, 220, data1);
29118                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29119                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29120                 */
29121                 
29122                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29123                 // r.barchart(30, 30, 560, 250,  xdata, {
29124                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29125                 //     axis : "0 0 1 1",
29126                 //     axisxlabels :  xdata
29127                 //     //yvalues : cols,
29128                    
29129                 // });
29130 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29131 //        
29132 //        this.load(null,xdata,{
29133 //                axis : "0 0 1 1",
29134 //                axisxlabels :  xdata
29135 //                });
29136
29137     },
29138
29139     load : function(graphtype,xdata,opts)
29140     {
29141         this.raphael.clear();
29142         if(!graphtype) {
29143             graphtype = this.graphtype;
29144         }
29145         if(!opts){
29146             opts = this.opts;
29147         }
29148         var r = this.raphael,
29149             fin = function () {
29150                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29151             },
29152             fout = function () {
29153                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29154             },
29155             pfin = function() {
29156                 this.sector.stop();
29157                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29158
29159                 if (this.label) {
29160                     this.label[0].stop();
29161                     this.label[0].attr({ r: 7.5 });
29162                     this.label[1].attr({ "font-weight": 800 });
29163                 }
29164             },
29165             pfout = function() {
29166                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29167
29168                 if (this.label) {
29169                     this.label[0].animate({ r: 5 }, 500, "bounce");
29170                     this.label[1].attr({ "font-weight": 400 });
29171                 }
29172             };
29173
29174         switch(graphtype){
29175             case 'bar':
29176                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29177                 break;
29178             case 'hbar':
29179                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29180                 break;
29181             case 'pie':
29182 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29183 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29184 //            
29185                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29186                 
29187                 break;
29188
29189         }
29190         
29191         if(this.title){
29192             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29193         }
29194         
29195     },
29196     
29197     setTitle: function(o)
29198     {
29199         this.title = o;
29200     },
29201     
29202     initEvents: function() {
29203         
29204         if(!this.href){
29205             this.el.on('click', this.onClick, this);
29206         }
29207     },
29208     
29209     onClick : function(e)
29210     {
29211         Roo.log('img onclick');
29212         this.fireEvent('click', this, e);
29213     }
29214    
29215 });
29216
29217  
29218 /*
29219  * - LGPL
29220  *
29221  * numberBox
29222  * 
29223  */
29224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29225
29226 /**
29227  * @class Roo.bootstrap.dash.NumberBox
29228  * @extends Roo.bootstrap.Component
29229  * Bootstrap NumberBox class
29230  * @cfg {String} headline Box headline
29231  * @cfg {String} content Box content
29232  * @cfg {String} icon Box icon
29233  * @cfg {String} footer Footer text
29234  * @cfg {String} fhref Footer href
29235  * 
29236  * @constructor
29237  * Create a new NumberBox
29238  * @param {Object} config The config object
29239  */
29240
29241
29242 Roo.bootstrap.dash.NumberBox = function(config){
29243     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29244     
29245 };
29246
29247 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29248     
29249     headline : '',
29250     content : '',
29251     icon : '',
29252     footer : '',
29253     fhref : '',
29254     ficon : '',
29255     
29256     getAutoCreate : function(){
29257         
29258         var cfg = {
29259             tag : 'div',
29260             cls : 'small-box ',
29261             cn : [
29262                 {
29263                     tag : 'div',
29264                     cls : 'inner',
29265                     cn :[
29266                         {
29267                             tag : 'h3',
29268                             cls : 'roo-headline',
29269                             html : this.headline
29270                         },
29271                         {
29272                             tag : 'p',
29273                             cls : 'roo-content',
29274                             html : this.content
29275                         }
29276                     ]
29277                 }
29278             ]
29279         };
29280         
29281         if(this.icon){
29282             cfg.cn.push({
29283                 tag : 'div',
29284                 cls : 'icon',
29285                 cn :[
29286                     {
29287                         tag : 'i',
29288                         cls : 'ion ' + this.icon
29289                     }
29290                 ]
29291             });
29292         }
29293         
29294         if(this.footer){
29295             var footer = {
29296                 tag : 'a',
29297                 cls : 'small-box-footer',
29298                 href : this.fhref || '#',
29299                 html : this.footer
29300             };
29301             
29302             cfg.cn.push(footer);
29303             
29304         }
29305         
29306         return  cfg;
29307     },
29308
29309     onRender : function(ct,position){
29310         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29311
29312
29313        
29314                 
29315     },
29316
29317     setHeadline: function (value)
29318     {
29319         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29320     },
29321     
29322     setFooter: function (value, href)
29323     {
29324         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29325         
29326         if(href){
29327             this.el.select('a.small-box-footer',true).first().attr('href', href);
29328         }
29329         
29330     },
29331
29332     setContent: function (value)
29333     {
29334         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29335     },
29336
29337     initEvents: function() 
29338     {   
29339         
29340     }
29341     
29342 });
29343
29344  
29345 /*
29346  * - LGPL
29347  *
29348  * TabBox
29349  * 
29350  */
29351 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29352
29353 /**
29354  * @class Roo.bootstrap.dash.TabBox
29355  * @extends Roo.bootstrap.Component
29356  * @children Roo.bootstrap.dash.TabPane
29357  * Bootstrap TabBox class
29358  * @cfg {String} title Title of the TabBox
29359  * @cfg {String} icon Icon of the TabBox
29360  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29361  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29362  * 
29363  * @constructor
29364  * Create a new TabBox
29365  * @param {Object} config The config object
29366  */
29367
29368
29369 Roo.bootstrap.dash.TabBox = function(config){
29370     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29371     this.addEvents({
29372         // raw events
29373         /**
29374          * @event addpane
29375          * When a pane is added
29376          * @param {Roo.bootstrap.dash.TabPane} pane
29377          */
29378         "addpane" : true,
29379         /**
29380          * @event activatepane
29381          * When a pane is activated
29382          * @param {Roo.bootstrap.dash.TabPane} pane
29383          */
29384         "activatepane" : true
29385         
29386          
29387     });
29388     
29389     this.panes = [];
29390 };
29391
29392 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29393
29394     title : '',
29395     icon : false,
29396     showtabs : true,
29397     tabScrollable : false,
29398     
29399     getChildContainer : function()
29400     {
29401         return this.el.select('.tab-content', true).first();
29402     },
29403     
29404     getAutoCreate : function(){
29405         
29406         var header = {
29407             tag: 'li',
29408             cls: 'pull-left header',
29409             html: this.title,
29410             cn : []
29411         };
29412         
29413         if(this.icon){
29414             header.cn.push({
29415                 tag: 'i',
29416                 cls: 'fa ' + this.icon
29417             });
29418         }
29419         
29420         var h = {
29421             tag: 'ul',
29422             cls: 'nav nav-tabs pull-right',
29423             cn: [
29424                 header
29425             ]
29426         };
29427         
29428         if(this.tabScrollable){
29429             h = {
29430                 tag: 'div',
29431                 cls: 'tab-header',
29432                 cn: [
29433                     {
29434                         tag: 'ul',
29435                         cls: 'nav nav-tabs pull-right',
29436                         cn: [
29437                             header
29438                         ]
29439                     }
29440                 ]
29441             };
29442         }
29443         
29444         var cfg = {
29445             tag: 'div',
29446             cls: 'nav-tabs-custom',
29447             cn: [
29448                 h,
29449                 {
29450                     tag: 'div',
29451                     cls: 'tab-content no-padding',
29452                     cn: []
29453                 }
29454             ]
29455         };
29456
29457         return  cfg;
29458     },
29459     initEvents : function()
29460     {
29461         //Roo.log('add add pane handler');
29462         this.on('addpane', this.onAddPane, this);
29463     },
29464      /**
29465      * Updates the box title
29466      * @param {String} html to set the title to.
29467      */
29468     setTitle : function(value)
29469     {
29470         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29471     },
29472     onAddPane : function(pane)
29473     {
29474         this.panes.push(pane);
29475         //Roo.log('addpane');
29476         //Roo.log(pane);
29477         // tabs are rendere left to right..
29478         if(!this.showtabs){
29479             return;
29480         }
29481         
29482         var ctr = this.el.select('.nav-tabs', true).first();
29483          
29484          
29485         var existing = ctr.select('.nav-tab',true);
29486         var qty = existing.getCount();;
29487         
29488         
29489         var tab = ctr.createChild({
29490             tag : 'li',
29491             cls : 'nav-tab' + (qty ? '' : ' active'),
29492             cn : [
29493                 {
29494                     tag : 'a',
29495                     href:'#',
29496                     html : pane.title
29497                 }
29498             ]
29499         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29500         pane.tab = tab;
29501         
29502         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29503         if (!qty) {
29504             pane.el.addClass('active');
29505         }
29506         
29507                 
29508     },
29509     onTabClick : function(ev,un,ob,pane)
29510     {
29511         //Roo.log('tab - prev default');
29512         ev.preventDefault();
29513         
29514         
29515         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29516         pane.tab.addClass('active');
29517         //Roo.log(pane.title);
29518         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29519         // technically we should have a deactivate event.. but maybe add later.
29520         // and it should not de-activate the selected tab...
29521         this.fireEvent('activatepane', pane);
29522         pane.el.addClass('active');
29523         pane.fireEvent('activate');
29524         
29525         
29526     },
29527     
29528     getActivePane : function()
29529     {
29530         var r = false;
29531         Roo.each(this.panes, function(p) {
29532             if(p.el.hasClass('active')){
29533                 r = p;
29534                 return false;
29535             }
29536             
29537             return;
29538         });
29539         
29540         return r;
29541     }
29542     
29543     
29544 });
29545
29546  
29547 /*
29548  * - LGPL
29549  *
29550  * Tab pane
29551  * 
29552  */
29553 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29554 /**
29555  * @class Roo.bootstrap.TabPane
29556  * @extends Roo.bootstrap.Component
29557  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29558  * Bootstrap TabPane class
29559  * @cfg {Boolean} active (false | true) Default false
29560  * @cfg {String} title title of panel
29561
29562  * 
29563  * @constructor
29564  * Create a new TabPane
29565  * @param {Object} config The config object
29566  */
29567
29568 Roo.bootstrap.dash.TabPane = function(config){
29569     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29570     
29571     this.addEvents({
29572         // raw events
29573         /**
29574          * @event activate
29575          * When a pane is activated
29576          * @param {Roo.bootstrap.dash.TabPane} pane
29577          */
29578         "activate" : true
29579          
29580     });
29581 };
29582
29583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29584     
29585     active : false,
29586     title : '',
29587     
29588     // the tabBox that this is attached to.
29589     tab : false,
29590      
29591     getAutoCreate : function() 
29592     {
29593         var cfg = {
29594             tag: 'div',
29595             cls: 'tab-pane'
29596         };
29597         
29598         if(this.active){
29599             cfg.cls += ' active';
29600         }
29601         
29602         return cfg;
29603     },
29604     initEvents  : function()
29605     {
29606         //Roo.log('trigger add pane handler');
29607         this.parent().fireEvent('addpane', this)
29608     },
29609     
29610      /**
29611      * Updates the tab title 
29612      * @param {String} html to set the title to.
29613      */
29614     setTitle: function(str)
29615     {
29616         if (!this.tab) {
29617             return;
29618         }
29619         this.title = str;
29620         this.tab.select('a', true).first().dom.innerHTML = str;
29621         
29622     }
29623     
29624     
29625     
29626 });
29627
29628  
29629
29630
29631  /*
29632  * - LGPL
29633  *
29634  * Tooltip
29635  * 
29636  */
29637
29638 /**
29639  * @class Roo.bootstrap.Tooltip
29640  * Bootstrap Tooltip class
29641  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29642  * to determine which dom element triggers the tooltip.
29643  * 
29644  * It needs to add support for additional attributes like tooltip-position
29645  * 
29646  * @constructor
29647  * Create a new Toolti
29648  * @param {Object} config The config object
29649  */
29650
29651 Roo.bootstrap.Tooltip = function(config){
29652     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29653     
29654     this.alignment = Roo.bootstrap.Tooltip.alignment;
29655     
29656     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29657         this.alignment = config.alignment;
29658     }
29659     
29660 };
29661
29662 Roo.apply(Roo.bootstrap.Tooltip, {
29663     /**
29664      * @function init initialize tooltip monitoring.
29665      * @static
29666      */
29667     currentEl : false,
29668     currentTip : false,
29669     currentRegion : false,
29670     
29671     //  init : delay?
29672     
29673     init : function()
29674     {
29675         Roo.get(document).on('mouseover', this.enter ,this);
29676         Roo.get(document).on('mouseout', this.leave, this);
29677          
29678         
29679         this.currentTip = new Roo.bootstrap.Tooltip();
29680     },
29681     
29682     enter : function(ev)
29683     {
29684         var dom = ev.getTarget();
29685         
29686         //Roo.log(['enter',dom]);
29687         var el = Roo.fly(dom);
29688         if (this.currentEl) {
29689             //Roo.log(dom);
29690             //Roo.log(this.currentEl);
29691             //Roo.log(this.currentEl.contains(dom));
29692             if (this.currentEl == el) {
29693                 return;
29694             }
29695             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29696                 return;
29697             }
29698
29699         }
29700         
29701         if (this.currentTip.el) {
29702             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29703         }    
29704         //Roo.log(ev);
29705         
29706         if(!el || el.dom == document){
29707             return;
29708         }
29709         
29710         var bindEl = el; 
29711         var pel = false;
29712         if (!el.attr('tooltip')) {
29713             pel = el.findParent("[tooltip]");
29714             if (pel) {
29715                 bindEl = Roo.get(pel);
29716             }
29717         }
29718         
29719        
29720         
29721         // you can not look for children, as if el is the body.. then everythign is the child..
29722         if (!pel && !el.attr('tooltip')) { //
29723             if (!el.select("[tooltip]").elements.length) {
29724                 return;
29725             }
29726             // is the mouse over this child...?
29727             bindEl = el.select("[tooltip]").first();
29728             var xy = ev.getXY();
29729             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29730                 //Roo.log("not in region.");
29731                 return;
29732             }
29733             //Roo.log("child element over..");
29734             
29735         }
29736         this.currentEl = el;
29737         this.currentTip.bind(bindEl);
29738         this.currentRegion = Roo.lib.Region.getRegion(dom);
29739         this.currentTip.enter();
29740         
29741     },
29742     leave : function(ev)
29743     {
29744         var dom = ev.getTarget();
29745         //Roo.log(['leave',dom]);
29746         if (!this.currentEl) {
29747             return;
29748         }
29749         
29750         
29751         if (dom != this.currentEl.dom) {
29752             return;
29753         }
29754         var xy = ev.getXY();
29755         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29756             return;
29757         }
29758         // only activate leave if mouse cursor is outside... bounding box..
29759         
29760         
29761         
29762         
29763         if (this.currentTip) {
29764             this.currentTip.leave();
29765         }
29766         //Roo.log('clear currentEl');
29767         this.currentEl = false;
29768         
29769         
29770     },
29771     alignment : {
29772         'left' : ['r-l', [-2,0], 'right'],
29773         'right' : ['l-r', [2,0], 'left'],
29774         'bottom' : ['t-b', [0,2], 'top'],
29775         'top' : [ 'b-t', [0,-2], 'bottom']
29776     }
29777     
29778 });
29779
29780
29781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29782     
29783     
29784     bindEl : false,
29785     
29786     delay : null, // can be { show : 300 , hide: 500}
29787     
29788     timeout : null,
29789     
29790     hoverState : null, //???
29791     
29792     placement : 'bottom', 
29793     
29794     alignment : false,
29795     
29796     getAutoCreate : function(){
29797     
29798         var cfg = {
29799            cls : 'tooltip',   
29800            role : 'tooltip',
29801            cn : [
29802                 {
29803                     cls : 'tooltip-arrow arrow'
29804                 },
29805                 {
29806                     cls : 'tooltip-inner'
29807                 }
29808            ]
29809         };
29810         
29811         return cfg;
29812     },
29813     bind : function(el)
29814     {
29815         this.bindEl = el;
29816     },
29817     
29818     initEvents : function()
29819     {
29820         this.arrowEl = this.el.select('.arrow', true).first();
29821         this.innerEl = this.el.select('.tooltip-inner', true).first();
29822     },
29823     
29824     enter : function () {
29825        
29826         if (this.timeout != null) {
29827             clearTimeout(this.timeout);
29828         }
29829         
29830         this.hoverState = 'in';
29831          //Roo.log("enter - show");
29832         if (!this.delay || !this.delay.show) {
29833             this.show();
29834             return;
29835         }
29836         var _t = this;
29837         this.timeout = setTimeout(function () {
29838             if (_t.hoverState == 'in') {
29839                 _t.show();
29840             }
29841         }, this.delay.show);
29842     },
29843     leave : function()
29844     {
29845         clearTimeout(this.timeout);
29846     
29847         this.hoverState = 'out';
29848          if (!this.delay || !this.delay.hide) {
29849             this.hide();
29850             return;
29851         }
29852        
29853         var _t = this;
29854         this.timeout = setTimeout(function () {
29855             //Roo.log("leave - timeout");
29856             
29857             if (_t.hoverState == 'out') {
29858                 _t.hide();
29859                 Roo.bootstrap.Tooltip.currentEl = false;
29860             }
29861         }, delay);
29862     },
29863     
29864     show : function (msg)
29865     {
29866         if (!this.el) {
29867             this.render(document.body);
29868         }
29869         // set content.
29870         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29871         
29872         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29873         
29874         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29875         
29876         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29877                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29878         
29879         var placement = typeof this.placement == 'function' ?
29880             this.placement.call(this, this.el, on_el) :
29881             this.placement;
29882             
29883         var autoToken = /\s?auto?\s?/i;
29884         var autoPlace = autoToken.test(placement);
29885         if (autoPlace) {
29886             placement = placement.replace(autoToken, '') || 'top';
29887         }
29888         
29889         //this.el.detach()
29890         //this.el.setXY([0,0]);
29891         this.el.show();
29892         //this.el.dom.style.display='block';
29893         
29894         //this.el.appendTo(on_el);
29895         
29896         var p = this.getPosition();
29897         var box = this.el.getBox();
29898         
29899         if (autoPlace) {
29900             // fixme..
29901         }
29902         
29903         var align = this.alignment[placement];
29904         
29905         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29906         
29907         if(placement == 'top' || placement == 'bottom'){
29908             if(xy[0] < 0){
29909                 placement = 'right';
29910             }
29911             
29912             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29913                 placement = 'left';
29914             }
29915             
29916             var scroll = Roo.select('body', true).first().getScroll();
29917             
29918             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29919                 placement = 'top';
29920             }
29921             
29922             align = this.alignment[placement];
29923             
29924             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29925             
29926         }
29927         
29928         var elems = document.getElementsByTagName('div');
29929         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29930         for (var i = 0; i < elems.length; i++) {
29931           var zindex = Number.parseInt(
29932                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29933                 10
29934           );
29935           if (zindex > highest) {
29936             highest = zindex;
29937           }
29938         }
29939         
29940         
29941         
29942         this.el.dom.style.zIndex = highest;
29943         
29944         this.el.alignTo(this.bindEl, align[0],align[1]);
29945         //var arrow = this.el.select('.arrow',true).first();
29946         //arrow.set(align[2], 
29947         
29948         this.el.addClass(placement);
29949         this.el.addClass("bs-tooltip-"+ placement);
29950         
29951         this.el.addClass('in fade show');
29952         
29953         this.hoverState = null;
29954         
29955         if (this.el.hasClass('fade')) {
29956             // fade it?
29957         }
29958         
29959         
29960         
29961         
29962         
29963     },
29964     hide : function()
29965     {
29966          
29967         if (!this.el) {
29968             return;
29969         }
29970         //this.el.setXY([0,0]);
29971         this.el.removeClass(['show', 'in']);
29972         //this.el.hide();
29973         
29974     }
29975     
29976 });
29977  
29978
29979  /*
29980  * - LGPL
29981  *
29982  * Location Picker
29983  * 
29984  */
29985
29986 /**
29987  * @class Roo.bootstrap.LocationPicker
29988  * @extends Roo.bootstrap.Component
29989  * Bootstrap LocationPicker class
29990  * @cfg {Number} latitude Position when init default 0
29991  * @cfg {Number} longitude Position when init default 0
29992  * @cfg {Number} zoom default 15
29993  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29994  * @cfg {Boolean} mapTypeControl default false
29995  * @cfg {Boolean} disableDoubleClickZoom default false
29996  * @cfg {Boolean} scrollwheel default true
29997  * @cfg {Boolean} streetViewControl default false
29998  * @cfg {Number} radius default 0
29999  * @cfg {String} locationName
30000  * @cfg {Boolean} draggable default true
30001  * @cfg {Boolean} enableAutocomplete default false
30002  * @cfg {Boolean} enableReverseGeocode default true
30003  * @cfg {String} markerTitle
30004  * 
30005  * @constructor
30006  * Create a new LocationPicker
30007  * @param {Object} config The config object
30008  */
30009
30010
30011 Roo.bootstrap.LocationPicker = function(config){
30012     
30013     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30014     
30015     this.addEvents({
30016         /**
30017          * @event initial
30018          * Fires when the picker initialized.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Google Location} location
30021          */
30022         initial : true,
30023         /**
30024          * @event positionchanged
30025          * Fires when the picker position changed.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Google Location} location
30028          */
30029         positionchanged : true,
30030         /**
30031          * @event resize
30032          * Fires when the map resize.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         resize : true,
30036         /**
30037          * @event show
30038          * Fires when the map show.
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          */
30041         show : true,
30042         /**
30043          * @event hide
30044          * Fires when the map hide.
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         hide : true,
30048         /**
30049          * @event mapClick
30050          * Fires when click the map.
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          * @param {Map event} e
30053          */
30054         mapClick : true,
30055         /**
30056          * @event mapRightClick
30057          * Fires when right click the map.
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          * @param {Map event} e
30060          */
30061         mapRightClick : true,
30062         /**
30063          * @event markerClick
30064          * Fires when click the marker.
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          * @param {Map event} e
30067          */
30068         markerClick : true,
30069         /**
30070          * @event markerRightClick
30071          * Fires when right click the marker.
30072          * @param {Roo.bootstrap.LocationPicker} this
30073          * @param {Map event} e
30074          */
30075         markerRightClick : true,
30076         /**
30077          * @event OverlayViewDraw
30078          * Fires when OverlayView Draw
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          */
30081         OverlayViewDraw : true,
30082         /**
30083          * @event OverlayViewOnAdd
30084          * Fires when OverlayView Draw
30085          * @param {Roo.bootstrap.LocationPicker} this
30086          */
30087         OverlayViewOnAdd : true,
30088         /**
30089          * @event OverlayViewOnRemove
30090          * Fires when OverlayView Draw
30091          * @param {Roo.bootstrap.LocationPicker} this
30092          */
30093         OverlayViewOnRemove : true,
30094         /**
30095          * @event OverlayViewShow
30096          * Fires when OverlayView Draw
30097          * @param {Roo.bootstrap.LocationPicker} this
30098          * @param {Pixel} cpx
30099          */
30100         OverlayViewShow : true,
30101         /**
30102          * @event OverlayViewHide
30103          * Fires when OverlayView Draw
30104          * @param {Roo.bootstrap.LocationPicker} this
30105          */
30106         OverlayViewHide : true,
30107         /**
30108          * @event loadexception
30109          * Fires when load google lib failed.
30110          * @param {Roo.bootstrap.LocationPicker} this
30111          */
30112         loadexception : true
30113     });
30114         
30115 };
30116
30117 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30118     
30119     gMapContext: false,
30120     
30121     latitude: 0,
30122     longitude: 0,
30123     zoom: 15,
30124     mapTypeId: false,
30125     mapTypeControl: false,
30126     disableDoubleClickZoom: false,
30127     scrollwheel: true,
30128     streetViewControl: false,
30129     radius: 0,
30130     locationName: '',
30131     draggable: true,
30132     enableAutocomplete: false,
30133     enableReverseGeocode: true,
30134     markerTitle: '',
30135     
30136     getAutoCreate: function()
30137     {
30138
30139         var cfg = {
30140             tag: 'div',
30141             cls: 'roo-location-picker'
30142         };
30143         
30144         return cfg
30145     },
30146     
30147     initEvents: function(ct, position)
30148     {       
30149         if(!this.el.getWidth() || this.isApplied()){
30150             return;
30151         }
30152         
30153         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30154         
30155         this.initial();
30156     },
30157     
30158     initial: function()
30159     {
30160         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30161             this.fireEvent('loadexception', this);
30162             return;
30163         }
30164         
30165         if(!this.mapTypeId){
30166             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30167         }
30168         
30169         this.gMapContext = this.GMapContext();
30170         
30171         this.initOverlayView();
30172         
30173         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30174         
30175         var _this = this;
30176                 
30177         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30178             _this.setPosition(_this.gMapContext.marker.position);
30179         });
30180         
30181         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30182             _this.fireEvent('mapClick', this, event);
30183             
30184         });
30185
30186         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30187             _this.fireEvent('mapRightClick', this, event);
30188             
30189         });
30190         
30191         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30192             _this.fireEvent('markerClick', this, event);
30193             
30194         });
30195
30196         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30197             _this.fireEvent('markerRightClick', this, event);
30198             
30199         });
30200         
30201         this.setPosition(this.gMapContext.location);
30202         
30203         this.fireEvent('initial', this, this.gMapContext.location);
30204     },
30205     
30206     initOverlayView: function()
30207     {
30208         var _this = this;
30209         
30210         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30211             
30212             draw: function()
30213             {
30214                 _this.fireEvent('OverlayViewDraw', _this);
30215             },
30216             
30217             onAdd: function()
30218             {
30219                 _this.fireEvent('OverlayViewOnAdd', _this);
30220             },
30221             
30222             onRemove: function()
30223             {
30224                 _this.fireEvent('OverlayViewOnRemove', _this);
30225             },
30226             
30227             show: function(cpx)
30228             {
30229                 _this.fireEvent('OverlayViewShow', _this, cpx);
30230             },
30231             
30232             hide: function()
30233             {
30234                 _this.fireEvent('OverlayViewHide', _this);
30235             }
30236             
30237         });
30238     },
30239     
30240     fromLatLngToContainerPixel: function(event)
30241     {
30242         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30243     },
30244     
30245     isApplied: function() 
30246     {
30247         return this.getGmapContext() == false ? false : true;
30248     },
30249     
30250     getGmapContext: function() 
30251     {
30252         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30253     },
30254     
30255     GMapContext: function() 
30256     {
30257         var position = new google.maps.LatLng(this.latitude, this.longitude);
30258         
30259         var _map = new google.maps.Map(this.el.dom, {
30260             center: position,
30261             zoom: this.zoom,
30262             mapTypeId: this.mapTypeId,
30263             mapTypeControl: this.mapTypeControl,
30264             disableDoubleClickZoom: this.disableDoubleClickZoom,
30265             scrollwheel: this.scrollwheel,
30266             streetViewControl: this.streetViewControl,
30267             locationName: this.locationName,
30268             draggable: this.draggable,
30269             enableAutocomplete: this.enableAutocomplete,
30270             enableReverseGeocode: this.enableReverseGeocode
30271         });
30272         
30273         var _marker = new google.maps.Marker({
30274             position: position,
30275             map: _map,
30276             title: this.markerTitle,
30277             draggable: this.draggable
30278         });
30279         
30280         return {
30281             map: _map,
30282             marker: _marker,
30283             circle: null,
30284             location: position,
30285             radius: this.radius,
30286             locationName: this.locationName,
30287             addressComponents: {
30288                 formatted_address: null,
30289                 addressLine1: null,
30290                 addressLine2: null,
30291                 streetName: null,
30292                 streetNumber: null,
30293                 city: null,
30294                 district: null,
30295                 state: null,
30296                 stateOrProvince: null
30297             },
30298             settings: this,
30299             domContainer: this.el.dom,
30300             geodecoder: new google.maps.Geocoder()
30301         };
30302     },
30303     
30304     drawCircle: function(center, radius, options) 
30305     {
30306         if (this.gMapContext.circle != null) {
30307             this.gMapContext.circle.setMap(null);
30308         }
30309         if (radius > 0) {
30310             radius *= 1;
30311             options = Roo.apply({}, options, {
30312                 strokeColor: "#0000FF",
30313                 strokeOpacity: .35,
30314                 strokeWeight: 2,
30315                 fillColor: "#0000FF",
30316                 fillOpacity: .2
30317             });
30318             
30319             options.map = this.gMapContext.map;
30320             options.radius = radius;
30321             options.center = center;
30322             this.gMapContext.circle = new google.maps.Circle(options);
30323             return this.gMapContext.circle;
30324         }
30325         
30326         return null;
30327     },
30328     
30329     setPosition: function(location) 
30330     {
30331         this.gMapContext.location = location;
30332         this.gMapContext.marker.setPosition(location);
30333         this.gMapContext.map.panTo(location);
30334         this.drawCircle(location, this.gMapContext.radius, {});
30335         
30336         var _this = this;
30337         
30338         if (this.gMapContext.settings.enableReverseGeocode) {
30339             this.gMapContext.geodecoder.geocode({
30340                 latLng: this.gMapContext.location
30341             }, function(results, status) {
30342                 
30343                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30344                     _this.gMapContext.locationName = results[0].formatted_address;
30345                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30346                     
30347                     _this.fireEvent('positionchanged', this, location);
30348                 }
30349             });
30350             
30351             return;
30352         }
30353         
30354         this.fireEvent('positionchanged', this, location);
30355     },
30356     
30357     resize: function()
30358     {
30359         google.maps.event.trigger(this.gMapContext.map, "resize");
30360         
30361         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30362         
30363         this.fireEvent('resize', this);
30364     },
30365     
30366     setPositionByLatLng: function(latitude, longitude)
30367     {
30368         this.setPosition(new google.maps.LatLng(latitude, longitude));
30369     },
30370     
30371     getCurrentPosition: function() 
30372     {
30373         return {
30374             latitude: this.gMapContext.location.lat(),
30375             longitude: this.gMapContext.location.lng()
30376         };
30377     },
30378     
30379     getAddressName: function() 
30380     {
30381         return this.gMapContext.locationName;
30382     },
30383     
30384     getAddressComponents: function() 
30385     {
30386         return this.gMapContext.addressComponents;
30387     },
30388     
30389     address_component_from_google_geocode: function(address_components) 
30390     {
30391         var result = {};
30392         
30393         for (var i = 0; i < address_components.length; i++) {
30394             var component = address_components[i];
30395             if (component.types.indexOf("postal_code") >= 0) {
30396                 result.postalCode = component.short_name;
30397             } else if (component.types.indexOf("street_number") >= 0) {
30398                 result.streetNumber = component.short_name;
30399             } else if (component.types.indexOf("route") >= 0) {
30400                 result.streetName = component.short_name;
30401             } else if (component.types.indexOf("neighborhood") >= 0) {
30402                 result.city = component.short_name;
30403             } else if (component.types.indexOf("locality") >= 0) {
30404                 result.city = component.short_name;
30405             } else if (component.types.indexOf("sublocality") >= 0) {
30406                 result.district = component.short_name;
30407             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30408                 result.stateOrProvince = component.short_name;
30409             } else if (component.types.indexOf("country") >= 0) {
30410                 result.country = component.short_name;
30411             }
30412         }
30413         
30414         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30415         result.addressLine2 = "";
30416         return result;
30417     },
30418     
30419     setZoomLevel: function(zoom)
30420     {
30421         this.gMapContext.map.setZoom(zoom);
30422     },
30423     
30424     show: function()
30425     {
30426         if(!this.el){
30427             return;
30428         }
30429         
30430         this.el.show();
30431         
30432         this.resize();
30433         
30434         this.fireEvent('show', this);
30435     },
30436     
30437     hide: function()
30438     {
30439         if(!this.el){
30440             return;
30441         }
30442         
30443         this.el.hide();
30444         
30445         this.fireEvent('hide', this);
30446     }
30447     
30448 });
30449
30450 Roo.apply(Roo.bootstrap.LocationPicker, {
30451     
30452     OverlayView : function(map, options)
30453     {
30454         options = options || {};
30455         
30456         this.setMap(map);
30457     }
30458     
30459     
30460 });/**
30461  * @class Roo.bootstrap.Alert
30462  * @extends Roo.bootstrap.Component
30463  * Bootstrap Alert class - shows an alert area box
30464  * eg
30465  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30466   Enter a valid email address
30467 </div>
30468  * @licence LGPL
30469  * @cfg {String} title The title of alert
30470  * @cfg {String} html The content of alert
30471  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30472  * @cfg {String} fa font-awesomeicon
30473  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30474  * @cfg {Boolean} close true to show a x closer
30475  * 
30476  * 
30477  * @constructor
30478  * Create a new alert
30479  * @param {Object} config The config object
30480  */
30481
30482
30483 Roo.bootstrap.Alert = function(config){
30484     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30485     
30486 };
30487
30488 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30489     
30490     title: '',
30491     html: '',
30492     weight: false,
30493     fa: false,
30494     faicon: false, // BC
30495     close : false,
30496     
30497     
30498     getAutoCreate : function()
30499     {
30500         
30501         var cfg = {
30502             tag : 'div',
30503             cls : 'alert',
30504             cn : [
30505                 {
30506                     tag: 'button',
30507                     type :  "button",
30508                     cls: "close",
30509                     html : '×',
30510                     style : this.close ? '' : 'display:none'
30511                 },
30512                 {
30513                     tag : 'i',
30514                     cls : 'roo-alert-icon'
30515                     
30516                 },
30517                 {
30518                     tag : 'b',
30519                     cls : 'roo-alert-title',
30520                     html : this.title
30521                 },
30522                 {
30523                     tag : 'span',
30524                     cls : 'roo-alert-text',
30525                     html : this.html
30526                 }
30527             ]
30528         };
30529         
30530         if(this.faicon){
30531             cfg.cn[0].cls += ' fa ' + this.faicon;
30532         }
30533         if(this.fa){
30534             cfg.cn[0].cls += ' fa ' + this.fa;
30535         }
30536         
30537         if(this.weight){
30538             cfg.cls += ' alert-' + this.weight;
30539         }
30540         
30541         return cfg;
30542     },
30543     
30544     initEvents: function() 
30545     {
30546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30547         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30548         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30549         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30550         if (this.seconds > 0) {
30551             this.hide.defer(this.seconds, this);
30552         }
30553     },
30554     /**
30555      * Set the Title Message HTML
30556      * @param {String} html
30557      */
30558     setTitle : function(str)
30559     {
30560         this.titleEl.dom.innerHTML = str;
30561     },
30562      
30563      /**
30564      * Set the Body Message HTML
30565      * @param {String} html
30566      */
30567     setHtml : function(str)
30568     {
30569         this.htmlEl.dom.innerHTML = str;
30570     },
30571     /**
30572      * Set the Weight of the alert
30573      * @param {String} (success|info|warning|danger) weight
30574      */
30575     
30576     setWeight : function(weight)
30577     {
30578         if(this.weight){
30579             this.el.removeClass('alert-' + this.weight);
30580         }
30581         
30582         this.weight = weight;
30583         
30584         this.el.addClass('alert-' + this.weight);
30585     },
30586       /**
30587      * Set the Icon of the alert
30588      * @param {String} see fontawsome names (name without the 'fa-' bit)
30589      */
30590     setIcon : function(icon)
30591     {
30592         if(this.faicon){
30593             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30594         }
30595         
30596         this.faicon = icon;
30597         
30598         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30599     },
30600     /**
30601      * Hide the Alert
30602      */
30603     hide: function() 
30604     {
30605         this.el.hide();   
30606     },
30607     /**
30608      * Show the Alert
30609      */
30610     show: function() 
30611     {  
30612         this.el.show();   
30613     }
30614     
30615 });
30616
30617  
30618 /*
30619 * Licence: LGPL
30620 */
30621
30622 /**
30623  * @class Roo.bootstrap.UploadCropbox
30624  * @extends Roo.bootstrap.Component
30625  * Bootstrap UploadCropbox class
30626  * @cfg {String} emptyText show when image has been loaded
30627  * @cfg {String} rotateNotify show when image too small to rotate
30628  * @cfg {Number} errorTimeout default 3000
30629  * @cfg {Number} minWidth default 300
30630  * @cfg {Number} minHeight default 300
30631  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30632  * @cfg {Boolean} isDocument (true|false) default false
30633  * @cfg {String} url action url
30634  * @cfg {String} paramName default 'imageUpload'
30635  * @cfg {String} method default POST
30636  * @cfg {Boolean} loadMask (true|false) default true
30637  * @cfg {Boolean} loadingText default 'Loading...'
30638  * 
30639  * @constructor
30640  * Create a new UploadCropbox
30641  * @param {Object} config The config object
30642  */
30643
30644 Roo.bootstrap.UploadCropbox = function(config){
30645     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30646     
30647     this.addEvents({
30648         /**
30649          * @event beforeselectfile
30650          * Fire before select file
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          */
30653         "beforeselectfile" : true,
30654         /**
30655          * @event initial
30656          * Fire after initEvent
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          */
30659         "initial" : true,
30660         /**
30661          * @event crop
30662          * Fire after initEvent
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {String} data
30665          */
30666         "crop" : true,
30667         /**
30668          * @event prepare
30669          * Fire when preparing the file data
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {Object} file
30672          */
30673         "prepare" : true,
30674         /**
30675          * @event exception
30676          * Fire when get exception
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {XMLHttpRequest} xhr
30679          */
30680         "exception" : true,
30681         /**
30682          * @event beforeloadcanvas
30683          * Fire before load the canvas
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {String} src
30686          */
30687         "beforeloadcanvas" : true,
30688         /**
30689          * @event trash
30690          * Fire when trash image
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          */
30693         "trash" : true,
30694         /**
30695          * @event download
30696          * Fire when download the image
30697          * @param {Roo.bootstrap.UploadCropbox} this
30698          */
30699         "download" : true,
30700         /**
30701          * @event footerbuttonclick
30702          * Fire when footerbuttonclick
30703          * @param {Roo.bootstrap.UploadCropbox} this
30704          * @param {String} type
30705          */
30706         "footerbuttonclick" : true,
30707         /**
30708          * @event resize
30709          * Fire when resize
30710          * @param {Roo.bootstrap.UploadCropbox} this
30711          */
30712         "resize" : true,
30713         /**
30714          * @event rotate
30715          * Fire when rotate the image
30716          * @param {Roo.bootstrap.UploadCropbox} this
30717          * @param {String} pos
30718          */
30719         "rotate" : true,
30720         /**
30721          * @event inspect
30722          * Fire when inspect the file
30723          * @param {Roo.bootstrap.UploadCropbox} this
30724          * @param {Object} file
30725          */
30726         "inspect" : true,
30727         /**
30728          * @event upload
30729          * Fire when xhr upload the file
30730          * @param {Roo.bootstrap.UploadCropbox} this
30731          * @param {Object} data
30732          */
30733         "upload" : true,
30734         /**
30735          * @event arrange
30736          * Fire when arrange the file data
30737          * @param {Roo.bootstrap.UploadCropbox} this
30738          * @param {Object} formData
30739          */
30740         "arrange" : true
30741     });
30742     
30743     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30744 };
30745
30746 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30747     
30748     emptyText : 'Click to upload image',
30749     rotateNotify : 'Image is too small to rotate',
30750     errorTimeout : 3000,
30751     scale : 0,
30752     baseScale : 1,
30753     rotate : 0,
30754     dragable : false,
30755     pinching : false,
30756     mouseX : 0,
30757     mouseY : 0,
30758     cropData : false,
30759     minWidth : 300,
30760     minHeight : 300,
30761     file : false,
30762     exif : {},
30763     baseRotate : 1,
30764     cropType : 'image/jpeg',
30765     buttons : false,
30766     canvasLoaded : false,
30767     isDocument : false,
30768     method : 'POST',
30769     paramName : 'imageUpload',
30770     loadMask : true,
30771     loadingText : 'Loading...',
30772     maskEl : false,
30773     
30774     getAutoCreate : function()
30775     {
30776         var cfg = {
30777             tag : 'div',
30778             cls : 'roo-upload-cropbox',
30779             cn : [
30780                 {
30781                     tag : 'input',
30782                     cls : 'roo-upload-cropbox-selector',
30783                     type : 'file'
30784                 },
30785                 {
30786                     tag : 'div',
30787                     cls : 'roo-upload-cropbox-body',
30788                     style : 'cursor:pointer',
30789                     cn : [
30790                         {
30791                             tag : 'div',
30792                             cls : 'roo-upload-cropbox-preview'
30793                         },
30794                         {
30795                             tag : 'div',
30796                             cls : 'roo-upload-cropbox-thumb'
30797                         },
30798                         {
30799                             tag : 'div',
30800                             cls : 'roo-upload-cropbox-empty-notify',
30801                             html : this.emptyText
30802                         },
30803                         {
30804                             tag : 'div',
30805                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30806                             html : this.rotateNotify
30807                         }
30808                     ]
30809                 },
30810                 {
30811                     tag : 'div',
30812                     cls : 'roo-upload-cropbox-footer',
30813                     cn : {
30814                         tag : 'div',
30815                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30816                         cn : []
30817                     }
30818                 }
30819             ]
30820         };
30821         
30822         return cfg;
30823     },
30824     
30825     onRender : function(ct, position)
30826     {
30827         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30828         
30829         if (this.buttons.length) {
30830             
30831             Roo.each(this.buttons, function(bb) {
30832                 
30833                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30834                 
30835                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30836                 
30837             }, this);
30838         }
30839         
30840         if(this.loadMask){
30841             this.maskEl = this.el;
30842         }
30843     },
30844     
30845     initEvents : function()
30846     {
30847         this.urlAPI = (window.createObjectURL && window) || 
30848                                 (window.URL && URL.revokeObjectURL && URL) || 
30849                                 (window.webkitURL && webkitURL);
30850                         
30851         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30852         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30853         
30854         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30855         this.selectorEl.hide();
30856         
30857         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30858         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30859         
30860         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30861         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30862         this.thumbEl.hide();
30863         
30864         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30865         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30866         
30867         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30868         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869         this.errorEl.hide();
30870         
30871         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30872         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30873         this.footerEl.hide();
30874         
30875         this.setThumbBoxSize();
30876         
30877         this.bind();
30878         
30879         this.resize();
30880         
30881         this.fireEvent('initial', this);
30882     },
30883
30884     bind : function()
30885     {
30886         var _this = this;
30887         
30888         window.addEventListener("resize", function() { _this.resize(); } );
30889         
30890         this.bodyEl.on('click', this.beforeSelectFile, this);
30891         
30892         if(Roo.isTouch){
30893             this.bodyEl.on('touchstart', this.onTouchStart, this);
30894             this.bodyEl.on('touchmove', this.onTouchMove, this);
30895             this.bodyEl.on('touchend', this.onTouchEnd, this);
30896         }
30897         
30898         if(!Roo.isTouch){
30899             this.bodyEl.on('mousedown', this.onMouseDown, this);
30900             this.bodyEl.on('mousemove', this.onMouseMove, this);
30901             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30902             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30903             Roo.get(document).on('mouseup', this.onMouseUp, this);
30904         }
30905         
30906         this.selectorEl.on('change', this.onFileSelected, this);
30907     },
30908     
30909     reset : function()
30910     {    
30911         this.scale = 0;
30912         this.baseScale = 1;
30913         this.rotate = 0;
30914         this.baseRotate = 1;
30915         this.dragable = false;
30916         this.pinching = false;
30917         this.mouseX = 0;
30918         this.mouseY = 0;
30919         this.cropData = false;
30920         this.notifyEl.dom.innerHTML = this.emptyText;
30921         
30922         this.selectorEl.dom.value = '';
30923         
30924     },
30925     
30926     resize : function()
30927     {
30928         if(this.fireEvent('resize', this) != false){
30929             this.setThumbBoxPosition();
30930             this.setCanvasPosition();
30931         }
30932     },
30933     
30934     onFooterButtonClick : function(e, el, o, type)
30935     {
30936         switch (type) {
30937             case 'rotate-left' :
30938                 this.onRotateLeft(e);
30939                 break;
30940             case 'rotate-right' :
30941                 this.onRotateRight(e);
30942                 break;
30943             case 'picture' :
30944                 this.beforeSelectFile(e);
30945                 break;
30946             case 'trash' :
30947                 this.trash(e);
30948                 break;
30949             case 'crop' :
30950                 this.crop(e);
30951                 break;
30952             case 'download' :
30953                 this.download(e);
30954                 break;
30955             default :
30956                 break;
30957         }
30958         
30959         this.fireEvent('footerbuttonclick', this, type);
30960     },
30961     
30962     beforeSelectFile : function(e)
30963     {
30964         e.preventDefault();
30965         
30966         if(this.fireEvent('beforeselectfile', this) != false){
30967             this.selectorEl.dom.click();
30968         }
30969     },
30970     
30971     onFileSelected : function(e)
30972     {
30973         e.preventDefault();
30974         
30975         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30976             return;
30977         }
30978         
30979         var file = this.selectorEl.dom.files[0];
30980         
30981         if(this.fireEvent('inspect', this, file) != false){
30982             this.prepare(file);
30983         }
30984         
30985     },
30986     
30987     trash : function(e)
30988     {
30989         this.fireEvent('trash', this);
30990     },
30991     
30992     download : function(e)
30993     {
30994         this.fireEvent('download', this);
30995     },
30996     
30997     loadCanvas : function(src)
30998     {   
30999         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31000             
31001             this.reset();
31002             
31003             this.imageEl = document.createElement('img');
31004             
31005             var _this = this;
31006             
31007             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31008             
31009             this.imageEl.src = src;
31010         }
31011     },
31012     
31013     onLoadCanvas : function()
31014     {   
31015         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31016         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31017         
31018         this.bodyEl.un('click', this.beforeSelectFile, this);
31019         
31020         this.notifyEl.hide();
31021         this.thumbEl.show();
31022         this.footerEl.show();
31023         
31024         this.baseRotateLevel();
31025         
31026         if(this.isDocument){
31027             this.setThumbBoxSize();
31028         }
31029         
31030         this.setThumbBoxPosition();
31031         
31032         this.baseScaleLevel();
31033         
31034         this.draw();
31035         
31036         this.resize();
31037         
31038         this.canvasLoaded = true;
31039         
31040         if(this.loadMask){
31041             this.maskEl.unmask();
31042         }
31043         
31044     },
31045     
31046     setCanvasPosition : function()
31047     {   
31048         if(!this.canvasEl){
31049             return;
31050         }
31051         
31052         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31053         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31054         
31055         this.previewEl.setLeft(pw);
31056         this.previewEl.setTop(ph);
31057         
31058     },
31059     
31060     onMouseDown : function(e)
31061     {   
31062         e.stopEvent();
31063         
31064         this.dragable = true;
31065         this.pinching = false;
31066         
31067         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31068             this.dragable = false;
31069             return;
31070         }
31071         
31072         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31073         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31074         
31075     },
31076     
31077     onMouseMove : function(e)
31078     {   
31079         e.stopEvent();
31080         
31081         if(!this.canvasLoaded){
31082             return;
31083         }
31084         
31085         if (!this.dragable){
31086             return;
31087         }
31088         
31089         var minX = Math.ceil(this.thumbEl.getLeft(true));
31090         var minY = Math.ceil(this.thumbEl.getTop(true));
31091         
31092         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31093         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31094         
31095         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31096         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31097         
31098         x = x - this.mouseX;
31099         y = y - this.mouseY;
31100         
31101         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31102         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31103         
31104         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31105         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31106         
31107         this.previewEl.setLeft(bgX);
31108         this.previewEl.setTop(bgY);
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     onMouseUp : function(e)
31115     {   
31116         e.stopEvent();
31117         
31118         this.dragable = false;
31119     },
31120     
31121     onMouseWheel : function(e)
31122     {   
31123         e.stopEvent();
31124         
31125         this.startScale = this.scale;
31126         
31127         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31128         
31129         if(!this.zoomable()){
31130             this.scale = this.startScale;
31131             return;
31132         }
31133         
31134         this.draw();
31135         
31136         return;
31137     },
31138     
31139     zoomable : function()
31140     {
31141         var minScale = this.thumbEl.getWidth() / this.minWidth;
31142         
31143         if(this.minWidth < this.minHeight){
31144             minScale = this.thumbEl.getHeight() / this.minHeight;
31145         }
31146         
31147         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31148         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31149         
31150         if(
31151                 this.isDocument &&
31152                 (this.rotate == 0 || this.rotate == 180) && 
31153                 (
31154                     width > this.imageEl.OriginWidth || 
31155                     height > this.imageEl.OriginHeight ||
31156                     (width < this.minWidth && height < this.minHeight)
31157                 )
31158         ){
31159             return false;
31160         }
31161         
31162         if(
31163                 this.isDocument &&
31164                 (this.rotate == 90 || this.rotate == 270) && 
31165                 (
31166                     width > this.imageEl.OriginWidth || 
31167                     height > this.imageEl.OriginHeight ||
31168                     (width < this.minHeight && height < this.minWidth)
31169                 )
31170         ){
31171             return false;
31172         }
31173         
31174         if(
31175                 !this.isDocument &&
31176                 (this.rotate == 0 || this.rotate == 180) && 
31177                 (
31178                     width < this.minWidth || 
31179                     width > this.imageEl.OriginWidth || 
31180                     height < this.minHeight || 
31181                     height > this.imageEl.OriginHeight
31182                 )
31183         ){
31184             return false;
31185         }
31186         
31187         if(
31188                 !this.isDocument &&
31189                 (this.rotate == 90 || this.rotate == 270) && 
31190                 (
31191                     width < this.minHeight || 
31192                     width > this.imageEl.OriginWidth || 
31193                     height < this.minWidth || 
31194                     height > this.imageEl.OriginHeight
31195                 )
31196         ){
31197             return false;
31198         }
31199         
31200         return true;
31201         
31202     },
31203     
31204     onRotateLeft : function(e)
31205     {   
31206         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31207             
31208             var minScale = this.thumbEl.getWidth() / this.minWidth;
31209             
31210             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31212             
31213             this.startScale = this.scale;
31214             
31215             while (this.getScaleLevel() < minScale){
31216             
31217                 this.scale = this.scale + 1;
31218                 
31219                 if(!this.zoomable()){
31220                     break;
31221                 }
31222                 
31223                 if(
31224                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31226                 ){
31227                     continue;
31228                 }
31229                 
31230                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31231
31232                 this.draw();
31233                 
31234                 return;
31235             }
31236             
31237             this.scale = this.startScale;
31238             
31239             this.onRotateFail();
31240             
31241             return false;
31242         }
31243         
31244         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31245
31246         if(this.isDocument){
31247             this.setThumbBoxSize();
31248             this.setThumbBoxPosition();
31249             this.setCanvasPosition();
31250         }
31251         
31252         this.draw();
31253         
31254         this.fireEvent('rotate', this, 'left');
31255         
31256     },
31257     
31258     onRotateRight : function(e)
31259     {
31260         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31261             
31262             var minScale = this.thumbEl.getWidth() / this.minWidth;
31263         
31264             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31265             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31266             
31267             this.startScale = this.scale;
31268             
31269             while (this.getScaleLevel() < minScale){
31270             
31271                 this.scale = this.scale + 1;
31272                 
31273                 if(!this.zoomable()){
31274                     break;
31275                 }
31276                 
31277                 if(
31278                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31279                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31280                 ){
31281                     continue;
31282                 }
31283                 
31284                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31285
31286                 this.draw();
31287                 
31288                 return;
31289             }
31290             
31291             this.scale = this.startScale;
31292             
31293             this.onRotateFail();
31294             
31295             return false;
31296         }
31297         
31298         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31299
31300         if(this.isDocument){
31301             this.setThumbBoxSize();
31302             this.setThumbBoxPosition();
31303             this.setCanvasPosition();
31304         }
31305         
31306         this.draw();
31307         
31308         this.fireEvent('rotate', this, 'right');
31309     },
31310     
31311     onRotateFail : function()
31312     {
31313         this.errorEl.show(true);
31314         
31315         var _this = this;
31316         
31317         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31318     },
31319     
31320     draw : function()
31321     {
31322         this.previewEl.dom.innerHTML = '';
31323         
31324         var canvasEl = document.createElement("canvas");
31325         
31326         var contextEl = canvasEl.getContext("2d");
31327         
31328         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330         var center = this.imageEl.OriginWidth / 2;
31331         
31332         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31333             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31334             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31335             center = this.imageEl.OriginHeight / 2;
31336         }
31337         
31338         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31339         
31340         contextEl.translate(center, center);
31341         contextEl.rotate(this.rotate * Math.PI / 180);
31342
31343         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31344         
31345         this.canvasEl = document.createElement("canvas");
31346         
31347         this.contextEl = this.canvasEl.getContext("2d");
31348         
31349         switch (this.rotate) {
31350             case 0 :
31351                 
31352                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31353                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31354                 
31355                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31356                 
31357                 break;
31358             case 90 : 
31359                 
31360                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31361                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31362                 
31363                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31364                     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);
31365                     break;
31366                 }
31367                 
31368                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31369                 
31370                 break;
31371             case 180 :
31372                 
31373                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31374                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31375                 
31376                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31377                     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);
31378                     break;
31379                 }
31380                 
31381                 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);
31382                 
31383                 break;
31384             case 270 :
31385                 
31386                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31388         
31389                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31391                     break;
31392                 }
31393                 
31394                 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);
31395                 
31396                 break;
31397             default : 
31398                 break;
31399         }
31400         
31401         this.previewEl.appendChild(this.canvasEl);
31402         
31403         this.setCanvasPosition();
31404     },
31405     
31406     crop : function()
31407     {
31408         if(!this.canvasLoaded){
31409             return;
31410         }
31411         
31412         var imageCanvas = document.createElement("canvas");
31413         
31414         var imageContext = imageCanvas.getContext("2d");
31415         
31416         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31417         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418         
31419         var center = imageCanvas.width / 2;
31420         
31421         imageContext.translate(center, center);
31422         
31423         imageContext.rotate(this.rotate * Math.PI / 180);
31424         
31425         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31426         
31427         var canvas = document.createElement("canvas");
31428         
31429         var context = canvas.getContext("2d");
31430                 
31431         canvas.width = this.minWidth;
31432         canvas.height = this.minHeight;
31433
31434         switch (this.rotate) {
31435             case 0 :
31436                 
31437                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31438                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31439                 
31440                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31441                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31442                 
31443                 var targetWidth = this.minWidth - 2 * x;
31444                 var targetHeight = this.minHeight - 2 * y;
31445                 
31446                 var scale = 1;
31447                 
31448                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31449                     scale = targetWidth / width;
31450                 }
31451                 
31452                 if(x > 0 && y == 0){
31453                     scale = targetHeight / height;
31454                 }
31455                 
31456                 if(x > 0 && y > 0){
31457                     scale = targetWidth / width;
31458                     
31459                     if(width < height){
31460                         scale = targetHeight / height;
31461                     }
31462                 }
31463                 
31464                 context.scale(scale, scale);
31465                 
31466                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31467                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31468
31469                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31470                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31471
31472                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31473                 
31474                 break;
31475             case 90 : 
31476                 
31477                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31478                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31479                 
31480                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31482                 
31483                 var targetWidth = this.minWidth - 2 * x;
31484                 var targetHeight = this.minHeight - 2 * y;
31485                 
31486                 var scale = 1;
31487                 
31488                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489                     scale = targetWidth / width;
31490                 }
31491                 
31492                 if(x > 0 && y == 0){
31493                     scale = targetHeight / height;
31494                 }
31495                 
31496                 if(x > 0 && y > 0){
31497                     scale = targetWidth / width;
31498                     
31499                     if(width < height){
31500                         scale = targetHeight / height;
31501                     }
31502                 }
31503                 
31504                 context.scale(scale, scale);
31505                 
31506                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31508
31509                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31511                 
31512                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31513                 
31514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31515                 
31516                 break;
31517             case 180 :
31518                 
31519                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31520                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31521                 
31522                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31524                 
31525                 var targetWidth = this.minWidth - 2 * x;
31526                 var targetHeight = this.minHeight - 2 * y;
31527                 
31528                 var scale = 1;
31529                 
31530                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531                     scale = targetWidth / width;
31532                 }
31533                 
31534                 if(x > 0 && y == 0){
31535                     scale = targetHeight / height;
31536                 }
31537                 
31538                 if(x > 0 && y > 0){
31539                     scale = targetWidth / width;
31540                     
31541                     if(width < height){
31542                         scale = targetHeight / height;
31543                     }
31544                 }
31545                 
31546                 context.scale(scale, scale);
31547                 
31548                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31550
31551                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31553
31554                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31555                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31556                 
31557                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31558                 
31559                 break;
31560             case 270 :
31561                 
31562                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31563                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31564                 
31565                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31566                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31567                 
31568                 var targetWidth = this.minWidth - 2 * x;
31569                 var targetHeight = this.minHeight - 2 * y;
31570                 
31571                 var scale = 1;
31572                 
31573                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31574                     scale = targetWidth / width;
31575                 }
31576                 
31577                 if(x > 0 && y == 0){
31578                     scale = targetHeight / height;
31579                 }
31580                 
31581                 if(x > 0 && y > 0){
31582                     scale = targetWidth / width;
31583                     
31584                     if(width < height){
31585                         scale = targetHeight / height;
31586                     }
31587                 }
31588                 
31589                 context.scale(scale, scale);
31590                 
31591                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31592                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31593
31594                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31595                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31596                 
31597                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31598                 
31599                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31600                 
31601                 break;
31602             default : 
31603                 break;
31604         }
31605         
31606         this.cropData = canvas.toDataURL(this.cropType);
31607         
31608         if(this.fireEvent('crop', this, this.cropData) !== false){
31609             this.process(this.file, this.cropData);
31610         }
31611         
31612         return;
31613         
31614     },
31615     
31616     setThumbBoxSize : function()
31617     {
31618         var width, height;
31619         
31620         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31621             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31622             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31623             
31624             this.minWidth = width;
31625             this.minHeight = height;
31626             
31627             if(this.rotate == 90 || this.rotate == 270){
31628                 this.minWidth = height;
31629                 this.minHeight = width;
31630             }
31631         }
31632         
31633         height = 300;
31634         width = Math.ceil(this.minWidth * height / this.minHeight);
31635         
31636         if(this.minWidth > this.minHeight){
31637             width = 300;
31638             height = Math.ceil(this.minHeight * width / this.minWidth);
31639         }
31640         
31641         this.thumbEl.setStyle({
31642             width : width + 'px',
31643             height : height + 'px'
31644         });
31645
31646         return;
31647             
31648     },
31649     
31650     setThumbBoxPosition : function()
31651     {
31652         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31653         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31654         
31655         this.thumbEl.setLeft(x);
31656         this.thumbEl.setTop(y);
31657         
31658     },
31659     
31660     baseRotateLevel : function()
31661     {
31662         this.baseRotate = 1;
31663         
31664         if(
31665                 typeof(this.exif) != 'undefined' &&
31666                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31667                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31668         ){
31669             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31670         }
31671         
31672         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31673         
31674     },
31675     
31676     baseScaleLevel : function()
31677     {
31678         var width, height;
31679         
31680         if(this.isDocument){
31681             
31682             if(this.baseRotate == 6 || this.baseRotate == 8){
31683             
31684                 height = this.thumbEl.getHeight();
31685                 this.baseScale = height / this.imageEl.OriginWidth;
31686
31687                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31688                     width = this.thumbEl.getWidth();
31689                     this.baseScale = width / this.imageEl.OriginHeight;
31690                 }
31691
31692                 return;
31693             }
31694
31695             height = this.thumbEl.getHeight();
31696             this.baseScale = height / this.imageEl.OriginHeight;
31697
31698             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31699                 width = this.thumbEl.getWidth();
31700                 this.baseScale = width / this.imageEl.OriginWidth;
31701             }
31702
31703             return;
31704         }
31705         
31706         if(this.baseRotate == 6 || this.baseRotate == 8){
31707             
31708             width = this.thumbEl.getHeight();
31709             this.baseScale = width / this.imageEl.OriginHeight;
31710             
31711             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31712                 height = this.thumbEl.getWidth();
31713                 this.baseScale = height / this.imageEl.OriginHeight;
31714             }
31715             
31716             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31717                 height = this.thumbEl.getWidth();
31718                 this.baseScale = height / this.imageEl.OriginHeight;
31719                 
31720                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31721                     width = this.thumbEl.getHeight();
31722                     this.baseScale = width / this.imageEl.OriginWidth;
31723                 }
31724             }
31725             
31726             return;
31727         }
31728         
31729         width = this.thumbEl.getWidth();
31730         this.baseScale = width / this.imageEl.OriginWidth;
31731         
31732         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31733             height = this.thumbEl.getHeight();
31734             this.baseScale = height / this.imageEl.OriginHeight;
31735         }
31736         
31737         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31738             
31739             height = this.thumbEl.getHeight();
31740             this.baseScale = height / this.imageEl.OriginHeight;
31741             
31742             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31743                 width = this.thumbEl.getWidth();
31744                 this.baseScale = width / this.imageEl.OriginWidth;
31745             }
31746             
31747         }
31748         
31749         return;
31750     },
31751     
31752     getScaleLevel : function()
31753     {
31754         return this.baseScale * Math.pow(1.1, this.scale);
31755     },
31756     
31757     onTouchStart : function(e)
31758     {
31759         if(!this.canvasLoaded){
31760             this.beforeSelectFile(e);
31761             return;
31762         }
31763         
31764         var touches = e.browserEvent.touches;
31765         
31766         if(!touches){
31767             return;
31768         }
31769         
31770         if(touches.length == 1){
31771             this.onMouseDown(e);
31772             return;
31773         }
31774         
31775         if(touches.length != 2){
31776             return;
31777         }
31778         
31779         var coords = [];
31780         
31781         for(var i = 0, finger; finger = touches[i]; i++){
31782             coords.push(finger.pageX, finger.pageY);
31783         }
31784         
31785         var x = Math.pow(coords[0] - coords[2], 2);
31786         var y = Math.pow(coords[1] - coords[3], 2);
31787         
31788         this.startDistance = Math.sqrt(x + y);
31789         
31790         this.startScale = this.scale;
31791         
31792         this.pinching = true;
31793         this.dragable = false;
31794         
31795     },
31796     
31797     onTouchMove : function(e)
31798     {
31799         if(!this.pinching && !this.dragable){
31800             return;
31801         }
31802         
31803         var touches = e.browserEvent.touches;
31804         
31805         if(!touches){
31806             return;
31807         }
31808         
31809         if(this.dragable){
31810             this.onMouseMove(e);
31811             return;
31812         }
31813         
31814         var coords = [];
31815         
31816         for(var i = 0, finger; finger = touches[i]; i++){
31817             coords.push(finger.pageX, finger.pageY);
31818         }
31819         
31820         var x = Math.pow(coords[0] - coords[2], 2);
31821         var y = Math.pow(coords[1] - coords[3], 2);
31822         
31823         this.endDistance = Math.sqrt(x + y);
31824         
31825         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31826         
31827         if(!this.zoomable()){
31828             this.scale = this.startScale;
31829             return;
31830         }
31831         
31832         this.draw();
31833         
31834     },
31835     
31836     onTouchEnd : function(e)
31837     {
31838         this.pinching = false;
31839         this.dragable = false;
31840         
31841     },
31842     
31843     process : function(file, crop)
31844     {
31845         if(this.loadMask){
31846             this.maskEl.mask(this.loadingText);
31847         }
31848         
31849         this.xhr = new XMLHttpRequest();
31850         
31851         file.xhr = this.xhr;
31852
31853         this.xhr.open(this.method, this.url, true);
31854         
31855         var headers = {
31856             "Accept": "application/json",
31857             "Cache-Control": "no-cache",
31858             "X-Requested-With": "XMLHttpRequest"
31859         };
31860         
31861         for (var headerName in headers) {
31862             var headerValue = headers[headerName];
31863             if (headerValue) {
31864                 this.xhr.setRequestHeader(headerName, headerValue);
31865             }
31866         }
31867         
31868         var _this = this;
31869         
31870         this.xhr.onload = function()
31871         {
31872             _this.xhrOnLoad(_this.xhr);
31873         }
31874         
31875         this.xhr.onerror = function()
31876         {
31877             _this.xhrOnError(_this.xhr);
31878         }
31879         
31880         var formData = new FormData();
31881
31882         formData.append('returnHTML', 'NO');
31883         
31884         if(crop){
31885             formData.append('crop', crop);
31886         }
31887         
31888         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31889             formData.append(this.paramName, file, file.name);
31890         }
31891         
31892         if(typeof(file.filename) != 'undefined'){
31893             formData.append('filename', file.filename);
31894         }
31895         
31896         if(typeof(file.mimetype) != 'undefined'){
31897             formData.append('mimetype', file.mimetype);
31898         }
31899         
31900         if(this.fireEvent('arrange', this, formData) != false){
31901             this.xhr.send(formData);
31902         };
31903     },
31904     
31905     xhrOnLoad : function(xhr)
31906     {
31907         if(this.loadMask){
31908             this.maskEl.unmask();
31909         }
31910         
31911         if (xhr.readyState !== 4) {
31912             this.fireEvent('exception', this, xhr);
31913             return;
31914         }
31915
31916         var response = Roo.decode(xhr.responseText);
31917         
31918         if(!response.success){
31919             this.fireEvent('exception', this, xhr);
31920             return;
31921         }
31922         
31923         var response = Roo.decode(xhr.responseText);
31924         
31925         this.fireEvent('upload', this, response);
31926         
31927     },
31928     
31929     xhrOnError : function()
31930     {
31931         if(this.loadMask){
31932             this.maskEl.unmask();
31933         }
31934         
31935         Roo.log('xhr on error');
31936         
31937         var response = Roo.decode(xhr.responseText);
31938           
31939         Roo.log(response);
31940         
31941     },
31942     
31943     prepare : function(file)
31944     {   
31945         if(this.loadMask){
31946             this.maskEl.mask(this.loadingText);
31947         }
31948         
31949         this.file = false;
31950         this.exif = {};
31951         
31952         if(typeof(file) === 'string'){
31953             this.loadCanvas(file);
31954             return;
31955         }
31956         
31957         if(!file || !this.urlAPI){
31958             return;
31959         }
31960         
31961         this.file = file;
31962         this.cropType = file.type;
31963         
31964         var _this = this;
31965         
31966         if(this.fireEvent('prepare', this, this.file) != false){
31967             
31968             var reader = new FileReader();
31969             
31970             reader.onload = function (e) {
31971                 if (e.target.error) {
31972                     Roo.log(e.target.error);
31973                     return;
31974                 }
31975                 
31976                 var buffer = e.target.result,
31977                     dataView = new DataView(buffer),
31978                     offset = 2,
31979                     maxOffset = dataView.byteLength - 4,
31980                     markerBytes,
31981                     markerLength;
31982                 
31983                 if (dataView.getUint16(0) === 0xffd8) {
31984                     while (offset < maxOffset) {
31985                         markerBytes = dataView.getUint16(offset);
31986                         
31987                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31988                             markerLength = dataView.getUint16(offset + 2) + 2;
31989                             if (offset + markerLength > dataView.byteLength) {
31990                                 Roo.log('Invalid meta data: Invalid segment size.');
31991                                 break;
31992                             }
31993                             
31994                             if(markerBytes == 0xffe1){
31995                                 _this.parseExifData(
31996                                     dataView,
31997                                     offset,
31998                                     markerLength
31999                                 );
32000                             }
32001                             
32002                             offset += markerLength;
32003                             
32004                             continue;
32005                         }
32006                         
32007                         break;
32008                     }
32009                     
32010                 }
32011                 
32012                 var url = _this.urlAPI.createObjectURL(_this.file);
32013                 
32014                 _this.loadCanvas(url);
32015                 
32016                 return;
32017             }
32018             
32019             reader.readAsArrayBuffer(this.file);
32020             
32021         }
32022         
32023     },
32024     
32025     parseExifData : function(dataView, offset, length)
32026     {
32027         var tiffOffset = offset + 10,
32028             littleEndian,
32029             dirOffset;
32030     
32031         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32032             // No Exif data, might be XMP data instead
32033             return;
32034         }
32035         
32036         // Check for the ASCII code for "Exif" (0x45786966):
32037         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32038             // No Exif data, might be XMP data instead
32039             return;
32040         }
32041         if (tiffOffset + 8 > dataView.byteLength) {
32042             Roo.log('Invalid Exif data: Invalid segment size.');
32043             return;
32044         }
32045         // Check for the two null bytes:
32046         if (dataView.getUint16(offset + 8) !== 0x0000) {
32047             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32048             return;
32049         }
32050         // Check the byte alignment:
32051         switch (dataView.getUint16(tiffOffset)) {
32052         case 0x4949:
32053             littleEndian = true;
32054             break;
32055         case 0x4D4D:
32056             littleEndian = false;
32057             break;
32058         default:
32059             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32060             return;
32061         }
32062         // Check for the TIFF tag marker (0x002A):
32063         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32064             Roo.log('Invalid Exif data: Missing TIFF marker.');
32065             return;
32066         }
32067         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32068         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32069         
32070         this.parseExifTags(
32071             dataView,
32072             tiffOffset,
32073             tiffOffset + dirOffset,
32074             littleEndian
32075         );
32076     },
32077     
32078     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32079     {
32080         var tagsNumber,
32081             dirEndOffset,
32082             i;
32083         if (dirOffset + 6 > dataView.byteLength) {
32084             Roo.log('Invalid Exif data: Invalid directory offset.');
32085             return;
32086         }
32087         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32088         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32089         if (dirEndOffset + 4 > dataView.byteLength) {
32090             Roo.log('Invalid Exif data: Invalid directory size.');
32091             return;
32092         }
32093         for (i = 0; i < tagsNumber; i += 1) {
32094             this.parseExifTag(
32095                 dataView,
32096                 tiffOffset,
32097                 dirOffset + 2 + 12 * i, // tag offset
32098                 littleEndian
32099             );
32100         }
32101         // Return the offset to the next directory:
32102         return dataView.getUint32(dirEndOffset, littleEndian);
32103     },
32104     
32105     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32106     {
32107         var tag = dataView.getUint16(offset, littleEndian);
32108         
32109         this.exif[tag] = this.getExifValue(
32110             dataView,
32111             tiffOffset,
32112             offset,
32113             dataView.getUint16(offset + 2, littleEndian), // tag type
32114             dataView.getUint32(offset + 4, littleEndian), // tag length
32115             littleEndian
32116         );
32117     },
32118     
32119     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32120     {
32121         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32122             tagSize,
32123             dataOffset,
32124             values,
32125             i,
32126             str,
32127             c;
32128     
32129         if (!tagType) {
32130             Roo.log('Invalid Exif data: Invalid tag type.');
32131             return;
32132         }
32133         
32134         tagSize = tagType.size * length;
32135         // Determine if the value is contained in the dataOffset bytes,
32136         // or if the value at the dataOffset is a pointer to the actual data:
32137         dataOffset = tagSize > 4 ?
32138                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32139         if (dataOffset + tagSize > dataView.byteLength) {
32140             Roo.log('Invalid Exif data: Invalid data offset.');
32141             return;
32142         }
32143         if (length === 1) {
32144             return tagType.getValue(dataView, dataOffset, littleEndian);
32145         }
32146         values = [];
32147         for (i = 0; i < length; i += 1) {
32148             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32149         }
32150         
32151         if (tagType.ascii) {
32152             str = '';
32153             // Concatenate the chars:
32154             for (i = 0; i < values.length; i += 1) {
32155                 c = values[i];
32156                 // Ignore the terminating NULL byte(s):
32157                 if (c === '\u0000') {
32158                     break;
32159                 }
32160                 str += c;
32161             }
32162             return str;
32163         }
32164         return values;
32165     }
32166     
32167 });
32168
32169 Roo.apply(Roo.bootstrap.UploadCropbox, {
32170     tags : {
32171         'Orientation': 0x0112
32172     },
32173     
32174     Orientation: {
32175             1: 0, //'top-left',
32176 //            2: 'top-right',
32177             3: 180, //'bottom-right',
32178 //            4: 'bottom-left',
32179 //            5: 'left-top',
32180             6: 90, //'right-top',
32181 //            7: 'right-bottom',
32182             8: 270 //'left-bottom'
32183     },
32184     
32185     exifTagTypes : {
32186         // byte, 8-bit unsigned int:
32187         1: {
32188             getValue: function (dataView, dataOffset) {
32189                 return dataView.getUint8(dataOffset);
32190             },
32191             size: 1
32192         },
32193         // ascii, 8-bit byte:
32194         2: {
32195             getValue: function (dataView, dataOffset) {
32196                 return String.fromCharCode(dataView.getUint8(dataOffset));
32197             },
32198             size: 1,
32199             ascii: true
32200         },
32201         // short, 16 bit int:
32202         3: {
32203             getValue: function (dataView, dataOffset, littleEndian) {
32204                 return dataView.getUint16(dataOffset, littleEndian);
32205             },
32206             size: 2
32207         },
32208         // long, 32 bit int:
32209         4: {
32210             getValue: function (dataView, dataOffset, littleEndian) {
32211                 return dataView.getUint32(dataOffset, littleEndian);
32212             },
32213             size: 4
32214         },
32215         // rational = two long values, first is numerator, second is denominator:
32216         5: {
32217             getValue: function (dataView, dataOffset, littleEndian) {
32218                 return dataView.getUint32(dataOffset, littleEndian) /
32219                     dataView.getUint32(dataOffset + 4, littleEndian);
32220             },
32221             size: 8
32222         },
32223         // slong, 32 bit signed int:
32224         9: {
32225             getValue: function (dataView, dataOffset, littleEndian) {
32226                 return dataView.getInt32(dataOffset, littleEndian);
32227             },
32228             size: 4
32229         },
32230         // srational, two slongs, first is numerator, second is denominator:
32231         10: {
32232             getValue: function (dataView, dataOffset, littleEndian) {
32233                 return dataView.getInt32(dataOffset, littleEndian) /
32234                     dataView.getInt32(dataOffset + 4, littleEndian);
32235             },
32236             size: 8
32237         }
32238     },
32239     
32240     footer : {
32241         STANDARD : [
32242             {
32243                 tag : 'div',
32244                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32245                 action : 'rotate-left',
32246                 cn : [
32247                     {
32248                         tag : 'button',
32249                         cls : 'btn btn-default',
32250                         html : '<i class="fa fa-undo"></i>'
32251                     }
32252                 ]
32253             },
32254             {
32255                 tag : 'div',
32256                 cls : 'btn-group roo-upload-cropbox-picture',
32257                 action : 'picture',
32258                 cn : [
32259                     {
32260                         tag : 'button',
32261                         cls : 'btn btn-default',
32262                         html : '<i class="fa fa-picture-o"></i>'
32263                     }
32264                 ]
32265             },
32266             {
32267                 tag : 'div',
32268                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32269                 action : 'rotate-right',
32270                 cn : [
32271                     {
32272                         tag : 'button',
32273                         cls : 'btn btn-default',
32274                         html : '<i class="fa fa-repeat"></i>'
32275                     }
32276                 ]
32277             }
32278         ],
32279         DOCUMENT : [
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-download',
32295                 action : 'download',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-download"></i>'
32301                     }
32302                 ]
32303             },
32304             {
32305                 tag : 'div',
32306                 cls : 'btn-group roo-upload-cropbox-crop',
32307                 action : 'crop',
32308                 cn : [
32309                     {
32310                         tag : 'button',
32311                         cls : 'btn btn-default',
32312                         html : '<i class="fa fa-crop"></i>'
32313                     }
32314                 ]
32315             },
32316             {
32317                 tag : 'div',
32318                 cls : 'btn-group roo-upload-cropbox-trash',
32319                 action : 'trash',
32320                 cn : [
32321                     {
32322                         tag : 'button',
32323                         cls : 'btn btn-default',
32324                         html : '<i class="fa fa-trash"></i>'
32325                     }
32326                 ]
32327             },
32328             {
32329                 tag : 'div',
32330                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32331                 action : 'rotate-right',
32332                 cn : [
32333                     {
32334                         tag : 'button',
32335                         cls : 'btn btn-default',
32336                         html : '<i class="fa fa-repeat"></i>'
32337                     }
32338                 ]
32339             }
32340         ],
32341         ROTATOR : [
32342             {
32343                 tag : 'div',
32344                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32345                 action : 'rotate-left',
32346                 cn : [
32347                     {
32348                         tag : 'button',
32349                         cls : 'btn btn-default',
32350                         html : '<i class="fa fa-undo"></i>'
32351                     }
32352                 ]
32353             },
32354             {
32355                 tag : 'div',
32356                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357                 action : 'rotate-right',
32358                 cn : [
32359                     {
32360                         tag : 'button',
32361                         cls : 'btn btn-default',
32362                         html : '<i class="fa fa-repeat"></i>'
32363                     }
32364                 ]
32365             }
32366         ]
32367     }
32368 });
32369
32370 /*
32371 * Licence: LGPL
32372 */
32373
32374 /**
32375  * @class Roo.bootstrap.DocumentManager
32376  * @extends Roo.bootstrap.Component
32377  * Bootstrap DocumentManager class
32378  * @cfg {String} paramName default 'imageUpload'
32379  * @cfg {String} toolTipName default 'filename'
32380  * @cfg {String} method default POST
32381  * @cfg {String} url action url
32382  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32383  * @cfg {Boolean} multiple multiple upload default true
32384  * @cfg {Number} thumbSize default 300
32385  * @cfg {String} fieldLabel
32386  * @cfg {Number} labelWidth default 4
32387  * @cfg {String} labelAlign (left|top) default left
32388  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32389 * @cfg {Number} labellg set the width of label (1-12)
32390  * @cfg {Number} labelmd set the width of label (1-12)
32391  * @cfg {Number} labelsm set the width of label (1-12)
32392  * @cfg {Number} labelxs set the width of label (1-12)
32393  * 
32394  * @constructor
32395  * Create a new DocumentManager
32396  * @param {Object} config The config object
32397  */
32398
32399 Roo.bootstrap.DocumentManager = function(config){
32400     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32401     
32402     this.files = [];
32403     this.delegates = [];
32404     
32405     this.addEvents({
32406         /**
32407          * @event initial
32408          * Fire when initial the DocumentManager
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          */
32411         "initial" : true,
32412         /**
32413          * @event inspect
32414          * inspect selected file
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {File} file
32417          */
32418         "inspect" : true,
32419         /**
32420          * @event exception
32421          * Fire when xhr load exception
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {XMLHttpRequest} xhr
32424          */
32425         "exception" : true,
32426         /**
32427          * @event afterupload
32428          * Fire when xhr load exception
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {XMLHttpRequest} xhr
32431          */
32432         "afterupload" : true,
32433         /**
32434          * @event prepare
32435          * prepare the form data
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          * @param {Object} formData
32438          */
32439         "prepare" : true,
32440         /**
32441          * @event remove
32442          * Fire when remove the file
32443          * @param {Roo.bootstrap.DocumentManager} this
32444          * @param {Object} file
32445          */
32446         "remove" : true,
32447         /**
32448          * @event refresh
32449          * Fire after refresh the file
32450          * @param {Roo.bootstrap.DocumentManager} this
32451          */
32452         "refresh" : true,
32453         /**
32454          * @event click
32455          * Fire after click the image
32456          * @param {Roo.bootstrap.DocumentManager} this
32457          * @param {Object} file
32458          */
32459         "click" : true,
32460         /**
32461          * @event edit
32462          * Fire when upload a image and editable set to true
32463          * @param {Roo.bootstrap.DocumentManager} this
32464          * @param {Object} file
32465          */
32466         "edit" : true,
32467         /**
32468          * @event beforeselectfile
32469          * Fire before select file
32470          * @param {Roo.bootstrap.DocumentManager} this
32471          */
32472         "beforeselectfile" : true,
32473         /**
32474          * @event process
32475          * Fire before process file
32476          * @param {Roo.bootstrap.DocumentManager} this
32477          * @param {Object} file
32478          */
32479         "process" : true,
32480         /**
32481          * @event previewrendered
32482          * Fire when preview rendered
32483          * @param {Roo.bootstrap.DocumentManager} this
32484          * @param {Object} file
32485          */
32486         "previewrendered" : true,
32487         /**
32488          */
32489         "previewResize" : true
32490         
32491     });
32492 };
32493
32494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32495     
32496     boxes : 0,
32497     inputName : '',
32498     thumbSize : 300,
32499     multiple : true,
32500     files : false,
32501     method : 'POST',
32502     url : '',
32503     paramName : 'imageUpload',
32504     toolTipName : 'filename',
32505     fieldLabel : '',
32506     labelWidth : 4,
32507     labelAlign : 'left',
32508     editable : true,
32509     delegates : false,
32510     xhr : false, 
32511     
32512     labellg : 0,
32513     labelmd : 0,
32514     labelsm : 0,
32515     labelxs : 0,
32516     
32517     getAutoCreate : function()
32518     {   
32519         var managerWidget = {
32520             tag : 'div',
32521             cls : 'roo-document-manager',
32522             cn : [
32523                 {
32524                     tag : 'input',
32525                     cls : 'roo-document-manager-selector',
32526                     type : 'file'
32527                 },
32528                 {
32529                     tag : 'div',
32530                     cls : 'roo-document-manager-uploader',
32531                     cn : [
32532                         {
32533                             tag : 'div',
32534                             cls : 'roo-document-manager-upload-btn',
32535                             html : '<i class="fa fa-plus"></i>'
32536                         }
32537                     ]
32538                     
32539                 }
32540             ]
32541         };
32542         
32543         var content = [
32544             {
32545                 tag : 'div',
32546                 cls : 'column col-md-12',
32547                 cn : managerWidget
32548             }
32549         ];
32550         
32551         if(this.fieldLabel.length){
32552             
32553             content = [
32554                 {
32555                     tag : 'div',
32556                     cls : 'column col-md-12',
32557                     html : this.fieldLabel
32558                 },
32559                 {
32560                     tag : 'div',
32561                     cls : 'column col-md-12',
32562                     cn : managerWidget
32563                 }
32564             ];
32565
32566             if(this.labelAlign == 'left'){
32567                 content = [
32568                     {
32569                         tag : 'div',
32570                         cls : 'column',
32571                         html : this.fieldLabel
32572                     },
32573                     {
32574                         tag : 'div',
32575                         cls : 'column',
32576                         cn : managerWidget
32577                     }
32578                 ];
32579                 
32580                 if(this.labelWidth > 12){
32581                     content[0].style = "width: " + this.labelWidth + 'px';
32582                 }
32583
32584                 if(this.labelWidth < 13 && this.labelmd == 0){
32585                     this.labelmd = this.labelWidth;
32586                 }
32587
32588                 if(this.labellg > 0){
32589                     content[0].cls += ' col-lg-' + this.labellg;
32590                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32591                 }
32592
32593                 if(this.labelmd > 0){
32594                     content[0].cls += ' col-md-' + this.labelmd;
32595                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32596                 }
32597
32598                 if(this.labelsm > 0){
32599                     content[0].cls += ' col-sm-' + this.labelsm;
32600                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32601                 }
32602
32603                 if(this.labelxs > 0){
32604                     content[0].cls += ' col-xs-' + this.labelxs;
32605                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32606                 }
32607                 
32608             }
32609         }
32610         
32611         var cfg = {
32612             tag : 'div',
32613             cls : 'row clearfix',
32614             cn : content
32615         };
32616         
32617         return cfg;
32618         
32619     },
32620     
32621     initEvents : function()
32622     {
32623         this.managerEl = this.el.select('.roo-document-manager', true).first();
32624         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32625         
32626         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32627         this.selectorEl.hide();
32628         
32629         if(this.multiple){
32630             this.selectorEl.attr('multiple', 'multiple');
32631         }
32632         
32633         this.selectorEl.on('change', this.onFileSelected, this);
32634         
32635         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32636         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32637         
32638         this.uploader.on('click', this.onUploaderClick, this);
32639         
32640         this.renderProgressDialog();
32641         
32642         var _this = this;
32643         
32644         window.addEventListener("resize", function() { _this.refresh(); } );
32645         
32646         this.fireEvent('initial', this);
32647     },
32648     
32649     renderProgressDialog : function()
32650     {
32651         var _this = this;
32652         
32653         this.progressDialog = new Roo.bootstrap.Modal({
32654             cls : 'roo-document-manager-progress-dialog',
32655             allow_close : false,
32656             animate : false,
32657             title : '',
32658             buttons : [
32659                 {
32660                     name  :'cancel',
32661                     weight : 'danger',
32662                     html : 'Cancel'
32663                 }
32664             ], 
32665             listeners : { 
32666                 btnclick : function() {
32667                     _this.uploadCancel();
32668                     this.hide();
32669                 }
32670             }
32671         });
32672          
32673         this.progressDialog.render(Roo.get(document.body));
32674          
32675         this.progress = new Roo.bootstrap.Progress({
32676             cls : 'roo-document-manager-progress',
32677             active : true,
32678             striped : true
32679         });
32680         
32681         this.progress.render(this.progressDialog.getChildContainer());
32682         
32683         this.progressBar = new Roo.bootstrap.ProgressBar({
32684             cls : 'roo-document-manager-progress-bar',
32685             aria_valuenow : 0,
32686             aria_valuemin : 0,
32687             aria_valuemax : 12,
32688             panel : 'success'
32689         });
32690         
32691         this.progressBar.render(this.progress.getChildContainer());
32692     },
32693     
32694     onUploaderClick : function(e)
32695     {
32696         e.preventDefault();
32697      
32698         if(this.fireEvent('beforeselectfile', this) != false){
32699             this.selectorEl.dom.click();
32700         }
32701         
32702     },
32703     
32704     onFileSelected : function(e)
32705     {
32706         e.preventDefault();
32707         
32708         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32709             return;
32710         }
32711         
32712         Roo.each(this.selectorEl.dom.files, function(file){
32713             if(this.fireEvent('inspect', this, file) != false){
32714                 this.files.push(file);
32715             }
32716         }, this);
32717         
32718         this.queue();
32719         
32720     },
32721     
32722     queue : function()
32723     {
32724         this.selectorEl.dom.value = '';
32725         
32726         if(!this.files || !this.files.length){
32727             return;
32728         }
32729         
32730         if(this.boxes > 0 && this.files.length > this.boxes){
32731             this.files = this.files.slice(0, this.boxes);
32732         }
32733         
32734         this.uploader.show();
32735         
32736         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32737             this.uploader.hide();
32738         }
32739         
32740         var _this = this;
32741         
32742         var files = [];
32743         
32744         var docs = [];
32745         
32746         Roo.each(this.files, function(file){
32747             
32748             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32749                 var f = this.renderPreview(file);
32750                 files.push(f);
32751                 return;
32752             }
32753             
32754             if(file.type.indexOf('image') != -1){
32755                 this.delegates.push(
32756                     (function(){
32757                         _this.process(file);
32758                     }).createDelegate(this)
32759                 );
32760         
32761                 return;
32762             }
32763             
32764             docs.push(
32765                 (function(){
32766                     _this.process(file);
32767                 }).createDelegate(this)
32768             );
32769             
32770         }, this);
32771         
32772         this.files = files;
32773         
32774         this.delegates = this.delegates.concat(docs);
32775         
32776         if(!this.delegates.length){
32777             this.refresh();
32778             return;
32779         }
32780         
32781         this.progressBar.aria_valuemax = this.delegates.length;
32782         
32783         this.arrange();
32784         
32785         return;
32786     },
32787     
32788     arrange : function()
32789     {
32790         if(!this.delegates.length){
32791             this.progressDialog.hide();
32792             this.refresh();
32793             return;
32794         }
32795         
32796         var delegate = this.delegates.shift();
32797         
32798         this.progressDialog.show();
32799         
32800         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32801         
32802         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32803         
32804         delegate();
32805     },
32806     
32807     refresh : function()
32808     {
32809         this.uploader.show();
32810         
32811         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32812             this.uploader.hide();
32813         }
32814         
32815         Roo.isTouch ? this.closable(false) : this.closable(true);
32816         
32817         this.fireEvent('refresh', this);
32818     },
32819     
32820     onRemove : function(e, el, o)
32821     {
32822         e.preventDefault();
32823         
32824         this.fireEvent('remove', this, o);
32825         
32826     },
32827     
32828     remove : function(o)
32829     {
32830         var files = [];
32831         
32832         Roo.each(this.files, function(file){
32833             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32834                 files.push(file);
32835                 return;
32836             }
32837
32838             o.target.remove();
32839
32840         }, this);
32841         
32842         this.files = files;
32843         
32844         this.refresh();
32845     },
32846     
32847     clear : function()
32848     {
32849         Roo.each(this.files, function(file){
32850             if(!file.target){
32851                 return;
32852             }
32853             
32854             file.target.remove();
32855
32856         }, this);
32857         
32858         this.files = [];
32859         
32860         this.refresh();
32861     },
32862     
32863     onClick : function(e, el, o)
32864     {
32865         e.preventDefault();
32866         
32867         this.fireEvent('click', this, o);
32868         
32869     },
32870     
32871     closable : function(closable)
32872     {
32873         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32874             
32875             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32876             
32877             if(closable){
32878                 el.show();
32879                 return;
32880             }
32881             
32882             el.hide();
32883             
32884         }, this);
32885     },
32886     
32887     xhrOnLoad : function(xhr)
32888     {
32889         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32890             el.remove();
32891         }, this);
32892         
32893         if (xhr.readyState !== 4) {
32894             this.arrange();
32895             this.fireEvent('exception', this, xhr);
32896             return;
32897         }
32898
32899         var response = Roo.decode(xhr.responseText);
32900         
32901         if(!response.success){
32902             this.arrange();
32903             this.fireEvent('exception', this, xhr);
32904             return;
32905         }
32906         
32907         var file = this.renderPreview(response.data);
32908         
32909         this.files.push(file);
32910         
32911         this.arrange();
32912         
32913         this.fireEvent('afterupload', this, xhr);
32914         
32915     },
32916     
32917     xhrOnError : function(xhr)
32918     {
32919         Roo.log('xhr on error');
32920         
32921         var response = Roo.decode(xhr.responseText);
32922           
32923         Roo.log(response);
32924         
32925         this.arrange();
32926     },
32927     
32928     process : function(file)
32929     {
32930         if(this.fireEvent('process', this, file) !== false){
32931             if(this.editable && file.type.indexOf('image') != -1){
32932                 this.fireEvent('edit', this, file);
32933                 return;
32934             }
32935
32936             this.uploadStart(file, false);
32937
32938             return;
32939         }
32940         
32941     },
32942     
32943     uploadStart : function(file, crop)
32944     {
32945         this.xhr = new XMLHttpRequest();
32946         
32947         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32948             this.arrange();
32949             return;
32950         }
32951         
32952         file.xhr = this.xhr;
32953             
32954         this.managerEl.createChild({
32955             tag : 'div',
32956             cls : 'roo-document-manager-loading',
32957             cn : [
32958                 {
32959                     tag : 'div',
32960                     tooltip : file.name,
32961                     cls : 'roo-document-manager-thumb',
32962                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32963                 }
32964             ]
32965
32966         });
32967
32968         this.xhr.open(this.method, this.url, true);
32969         
32970         var headers = {
32971             "Accept": "application/json",
32972             "Cache-Control": "no-cache",
32973             "X-Requested-With": "XMLHttpRequest"
32974         };
32975         
32976         for (var headerName in headers) {
32977             var headerValue = headers[headerName];
32978             if (headerValue) {
32979                 this.xhr.setRequestHeader(headerName, headerValue);
32980             }
32981         }
32982         
32983         var _this = this;
32984         
32985         this.xhr.onload = function()
32986         {
32987             _this.xhrOnLoad(_this.xhr);
32988         }
32989         
32990         this.xhr.onerror = function()
32991         {
32992             _this.xhrOnError(_this.xhr);
32993         }
32994         
32995         var formData = new FormData();
32996
32997         formData.append('returnHTML', 'NO');
32998         
32999         if(crop){
33000             formData.append('crop', crop);
33001         }
33002         
33003         formData.append(this.paramName, file, file.name);
33004         
33005         var options = {
33006             file : file, 
33007             manually : false
33008         };
33009         
33010         if(this.fireEvent('prepare', this, formData, options) != false){
33011             
33012             if(options.manually){
33013                 return;
33014             }
33015             
33016             this.xhr.send(formData);
33017             return;
33018         };
33019         
33020         this.uploadCancel();
33021     },
33022     
33023     uploadCancel : function()
33024     {
33025         if (this.xhr) {
33026             this.xhr.abort();
33027         }
33028         
33029         this.delegates = [];
33030         
33031         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33032             el.remove();
33033         }, this);
33034         
33035         this.arrange();
33036     },
33037     
33038     renderPreview : function(file)
33039     {
33040         if(typeof(file.target) != 'undefined' && file.target){
33041             return file;
33042         }
33043         
33044         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33045         
33046         var previewEl = this.managerEl.createChild({
33047             tag : 'div',
33048             cls : 'roo-document-manager-preview',
33049             cn : [
33050                 {
33051                     tag : 'div',
33052                     tooltip : file[this.toolTipName],
33053                     cls : 'roo-document-manager-thumb',
33054                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33055                 },
33056                 {
33057                     tag : 'button',
33058                     cls : 'close',
33059                     html : '<i class="fa fa-times-circle"></i>'
33060                 }
33061             ]
33062         });
33063
33064         var close = previewEl.select('button.close', true).first();
33065
33066         close.on('click', this.onRemove, this, file);
33067
33068         file.target = previewEl;
33069
33070         var image = previewEl.select('img', true).first();
33071         
33072         var _this = this;
33073         
33074         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33075         
33076         image.on('click', this.onClick, this, file);
33077         
33078         this.fireEvent('previewrendered', this, file);
33079         
33080         return file;
33081         
33082     },
33083     
33084     onPreviewLoad : function(file, image)
33085     {
33086         if(typeof(file.target) == 'undefined' || !file.target){
33087             return;
33088         }
33089         
33090         var width = image.dom.naturalWidth || image.dom.width;
33091         var height = image.dom.naturalHeight || image.dom.height;
33092         
33093         if(!this.previewResize) {
33094             return;
33095         }
33096         
33097         if(width > height){
33098             file.target.addClass('wide');
33099             return;
33100         }
33101         
33102         file.target.addClass('tall');
33103         return;
33104         
33105     },
33106     
33107     uploadFromSource : function(file, crop)
33108     {
33109         this.xhr = new XMLHttpRequest();
33110         
33111         this.managerEl.createChild({
33112             tag : 'div',
33113             cls : 'roo-document-manager-loading',
33114             cn : [
33115                 {
33116                     tag : 'div',
33117                     tooltip : file.name,
33118                     cls : 'roo-document-manager-thumb',
33119                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33120                 }
33121             ]
33122
33123         });
33124
33125         this.xhr.open(this.method, this.url, true);
33126         
33127         var headers = {
33128             "Accept": "application/json",
33129             "Cache-Control": "no-cache",
33130             "X-Requested-With": "XMLHttpRequest"
33131         };
33132         
33133         for (var headerName in headers) {
33134             var headerValue = headers[headerName];
33135             if (headerValue) {
33136                 this.xhr.setRequestHeader(headerName, headerValue);
33137             }
33138         }
33139         
33140         var _this = this;
33141         
33142         this.xhr.onload = function()
33143         {
33144             _this.xhrOnLoad(_this.xhr);
33145         }
33146         
33147         this.xhr.onerror = function()
33148         {
33149             _this.xhrOnError(_this.xhr);
33150         }
33151         
33152         var formData = new FormData();
33153
33154         formData.append('returnHTML', 'NO');
33155         
33156         formData.append('crop', crop);
33157         
33158         if(typeof(file.filename) != 'undefined'){
33159             formData.append('filename', file.filename);
33160         }
33161         
33162         if(typeof(file.mimetype) != 'undefined'){
33163             formData.append('mimetype', file.mimetype);
33164         }
33165         
33166         Roo.log(formData);
33167         
33168         if(this.fireEvent('prepare', this, formData) != false){
33169             this.xhr.send(formData);
33170         };
33171     }
33172 });
33173
33174 /*
33175 * Licence: LGPL
33176 */
33177
33178 /**
33179  * @class Roo.bootstrap.DocumentViewer
33180  * @extends Roo.bootstrap.Component
33181  * Bootstrap DocumentViewer class
33182  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33183  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33184  * 
33185  * @constructor
33186  * Create a new DocumentViewer
33187  * @param {Object} config The config object
33188  */
33189
33190 Roo.bootstrap.DocumentViewer = function(config){
33191     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33192     
33193     this.addEvents({
33194         /**
33195          * @event initial
33196          * Fire after initEvent
33197          * @param {Roo.bootstrap.DocumentViewer} this
33198          */
33199         "initial" : true,
33200         /**
33201          * @event click
33202          * Fire after click
33203          * @param {Roo.bootstrap.DocumentViewer} this
33204          */
33205         "click" : true,
33206         /**
33207          * @event download
33208          * Fire after download button
33209          * @param {Roo.bootstrap.DocumentViewer} this
33210          */
33211         "download" : true,
33212         /**
33213          * @event trash
33214          * Fire after trash button
33215          * @param {Roo.bootstrap.DocumentViewer} this
33216          */
33217         "trash" : true
33218         
33219     });
33220 };
33221
33222 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33223     
33224     showDownload : true,
33225     
33226     showTrash : true,
33227     
33228     getAutoCreate : function()
33229     {
33230         var cfg = {
33231             tag : 'div',
33232             cls : 'roo-document-viewer',
33233             cn : [
33234                 {
33235                     tag : 'div',
33236                     cls : 'roo-document-viewer-body',
33237                     cn : [
33238                         {
33239                             tag : 'div',
33240                             cls : 'roo-document-viewer-thumb',
33241                             cn : [
33242                                 {
33243                                     tag : 'img',
33244                                     cls : 'roo-document-viewer-image'
33245                                 }
33246                             ]
33247                         }
33248                     ]
33249                 },
33250                 {
33251                     tag : 'div',
33252                     cls : 'roo-document-viewer-footer',
33253                     cn : {
33254                         tag : 'div',
33255                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33256                         cn : [
33257                             {
33258                                 tag : 'div',
33259                                 cls : 'btn-group roo-document-viewer-download',
33260                                 cn : [
33261                                     {
33262                                         tag : 'button',
33263                                         cls : 'btn btn-default',
33264                                         html : '<i class="fa fa-download"></i>'
33265                                     }
33266                                 ]
33267                             },
33268                             {
33269                                 tag : 'div',
33270                                 cls : 'btn-group roo-document-viewer-trash',
33271                                 cn : [
33272                                     {
33273                                         tag : 'button',
33274                                         cls : 'btn btn-default',
33275                                         html : '<i class="fa fa-trash"></i>'
33276                                     }
33277                                 ]
33278                             }
33279                         ]
33280                     }
33281                 }
33282             ]
33283         };
33284         
33285         return cfg;
33286     },
33287     
33288     initEvents : function()
33289     {
33290         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33291         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33292         
33293         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33294         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33295         
33296         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33297         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33298         
33299         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33300         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33301         
33302         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33303         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33304         
33305         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33306         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33307         
33308         this.bodyEl.on('click', this.onClick, this);
33309         this.downloadBtn.on('click', this.onDownload, this);
33310         this.trashBtn.on('click', this.onTrash, this);
33311         
33312         this.downloadBtn.hide();
33313         this.trashBtn.hide();
33314         
33315         if(this.showDownload){
33316             this.downloadBtn.show();
33317         }
33318         
33319         if(this.showTrash){
33320             this.trashBtn.show();
33321         }
33322         
33323         if(!this.showDownload && !this.showTrash) {
33324             this.footerEl.hide();
33325         }
33326         
33327     },
33328     
33329     initial : function()
33330     {
33331         this.fireEvent('initial', this);
33332         
33333     },
33334     
33335     onClick : function(e)
33336     {
33337         e.preventDefault();
33338         
33339         this.fireEvent('click', this);
33340     },
33341     
33342     onDownload : function(e)
33343     {
33344         e.preventDefault();
33345         
33346         this.fireEvent('download', this);
33347     },
33348     
33349     onTrash : function(e)
33350     {
33351         e.preventDefault();
33352         
33353         this.fireEvent('trash', this);
33354     }
33355     
33356 });
33357 /*
33358  * - LGPL
33359  *
33360  * FieldLabel
33361  * 
33362  */
33363
33364 /**
33365  * @class Roo.bootstrap.form.FieldLabel
33366  * @extends Roo.bootstrap.Component
33367  * Bootstrap FieldLabel class
33368  * @cfg {String} html contents of the element
33369  * @cfg {String} tag tag of the element default label
33370  * @cfg {String} cls class of the element
33371  * @cfg {String} target label target 
33372  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33373  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33374  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33375  * @cfg {String} iconTooltip default "This field is required"
33376  * @cfg {String} indicatorpos (left|right) default left
33377  * 
33378  * @constructor
33379  * Create a new FieldLabel
33380  * @param {Object} config The config object
33381  */
33382
33383 Roo.bootstrap.form.FieldLabel = function(config){
33384     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33385     
33386     this.addEvents({
33387             /**
33388              * @event invalid
33389              * Fires after the field has been marked as invalid.
33390              * @param {Roo.form.FieldLabel} this
33391              * @param {String} msg The validation message
33392              */
33393             invalid : true,
33394             /**
33395              * @event valid
33396              * Fires after the field has been validated with no errors.
33397              * @param {Roo.form.FieldLabel} this
33398              */
33399             valid : true
33400         });
33401 };
33402
33403 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33404     
33405     tag: 'label',
33406     cls: '',
33407     html: '',
33408     target: '',
33409     allowBlank : true,
33410     invalidClass : 'has-warning',
33411     validClass : 'has-success',
33412     iconTooltip : 'This field is required',
33413     indicatorpos : 'left',
33414     
33415     getAutoCreate : function(){
33416         
33417         var cls = "";
33418         if (!this.allowBlank) {
33419             cls  = "visible";
33420         }
33421         
33422         var cfg = {
33423             tag : this.tag,
33424             cls : 'roo-bootstrap-field-label ' + this.cls,
33425             for : this.target,
33426             cn : [
33427                 {
33428                     tag : 'i',
33429                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33430                     tooltip : this.iconTooltip
33431                 },
33432                 {
33433                     tag : 'span',
33434                     html : this.html
33435                 }
33436             ] 
33437         };
33438         
33439         if(this.indicatorpos == 'right'){
33440             var cfg = {
33441                 tag : this.tag,
33442                 cls : 'roo-bootstrap-field-label ' + this.cls,
33443                 for : this.target,
33444                 cn : [
33445                     {
33446                         tag : 'span',
33447                         html : this.html
33448                     },
33449                     {
33450                         tag : 'i',
33451                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33452                         tooltip : this.iconTooltip
33453                     }
33454                 ] 
33455             };
33456         }
33457         
33458         return cfg;
33459     },
33460     
33461     initEvents: function() 
33462     {
33463         Roo.bootstrap.Element.superclass.initEvents.call(this);
33464         
33465         this.indicator = this.indicatorEl();
33466         
33467         if(this.indicator){
33468             this.indicator.removeClass('visible');
33469             this.indicator.addClass('invisible');
33470         }
33471         
33472         Roo.bootstrap.form.FieldLabel.register(this);
33473     },
33474     
33475     indicatorEl : function()
33476     {
33477         var indicator = this.el.select('i.roo-required-indicator',true).first();
33478         
33479         if(!indicator){
33480             return false;
33481         }
33482         
33483         return indicator;
33484         
33485     },
33486     
33487     /**
33488      * Mark this field as valid
33489      */
33490     markValid : function()
33491     {
33492         if(this.indicator){
33493             this.indicator.removeClass('visible');
33494             this.indicator.addClass('invisible');
33495         }
33496         if (Roo.bootstrap.version == 3) {
33497             this.el.removeClass(this.invalidClass);
33498             this.el.addClass(this.validClass);
33499         } else {
33500             this.el.removeClass('is-invalid');
33501             this.el.addClass('is-valid');
33502         }
33503         
33504         
33505         this.fireEvent('valid', this);
33506     },
33507     
33508     /**
33509      * Mark this field as invalid
33510      * @param {String} msg The validation message
33511      */
33512     markInvalid : function(msg)
33513     {
33514         if(this.indicator){
33515             this.indicator.removeClass('invisible');
33516             this.indicator.addClass('visible');
33517         }
33518           if (Roo.bootstrap.version == 3) {
33519             this.el.removeClass(this.validClass);
33520             this.el.addClass(this.invalidClass);
33521         } else {
33522             this.el.removeClass('is-valid');
33523             this.el.addClass('is-invalid');
33524         }
33525         
33526         
33527         this.fireEvent('invalid', this, msg);
33528     }
33529     
33530    
33531 });
33532
33533 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33534     
33535     groups: {},
33536     
33537      /**
33538     * register a FieldLabel Group
33539     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33540     */
33541     register : function(label)
33542     {
33543         if(this.groups.hasOwnProperty(label.target)){
33544             return;
33545         }
33546      
33547         this.groups[label.target] = label;
33548         
33549     },
33550     /**
33551     * fetch a FieldLabel Group based on the target
33552     * @param {string} target
33553     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33554     */
33555     get: function(target) {
33556         if (typeof(this.groups[target]) == 'undefined') {
33557             return false;
33558         }
33559         
33560         return this.groups[target] ;
33561     }
33562 });
33563
33564  
33565
33566  /*
33567  * - LGPL
33568  *
33569  * page DateSplitField.
33570  * 
33571  */
33572
33573
33574 /**
33575  * @class Roo.bootstrap.form.DateSplitField
33576  * @extends Roo.bootstrap.Component
33577  * Bootstrap DateSplitField class
33578  * @cfg {string} fieldLabel - the label associated
33579  * @cfg {Number} labelWidth set the width of label (0-12)
33580  * @cfg {String} labelAlign (top|left)
33581  * @cfg {Boolean} dayAllowBlank (true|false) default false
33582  * @cfg {Boolean} monthAllowBlank (true|false) default false
33583  * @cfg {Boolean} yearAllowBlank (true|false) default false
33584  * @cfg {string} dayPlaceholder 
33585  * @cfg {string} monthPlaceholder
33586  * @cfg {string} yearPlaceholder
33587  * @cfg {string} dayFormat default 'd'
33588  * @cfg {string} monthFormat default 'm'
33589  * @cfg {string} yearFormat default 'Y'
33590  * @cfg {Number} labellg set the width of label (1-12)
33591  * @cfg {Number} labelmd set the width of label (1-12)
33592  * @cfg {Number} labelsm set the width of label (1-12)
33593  * @cfg {Number} labelxs set the width of label (1-12)
33594
33595  *     
33596  * @constructor
33597  * Create a new DateSplitField
33598  * @param {Object} config The config object
33599  */
33600
33601 Roo.bootstrap.form.DateSplitField = function(config){
33602     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33603     
33604     this.addEvents({
33605         // raw events
33606          /**
33607          * @event years
33608          * getting the data of years
33609          * @param {Roo.bootstrap.form.DateSplitField} this
33610          * @param {Object} years
33611          */
33612         "years" : true,
33613         /**
33614          * @event days
33615          * getting the data of days
33616          * @param {Roo.bootstrap.form.DateSplitField} this
33617          * @param {Object} days
33618          */
33619         "days" : true,
33620         /**
33621          * @event invalid
33622          * Fires after the field has been marked as invalid.
33623          * @param {Roo.form.Field} this
33624          * @param {String} msg The validation message
33625          */
33626         invalid : true,
33627        /**
33628          * @event valid
33629          * Fires after the field has been validated with no errors.
33630          * @param {Roo.form.Field} this
33631          */
33632         valid : true
33633     });
33634 };
33635
33636 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33637     
33638     fieldLabel : '',
33639     labelAlign : 'top',
33640     labelWidth : 3,
33641     dayAllowBlank : false,
33642     monthAllowBlank : false,
33643     yearAllowBlank : false,
33644     dayPlaceholder : '',
33645     monthPlaceholder : '',
33646     yearPlaceholder : '',
33647     dayFormat : 'd',
33648     monthFormat : 'm',
33649     yearFormat : 'Y',
33650     isFormField : true,
33651     labellg : 0,
33652     labelmd : 0,
33653     labelsm : 0,
33654     labelxs : 0,
33655     
33656     getAutoCreate : function()
33657     {
33658         var cfg = {
33659             tag : 'div',
33660             cls : 'row roo-date-split-field-group',
33661             cn : [
33662                 {
33663                     tag : 'input',
33664                     type : 'hidden',
33665                     cls : 'form-hidden-field roo-date-split-field-group-value',
33666                     name : this.name
33667                 }
33668             ]
33669         };
33670         
33671         var labelCls = 'col-md-12';
33672         var contentCls = 'col-md-4';
33673         
33674         if(this.fieldLabel){
33675             
33676             var label = {
33677                 tag : 'div',
33678                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33679                 cn : [
33680                     {
33681                         tag : 'label',
33682                         html : this.fieldLabel
33683                     }
33684                 ]
33685             };
33686             
33687             if(this.labelAlign == 'left'){
33688             
33689                 if(this.labelWidth > 12){
33690                     label.style = "width: " + this.labelWidth + 'px';
33691                 }
33692
33693                 if(this.labelWidth < 13 && this.labelmd == 0){
33694                     this.labelmd = this.labelWidth;
33695                 }
33696
33697                 if(this.labellg > 0){
33698                     labelCls = ' col-lg-' + this.labellg;
33699                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33700                 }
33701
33702                 if(this.labelmd > 0){
33703                     labelCls = ' col-md-' + this.labelmd;
33704                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33705                 }
33706
33707                 if(this.labelsm > 0){
33708                     labelCls = ' col-sm-' + this.labelsm;
33709                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33710                 }
33711
33712                 if(this.labelxs > 0){
33713                     labelCls = ' col-xs-' + this.labelxs;
33714                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33715                 }
33716             }
33717             
33718             label.cls += ' ' + labelCls;
33719             
33720             cfg.cn.push(label);
33721         }
33722         
33723         Roo.each(['day', 'month', 'year'], function(t){
33724             cfg.cn.push({
33725                 tag : 'div',
33726                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33727             });
33728         }, this);
33729         
33730         return cfg;
33731     },
33732     
33733     inputEl: function ()
33734     {
33735         return this.el.select('.roo-date-split-field-group-value', true).first();
33736     },
33737     
33738     onRender : function(ct, position) 
33739     {
33740         var _this = this;
33741         
33742         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33743         
33744         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33745         
33746         this.dayField = new Roo.bootstrap.form.ComboBox({
33747             allowBlank : this.dayAllowBlank,
33748             alwaysQuery : true,
33749             displayField : 'value',
33750             editable : false,
33751             fieldLabel : '',
33752             forceSelection : true,
33753             mode : 'local',
33754             placeholder : this.dayPlaceholder,
33755             selectOnFocus : true,
33756             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33757             triggerAction : 'all',
33758             typeAhead : true,
33759             valueField : 'value',
33760             store : new Roo.data.SimpleStore({
33761                 data : (function() {    
33762                     var days = [];
33763                     _this.fireEvent('days', _this, days);
33764                     return days;
33765                 })(),
33766                 fields : [ 'value' ]
33767             }),
33768             listeners : {
33769                 select : function (_self, record, index)
33770                 {
33771                     _this.setValue(_this.getValue());
33772                 }
33773             }
33774         });
33775
33776         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33777         
33778         this.monthField = new Roo.bootstrap.form.MonthField({
33779             after : '<i class=\"fa fa-calendar\"></i>',
33780             allowBlank : this.monthAllowBlank,
33781             placeholder : this.monthPlaceholder,
33782             readOnly : true,
33783             listeners : {
33784                 render : function (_self)
33785                 {
33786                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33787                         e.preventDefault();
33788                         _self.focus();
33789                     });
33790                 },
33791                 select : function (_self, oldvalue, newvalue)
33792                 {
33793                     _this.setValue(_this.getValue());
33794                 }
33795             }
33796         });
33797         
33798         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33799         
33800         this.yearField = new Roo.bootstrap.form.ComboBox({
33801             allowBlank : this.yearAllowBlank,
33802             alwaysQuery : true,
33803             displayField : 'value',
33804             editable : false,
33805             fieldLabel : '',
33806             forceSelection : true,
33807             mode : 'local',
33808             placeholder : this.yearPlaceholder,
33809             selectOnFocus : true,
33810             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33811             triggerAction : 'all',
33812             typeAhead : true,
33813             valueField : 'value',
33814             store : new Roo.data.SimpleStore({
33815                 data : (function() {
33816                     var years = [];
33817                     _this.fireEvent('years', _this, years);
33818                     return years;
33819                 })(),
33820                 fields : [ 'value' ]
33821             }),
33822             listeners : {
33823                 select : function (_self, record, index)
33824                 {
33825                     _this.setValue(_this.getValue());
33826                 }
33827             }
33828         });
33829
33830         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33831     },
33832     
33833     setValue : function(v, format)
33834     {
33835         this.inputEl.dom.value = v;
33836         
33837         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33838         
33839         var d = Date.parseDate(v, f);
33840         
33841         if(!d){
33842             this.validate();
33843             return;
33844         }
33845         
33846         this.setDay(d.format(this.dayFormat));
33847         this.setMonth(d.format(this.monthFormat));
33848         this.setYear(d.format(this.yearFormat));
33849         
33850         this.validate();
33851         
33852         return;
33853     },
33854     
33855     setDay : function(v)
33856     {
33857         this.dayField.setValue(v);
33858         this.inputEl.dom.value = this.getValue();
33859         this.validate();
33860         return;
33861     },
33862     
33863     setMonth : function(v)
33864     {
33865         this.monthField.setValue(v, true);
33866         this.inputEl.dom.value = this.getValue();
33867         this.validate();
33868         return;
33869     },
33870     
33871     setYear : function(v)
33872     {
33873         this.yearField.setValue(v);
33874         this.inputEl.dom.value = this.getValue();
33875         this.validate();
33876         return;
33877     },
33878     
33879     getDay : function()
33880     {
33881         return this.dayField.getValue();
33882     },
33883     
33884     getMonth : function()
33885     {
33886         return this.monthField.getValue();
33887     },
33888     
33889     getYear : function()
33890     {
33891         return this.yearField.getValue();
33892     },
33893     
33894     getValue : function()
33895     {
33896         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33897         
33898         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33899         
33900         return date;
33901     },
33902     
33903     reset : function()
33904     {
33905         this.setDay('');
33906         this.setMonth('');
33907         this.setYear('');
33908         this.inputEl.dom.value = '';
33909         this.validate();
33910         return;
33911     },
33912     
33913     validate : function()
33914     {
33915         var d = this.dayField.validate();
33916         var m = this.monthField.validate();
33917         var y = this.yearField.validate();
33918         
33919         var valid = true;
33920         
33921         if(
33922                 (!this.dayAllowBlank && !d) ||
33923                 (!this.monthAllowBlank && !m) ||
33924                 (!this.yearAllowBlank && !y)
33925         ){
33926             valid = false;
33927         }
33928         
33929         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33930             return valid;
33931         }
33932         
33933         if(valid){
33934             this.markValid();
33935             return valid;
33936         }
33937         
33938         this.markInvalid();
33939         
33940         return valid;
33941     },
33942     
33943     markValid : function()
33944     {
33945         
33946         var label = this.el.select('label', true).first();
33947         var icon = this.el.select('i.fa-star', true).first();
33948
33949         if(label && icon){
33950             icon.remove();
33951         }
33952         
33953         this.fireEvent('valid', this);
33954     },
33955     
33956      /**
33957      * Mark this field as invalid
33958      * @param {String} msg The validation message
33959      */
33960     markInvalid : function(msg)
33961     {
33962         
33963         var label = this.el.select('label', true).first();
33964         var icon = this.el.select('i.fa-star', true).first();
33965
33966         if(label && !icon){
33967             this.el.select('.roo-date-split-field-label', true).createChild({
33968                 tag : 'i',
33969                 cls : 'text-danger fa fa-lg fa-star',
33970                 tooltip : 'This field is required',
33971                 style : 'margin-right:5px;'
33972             }, label, true);
33973         }
33974         
33975         this.fireEvent('invalid', this, msg);
33976     },
33977     
33978     clearInvalid : function()
33979     {
33980         var label = this.el.select('label', true).first();
33981         var icon = this.el.select('i.fa-star', true).first();
33982
33983         if(label && icon){
33984             icon.remove();
33985         }
33986         
33987         this.fireEvent('valid', this);
33988     },
33989     
33990     getName: function()
33991     {
33992         return this.name;
33993     }
33994     
33995 });
33996
33997  
33998
33999 /**
34000  * @class Roo.bootstrap.LayoutMasonry
34001  * @extends Roo.bootstrap.Component
34002  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34003  * Bootstrap Layout Masonry class
34004  *
34005  * This is based on 
34006  * http://masonry.desandro.com
34007  *
34008  * The idea is to render all the bricks based on vertical width...
34009  *
34010  * The original code extends 'outlayer' - we might need to use that....
34011
34012  * @constructor
34013  * Create a new Element
34014  * @param {Object} config The config object
34015  */
34016
34017 Roo.bootstrap.LayoutMasonry = function(config){
34018     
34019     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34020     
34021     this.bricks = [];
34022     
34023     Roo.bootstrap.LayoutMasonry.register(this);
34024     
34025     this.addEvents({
34026         // raw events
34027         /**
34028          * @event layout
34029          * Fire after layout the items
34030          * @param {Roo.bootstrap.LayoutMasonry} this
34031          * @param {Roo.EventObject} e
34032          */
34033         "layout" : true
34034     });
34035     
34036 };
34037
34038 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34039     
34040     /**
34041      * @cfg {Boolean} isLayoutInstant = no animation?
34042      */   
34043     isLayoutInstant : false, // needed?
34044    
34045     /**
34046      * @cfg {Number} boxWidth  width of the columns
34047      */   
34048     boxWidth : 450,
34049     
34050       /**
34051      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34052      */   
34053     boxHeight : 0,
34054     
34055     /**
34056      * @cfg {Number} padWidth padding below box..
34057      */   
34058     padWidth : 10, 
34059     
34060     /**
34061      * @cfg {Number} gutter gutter width..
34062      */   
34063     gutter : 10,
34064     
34065      /**
34066      * @cfg {Number} maxCols maximum number of columns
34067      */   
34068     
34069     maxCols: 0,
34070     
34071     /**
34072      * @cfg {Boolean} isAutoInitial defalut true
34073      */   
34074     isAutoInitial : true, 
34075     
34076     containerWidth: 0,
34077     
34078     /**
34079      * @cfg {Boolean} isHorizontal defalut false
34080      */   
34081     isHorizontal : false, 
34082
34083     currentSize : null,
34084     
34085     tag: 'div',
34086     
34087     cls: '',
34088     
34089     bricks: null, //CompositeElement
34090     
34091     cols : 1,
34092     
34093     _isLayoutInited : false,
34094     
34095 //    isAlternative : false, // only use for vertical layout...
34096     
34097     /**
34098      * @cfg {Number} alternativePadWidth padding below box..
34099      */   
34100     alternativePadWidth : 50,
34101     
34102     selectedBrick : [],
34103     
34104     getAutoCreate : function(){
34105         
34106         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34107         
34108         var cfg = {
34109             tag: this.tag,
34110             cls: 'blog-masonary-wrapper ' + this.cls,
34111             cn : {
34112                 cls : 'mas-boxes masonary'
34113             }
34114         };
34115         
34116         return cfg;
34117     },
34118     
34119     getChildContainer: function( )
34120     {
34121         if (this.boxesEl) {
34122             return this.boxesEl;
34123         }
34124         
34125         this.boxesEl = this.el.select('.mas-boxes').first();
34126         
34127         return this.boxesEl;
34128     },
34129     
34130     
34131     initEvents : function()
34132     {
34133         var _this = this;
34134         
34135         if(this.isAutoInitial){
34136             Roo.log('hook children rendered');
34137             this.on('childrenrendered', function() {
34138                 Roo.log('children rendered');
34139                 _this.initial();
34140             } ,this);
34141         }
34142     },
34143     
34144     initial : function()
34145     {
34146         this.selectedBrick = [];
34147         
34148         this.currentSize = this.el.getBox(true);
34149         
34150         Roo.EventManager.onWindowResize(this.resize, this); 
34151
34152         if(!this.isAutoInitial){
34153             this.layout();
34154             return;
34155         }
34156         
34157         this.layout();
34158         
34159         return;
34160         //this.layout.defer(500,this);
34161         
34162     },
34163     
34164     resize : function()
34165     {
34166         var cs = this.el.getBox(true);
34167         
34168         if (
34169                 this.currentSize.width == cs.width && 
34170                 this.currentSize.x == cs.x && 
34171                 this.currentSize.height == cs.height && 
34172                 this.currentSize.y == cs.y 
34173         ) {
34174             Roo.log("no change in with or X or Y");
34175             return;
34176         }
34177         
34178         this.currentSize = cs;
34179         
34180         this.layout();
34181         
34182     },
34183     
34184     layout : function()
34185     {   
34186         this._resetLayout();
34187         
34188         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34189         
34190         this.layoutItems( isInstant );
34191       
34192         this._isLayoutInited = true;
34193         
34194         this.fireEvent('layout', this);
34195         
34196     },
34197     
34198     _resetLayout : function()
34199     {
34200         if(this.isHorizontal){
34201             this.horizontalMeasureColumns();
34202             return;
34203         }
34204         
34205         this.verticalMeasureColumns();
34206         
34207     },
34208     
34209     verticalMeasureColumns : function()
34210     {
34211         this.getContainerWidth();
34212         
34213 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34214 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34215 //            return;
34216 //        }
34217         
34218         var boxWidth = this.boxWidth + this.padWidth;
34219         
34220         if(this.containerWidth < this.boxWidth){
34221             boxWidth = this.containerWidth
34222         }
34223         
34224         var containerWidth = this.containerWidth;
34225         
34226         var cols = Math.floor(containerWidth / boxWidth);
34227         
34228         this.cols = Math.max( cols, 1 );
34229         
34230         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34231         
34232         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34233         
34234         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34235         
34236         this.colWidth = boxWidth + avail - this.padWidth;
34237         
34238         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34239         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34240     },
34241     
34242     horizontalMeasureColumns : function()
34243     {
34244         this.getContainerWidth();
34245         
34246         var boxWidth = this.boxWidth;
34247         
34248         if(this.containerWidth < boxWidth){
34249             boxWidth = this.containerWidth;
34250         }
34251         
34252         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34253         
34254         this.el.setHeight(boxWidth);
34255         
34256     },
34257     
34258     getContainerWidth : function()
34259     {
34260         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34261     },
34262     
34263     layoutItems : function( isInstant )
34264     {
34265         Roo.log(this.bricks);
34266         
34267         var items = Roo.apply([], this.bricks);
34268         
34269         if(this.isHorizontal){
34270             this._horizontalLayoutItems( items , isInstant );
34271             return;
34272         }
34273         
34274 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34275 //            this._verticalAlternativeLayoutItems( items , isInstant );
34276 //            return;
34277 //        }
34278         
34279         this._verticalLayoutItems( items , isInstant );
34280         
34281     },
34282     
34283     _verticalLayoutItems : function ( items , isInstant)
34284     {
34285         if ( !items || !items.length ) {
34286             return;
34287         }
34288         
34289         var standard = [
34290             ['xs', 'xs', 'xs', 'tall'],
34291             ['xs', 'xs', 'tall'],
34292             ['xs', 'xs', 'sm'],
34293             ['xs', 'xs', 'xs'],
34294             ['xs', 'tall'],
34295             ['xs', 'sm'],
34296             ['xs', 'xs'],
34297             ['xs'],
34298             
34299             ['sm', 'xs', 'xs'],
34300             ['sm', 'xs'],
34301             ['sm'],
34302             
34303             ['tall', 'xs', 'xs', 'xs'],
34304             ['tall', 'xs', 'xs'],
34305             ['tall', 'xs'],
34306             ['tall']
34307             
34308         ];
34309         
34310         var queue = [];
34311         
34312         var boxes = [];
34313         
34314         var box = [];
34315         
34316         Roo.each(items, function(item, k){
34317             
34318             switch (item.size) {
34319                 // these layouts take up a full box,
34320                 case 'md' :
34321                 case 'md-left' :
34322                 case 'md-right' :
34323                 case 'wide' :
34324                     
34325                     if(box.length){
34326                         boxes.push(box);
34327                         box = [];
34328                     }
34329                     
34330                     boxes.push([item]);
34331                     
34332                     break;
34333                     
34334                 case 'xs' :
34335                 case 'sm' :
34336                 case 'tall' :
34337                     
34338                     box.push(item);
34339                     
34340                     break;
34341                 default :
34342                     break;
34343                     
34344             }
34345             
34346         }, this);
34347         
34348         if(box.length){
34349             boxes.push(box);
34350             box = [];
34351         }
34352         
34353         var filterPattern = function(box, length)
34354         {
34355             if(!box.length){
34356                 return;
34357             }
34358             
34359             var match = false;
34360             
34361             var pattern = box.slice(0, length);
34362             
34363             var format = [];
34364             
34365             Roo.each(pattern, function(i){
34366                 format.push(i.size);
34367             }, this);
34368             
34369             Roo.each(standard, function(s){
34370                 
34371                 if(String(s) != String(format)){
34372                     return;
34373                 }
34374                 
34375                 match = true;
34376                 return false;
34377                 
34378             }, this);
34379             
34380             if(!match && length == 1){
34381                 return;
34382             }
34383             
34384             if(!match){
34385                 filterPattern(box, length - 1);
34386                 return;
34387             }
34388                 
34389             queue.push(pattern);
34390
34391             box = box.slice(length, box.length);
34392
34393             filterPattern(box, 4);
34394
34395             return;
34396             
34397         }
34398         
34399         Roo.each(boxes, function(box, k){
34400             
34401             if(!box.length){
34402                 return;
34403             }
34404             
34405             if(box.length == 1){
34406                 queue.push(box);
34407                 return;
34408             }
34409             
34410             filterPattern(box, 4);
34411             
34412         }, this);
34413         
34414         this._processVerticalLayoutQueue( queue, isInstant );
34415         
34416     },
34417     
34418 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34419 //    {
34420 //        if ( !items || !items.length ) {
34421 //            return;
34422 //        }
34423 //
34424 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34425 //        
34426 //    },
34427     
34428     _horizontalLayoutItems : function ( items , isInstant)
34429     {
34430         if ( !items || !items.length || items.length < 3) {
34431             return;
34432         }
34433         
34434         items.reverse();
34435         
34436         var eItems = items.slice(0, 3);
34437         
34438         items = items.slice(3, items.length);
34439         
34440         var standard = [
34441             ['xs', 'xs', 'xs', 'wide'],
34442             ['xs', 'xs', 'wide'],
34443             ['xs', 'xs', 'sm'],
34444             ['xs', 'xs', 'xs'],
34445             ['xs', 'wide'],
34446             ['xs', 'sm'],
34447             ['xs', 'xs'],
34448             ['xs'],
34449             
34450             ['sm', 'xs', 'xs'],
34451             ['sm', 'xs'],
34452             ['sm'],
34453             
34454             ['wide', 'xs', 'xs', 'xs'],
34455             ['wide', 'xs', 'xs'],
34456             ['wide', 'xs'],
34457             ['wide'],
34458             
34459             ['wide-thin']
34460         ];
34461         
34462         var queue = [];
34463         
34464         var boxes = [];
34465         
34466         var box = [];
34467         
34468         Roo.each(items, function(item, k){
34469             
34470             switch (item.size) {
34471                 case 'md' :
34472                 case 'md-left' :
34473                 case 'md-right' :
34474                 case 'tall' :
34475                     
34476                     if(box.length){
34477                         boxes.push(box);
34478                         box = [];
34479                     }
34480                     
34481                     boxes.push([item]);
34482                     
34483                     break;
34484                     
34485                 case 'xs' :
34486                 case 'sm' :
34487                 case 'wide' :
34488                 case 'wide-thin' :
34489                     
34490                     box.push(item);
34491                     
34492                     break;
34493                 default :
34494                     break;
34495                     
34496             }
34497             
34498         }, this);
34499         
34500         if(box.length){
34501             boxes.push(box);
34502             box = [];
34503         }
34504         
34505         var filterPattern = function(box, length)
34506         {
34507             if(!box.length){
34508                 return;
34509             }
34510             
34511             var match = false;
34512             
34513             var pattern = box.slice(0, length);
34514             
34515             var format = [];
34516             
34517             Roo.each(pattern, function(i){
34518                 format.push(i.size);
34519             }, this);
34520             
34521             Roo.each(standard, function(s){
34522                 
34523                 if(String(s) != String(format)){
34524                     return;
34525                 }
34526                 
34527                 match = true;
34528                 return false;
34529                 
34530             }, this);
34531             
34532             if(!match && length == 1){
34533                 return;
34534             }
34535             
34536             if(!match){
34537                 filterPattern(box, length - 1);
34538                 return;
34539             }
34540                 
34541             queue.push(pattern);
34542
34543             box = box.slice(length, box.length);
34544
34545             filterPattern(box, 4);
34546
34547             return;
34548             
34549         }
34550         
34551         Roo.each(boxes, function(box, k){
34552             
34553             if(!box.length){
34554                 return;
34555             }
34556             
34557             if(box.length == 1){
34558                 queue.push(box);
34559                 return;
34560             }
34561             
34562             filterPattern(box, 4);
34563             
34564         }, this);
34565         
34566         
34567         var prune = [];
34568         
34569         var pos = this.el.getBox(true);
34570         
34571         var minX = pos.x;
34572         
34573         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34574         
34575         var hit_end = false;
34576         
34577         Roo.each(queue, function(box){
34578             
34579             if(hit_end){
34580                 
34581                 Roo.each(box, function(b){
34582                 
34583                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34584                     b.el.hide();
34585
34586                 }, this);
34587
34588                 return;
34589             }
34590             
34591             var mx = 0;
34592             
34593             Roo.each(box, function(b){
34594                 
34595                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34596                 b.el.show();
34597
34598                 mx = Math.max(mx, b.x);
34599                 
34600             }, this);
34601             
34602             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34603             
34604             if(maxX < minX){
34605                 
34606                 Roo.each(box, function(b){
34607                 
34608                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34609                     b.el.hide();
34610                     
34611                 }, this);
34612                 
34613                 hit_end = true;
34614                 
34615                 return;
34616             }
34617             
34618             prune.push(box);
34619             
34620         }, this);
34621         
34622         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34623     },
34624     
34625     /** Sets position of item in DOM
34626     * @param {Element} item
34627     * @param {Number} x - horizontal position
34628     * @param {Number} y - vertical position
34629     * @param {Boolean} isInstant - disables transitions
34630     */
34631     _processVerticalLayoutQueue : function( queue, isInstant )
34632     {
34633         var pos = this.el.getBox(true);
34634         var x = pos.x;
34635         var y = pos.y;
34636         var maxY = [];
34637         
34638         for (var i = 0; i < this.cols; i++){
34639             maxY[i] = pos.y;
34640         }
34641         
34642         Roo.each(queue, function(box, k){
34643             
34644             var col = k % this.cols;
34645             
34646             Roo.each(box, function(b,kk){
34647                 
34648                 b.el.position('absolute');
34649                 
34650                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34651                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34652                 
34653                 if(b.size == 'md-left' || b.size == 'md-right'){
34654                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34655                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34656                 }
34657                 
34658                 b.el.setWidth(width);
34659                 b.el.setHeight(height);
34660                 // iframe?
34661                 b.el.select('iframe',true).setSize(width,height);
34662                 
34663             }, this);
34664             
34665             for (var i = 0; i < this.cols; i++){
34666                 
34667                 if(maxY[i] < maxY[col]){
34668                     col = i;
34669                     continue;
34670                 }
34671                 
34672                 col = Math.min(col, i);
34673                 
34674             }
34675             
34676             x = pos.x + col * (this.colWidth + this.padWidth);
34677             
34678             y = maxY[col];
34679             
34680             var positions = [];
34681             
34682             switch (box.length){
34683                 case 1 :
34684                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34685                     break;
34686                 case 2 :
34687                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34688                     break;
34689                 case 3 :
34690                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34691                     break;
34692                 case 4 :
34693                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34694                     break;
34695                 default :
34696                     break;
34697             }
34698             
34699             Roo.each(box, function(b,kk){
34700                 
34701                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34702                 
34703                 var sz = b.el.getSize();
34704                 
34705                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34706                 
34707             }, this);
34708             
34709         }, this);
34710         
34711         var mY = 0;
34712         
34713         for (var i = 0; i < this.cols; i++){
34714             mY = Math.max(mY, maxY[i]);
34715         }
34716         
34717         this.el.setHeight(mY - pos.y);
34718         
34719     },
34720     
34721 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34722 //    {
34723 //        var pos = this.el.getBox(true);
34724 //        var x = pos.x;
34725 //        var y = pos.y;
34726 //        var maxX = pos.right;
34727 //        
34728 //        var maxHeight = 0;
34729 //        
34730 //        Roo.each(items, function(item, k){
34731 //            
34732 //            var c = k % 2;
34733 //            
34734 //            item.el.position('absolute');
34735 //                
34736 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34737 //
34738 //            item.el.setWidth(width);
34739 //
34740 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34741 //
34742 //            item.el.setHeight(height);
34743 //            
34744 //            if(c == 0){
34745 //                item.el.setXY([x, y], isInstant ? false : true);
34746 //            } else {
34747 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34748 //            }
34749 //            
34750 //            y = y + height + this.alternativePadWidth;
34751 //            
34752 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34753 //            
34754 //        }, this);
34755 //        
34756 //        this.el.setHeight(maxHeight);
34757 //        
34758 //    },
34759     
34760     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34761     {
34762         var pos = this.el.getBox(true);
34763         
34764         var minX = pos.x;
34765         var minY = pos.y;
34766         
34767         var maxX = pos.right;
34768         
34769         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34770         
34771         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34772         
34773         Roo.each(queue, function(box, k){
34774             
34775             Roo.each(box, function(b, kk){
34776                 
34777                 b.el.position('absolute');
34778                 
34779                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34780                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34781                 
34782                 if(b.size == 'md-left' || b.size == 'md-right'){
34783                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34784                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34785                 }
34786                 
34787                 b.el.setWidth(width);
34788                 b.el.setHeight(height);
34789                 
34790             }, this);
34791             
34792             if(!box.length){
34793                 return;
34794             }
34795             
34796             var positions = [];
34797             
34798             switch (box.length){
34799                 case 1 :
34800                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34801                     break;
34802                 case 2 :
34803                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34804                     break;
34805                 case 3 :
34806                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34807                     break;
34808                 case 4 :
34809                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34810                     break;
34811                 default :
34812                     break;
34813             }
34814             
34815             Roo.each(box, function(b,kk){
34816                 
34817                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34818                 
34819                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34820                 
34821             }, this);
34822             
34823         }, this);
34824         
34825     },
34826     
34827     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34828     {
34829         Roo.each(eItems, function(b,k){
34830             
34831             b.size = (k == 0) ? 'sm' : 'xs';
34832             b.x = (k == 0) ? 2 : 1;
34833             b.y = (k == 0) ? 2 : 1;
34834             
34835             b.el.position('absolute');
34836             
34837             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34838                 
34839             b.el.setWidth(width);
34840             
34841             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34842             
34843             b.el.setHeight(height);
34844             
34845         }, this);
34846
34847         var positions = [];
34848         
34849         positions.push({
34850             x : maxX - this.unitWidth * 2 - this.gutter,
34851             y : minY
34852         });
34853         
34854         positions.push({
34855             x : maxX - this.unitWidth,
34856             y : minY + (this.unitWidth + this.gutter) * 2
34857         });
34858         
34859         positions.push({
34860             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34861             y : minY
34862         });
34863         
34864         Roo.each(eItems, function(b,k){
34865             
34866             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34867
34868         }, this);
34869         
34870     },
34871     
34872     getVerticalOneBoxColPositions : function(x, y, box)
34873     {
34874         var pos = [];
34875         
34876         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34877         
34878         if(box[0].size == 'md-left'){
34879             rand = 0;
34880         }
34881         
34882         if(box[0].size == 'md-right'){
34883             rand = 1;
34884         }
34885         
34886         pos.push({
34887             x : x + (this.unitWidth + this.gutter) * rand,
34888             y : y
34889         });
34890         
34891         return pos;
34892     },
34893     
34894     getVerticalTwoBoxColPositions : function(x, y, box)
34895     {
34896         var pos = [];
34897         
34898         if(box[0].size == 'xs'){
34899             
34900             pos.push({
34901                 x : x,
34902                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34903             });
34904
34905             pos.push({
34906                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34907                 y : y
34908             });
34909             
34910             return pos;
34911             
34912         }
34913         
34914         pos.push({
34915             x : x,
34916             y : y
34917         });
34918
34919         pos.push({
34920             x : x + (this.unitWidth + this.gutter) * 2,
34921             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34922         });
34923         
34924         return pos;
34925         
34926     },
34927     
34928     getVerticalThreeBoxColPositions : function(x, y, box)
34929     {
34930         var pos = [];
34931         
34932         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34933             
34934             pos.push({
34935                 x : x,
34936                 y : y
34937             });
34938
34939             pos.push({
34940                 x : x + (this.unitWidth + this.gutter) * 1,
34941                 y : y
34942             });
34943             
34944             pos.push({
34945                 x : x + (this.unitWidth + this.gutter) * 2,
34946                 y : y
34947             });
34948             
34949             return pos;
34950             
34951         }
34952         
34953         if(box[0].size == 'xs' && box[1].size == 'xs'){
34954             
34955             pos.push({
34956                 x : x,
34957                 y : y
34958             });
34959
34960             pos.push({
34961                 x : x,
34962                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34963             });
34964             
34965             pos.push({
34966                 x : x + (this.unitWidth + this.gutter) * 1,
34967                 y : y
34968             });
34969             
34970             return pos;
34971             
34972         }
34973         
34974         pos.push({
34975             x : x,
34976             y : y
34977         });
34978
34979         pos.push({
34980             x : x + (this.unitWidth + this.gutter) * 2,
34981             y : y
34982         });
34983
34984         pos.push({
34985             x : x + (this.unitWidth + this.gutter) * 2,
34986             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34987         });
34988             
34989         return pos;
34990         
34991     },
34992     
34993     getVerticalFourBoxColPositions : function(x, y, box)
34994     {
34995         var pos = [];
34996         
34997         if(box[0].size == 'xs'){
34998             
34999             pos.push({
35000                 x : x,
35001                 y : y
35002             });
35003
35004             pos.push({
35005                 x : x,
35006                 y : y + (this.unitHeight + this.gutter) * 1
35007             });
35008             
35009             pos.push({
35010                 x : x,
35011                 y : y + (this.unitHeight + this.gutter) * 2
35012             });
35013             
35014             pos.push({
35015                 x : x + (this.unitWidth + this.gutter) * 1,
35016                 y : y
35017             });
35018             
35019             return pos;
35020             
35021         }
35022         
35023         pos.push({
35024             x : x,
35025             y : y
35026         });
35027
35028         pos.push({
35029             x : x + (this.unitWidth + this.gutter) * 2,
35030             y : y
35031         });
35032
35033         pos.push({
35034             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35035             y : y + (this.unitHeight + this.gutter) * 1
35036         });
35037
35038         pos.push({
35039             x : x + (this.unitWidth + this.gutter) * 2,
35040             y : y + (this.unitWidth + this.gutter) * 2
35041         });
35042
35043         return pos;
35044         
35045     },
35046     
35047     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35048     {
35049         var pos = [];
35050         
35051         if(box[0].size == 'md-left'){
35052             pos.push({
35053                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35054                 y : minY
35055             });
35056             
35057             return pos;
35058         }
35059         
35060         if(box[0].size == 'md-right'){
35061             pos.push({
35062                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35063                 y : minY + (this.unitWidth + this.gutter) * 1
35064             });
35065             
35066             return pos;
35067         }
35068         
35069         var rand = Math.floor(Math.random() * (4 - box[0].y));
35070         
35071         pos.push({
35072             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35073             y : minY + (this.unitWidth + this.gutter) * rand
35074         });
35075         
35076         return pos;
35077         
35078     },
35079     
35080     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35081     {
35082         var pos = [];
35083         
35084         if(box[0].size == 'xs'){
35085             
35086             pos.push({
35087                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35088                 y : minY
35089             });
35090
35091             pos.push({
35092                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35094             });
35095             
35096             return pos;
35097             
35098         }
35099         
35100         pos.push({
35101             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35102             y : minY
35103         });
35104
35105         pos.push({
35106             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35107             y : minY + (this.unitWidth + this.gutter) * 2
35108         });
35109         
35110         return pos;
35111         
35112     },
35113     
35114     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35115     {
35116         var pos = [];
35117         
35118         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35119             
35120             pos.push({
35121                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35122                 y : minY
35123             });
35124
35125             pos.push({
35126                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35127                 y : minY + (this.unitWidth + this.gutter) * 1
35128             });
35129             
35130             pos.push({
35131                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35132                 y : minY + (this.unitWidth + this.gutter) * 2
35133             });
35134             
35135             return pos;
35136             
35137         }
35138         
35139         if(box[0].size == 'xs' && box[1].size == 'xs'){
35140             
35141             pos.push({
35142                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35143                 y : minY
35144             });
35145
35146             pos.push({
35147                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35148                 y : minY
35149             });
35150             
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35153                 y : minY + (this.unitWidth + this.gutter) * 1
35154             });
35155             
35156             return pos;
35157             
35158         }
35159         
35160         pos.push({
35161             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35162             y : minY
35163         });
35164
35165         pos.push({
35166             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35167             y : minY + (this.unitWidth + this.gutter) * 2
35168         });
35169
35170         pos.push({
35171             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35172             y : minY + (this.unitWidth + this.gutter) * 2
35173         });
35174             
35175         return pos;
35176         
35177     },
35178     
35179     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35180     {
35181         var pos = [];
35182         
35183         if(box[0].size == 'xs'){
35184             
35185             pos.push({
35186                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35187                 y : minY
35188             });
35189
35190             pos.push({
35191                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35192                 y : minY
35193             });
35194             
35195             pos.push({
35196                 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),
35197                 y : minY
35198             });
35199             
35200             pos.push({
35201                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35202                 y : minY + (this.unitWidth + this.gutter) * 1
35203             });
35204             
35205             return pos;
35206             
35207         }
35208         
35209         pos.push({
35210             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35211             y : minY
35212         });
35213         
35214         pos.push({
35215             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35216             y : minY + (this.unitWidth + this.gutter) * 2
35217         });
35218         
35219         pos.push({
35220             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35221             y : minY + (this.unitWidth + this.gutter) * 2
35222         });
35223         
35224         pos.push({
35225             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),
35226             y : minY + (this.unitWidth + this.gutter) * 2
35227         });
35228
35229         return pos;
35230         
35231     },
35232     
35233     /**
35234     * remove a Masonry Brick
35235     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35236     */
35237     removeBrick : function(brick_id)
35238     {
35239         if (!brick_id) {
35240             return;
35241         }
35242         
35243         for (var i = 0; i<this.bricks.length; i++) {
35244             if (this.bricks[i].id == brick_id) {
35245                 this.bricks.splice(i,1);
35246                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35247                 this.initial();
35248             }
35249         }
35250     },
35251     
35252     /**
35253     * adds a Masonry Brick
35254     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35255     */
35256     addBrick : function(cfg)
35257     {
35258         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35259         //this.register(cn);
35260         cn.parentId = this.id;
35261         cn.render(this.el);
35262         return cn;
35263     },
35264     
35265     /**
35266     * register a Masonry Brick
35267     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35268     */
35269     
35270     register : function(brick)
35271     {
35272         this.bricks.push(brick);
35273         brick.masonryId = this.id;
35274     },
35275     
35276     /**
35277     * clear all the Masonry Brick
35278     */
35279     clearAll : function()
35280     {
35281         this.bricks = [];
35282         //this.getChildContainer().dom.innerHTML = "";
35283         this.el.dom.innerHTML = '';
35284     },
35285     
35286     getSelected : function()
35287     {
35288         if (!this.selectedBrick) {
35289             return false;
35290         }
35291         
35292         return this.selectedBrick;
35293     }
35294 });
35295
35296 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35297     
35298     groups: {},
35299      /**
35300     * register a Masonry Layout
35301     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35302     */
35303     
35304     register : function(layout)
35305     {
35306         this.groups[layout.id] = layout;
35307     },
35308     /**
35309     * fetch a  Masonry Layout based on the masonry layout ID
35310     * @param {string} the masonry layout to add
35311     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35312     */
35313     
35314     get: function(layout_id) {
35315         if (typeof(this.groups[layout_id]) == 'undefined') {
35316             return false;
35317         }
35318         return this.groups[layout_id] ;
35319     }
35320     
35321     
35322     
35323 });
35324
35325  
35326
35327  /**
35328  *
35329  * This is based on 
35330  * http://masonry.desandro.com
35331  *
35332  * The idea is to render all the bricks based on vertical width...
35333  *
35334  * The original code extends 'outlayer' - we might need to use that....
35335  * 
35336  */
35337
35338
35339 /**
35340  * @class Roo.bootstrap.LayoutMasonryAuto
35341  * @extends Roo.bootstrap.Component
35342  * Bootstrap Layout Masonry class
35343  * 
35344  * @constructor
35345  * Create a new Element
35346  * @param {Object} config The config object
35347  */
35348
35349 Roo.bootstrap.LayoutMasonryAuto = function(config){
35350     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35351 };
35352
35353 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35354     
35355       /**
35356      * @cfg {Boolean} isFitWidth  - resize the width..
35357      */   
35358     isFitWidth : false,  // options..
35359     /**
35360      * @cfg {Boolean} isOriginLeft = left align?
35361      */   
35362     isOriginLeft : true,
35363     /**
35364      * @cfg {Boolean} isOriginTop = top align?
35365      */   
35366     isOriginTop : false,
35367     /**
35368      * @cfg {Boolean} isLayoutInstant = no animation?
35369      */   
35370     isLayoutInstant : false, // needed?
35371     /**
35372      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35373      */   
35374     isResizingContainer : true,
35375     /**
35376      * @cfg {Number} columnWidth  width of the columns 
35377      */   
35378     
35379     columnWidth : 0,
35380     
35381     /**
35382      * @cfg {Number} maxCols maximum number of columns
35383      */   
35384     
35385     maxCols: 0,
35386     /**
35387      * @cfg {Number} padHeight padding below box..
35388      */   
35389     
35390     padHeight : 10, 
35391     
35392     /**
35393      * @cfg {Boolean} isAutoInitial defalut true
35394      */   
35395     
35396     isAutoInitial : true, 
35397     
35398     // private?
35399     gutter : 0,
35400     
35401     containerWidth: 0,
35402     initialColumnWidth : 0,
35403     currentSize : null,
35404     
35405     colYs : null, // array.
35406     maxY : 0,
35407     padWidth: 10,
35408     
35409     
35410     tag: 'div',
35411     cls: '',
35412     bricks: null, //CompositeElement
35413     cols : 0, // array?
35414     // element : null, // wrapped now this.el
35415     _isLayoutInited : null, 
35416     
35417     
35418     getAutoCreate : function(){
35419         
35420         var cfg = {
35421             tag: this.tag,
35422             cls: 'blog-masonary-wrapper ' + this.cls,
35423             cn : {
35424                 cls : 'mas-boxes masonary'
35425             }
35426         };
35427         
35428         return cfg;
35429     },
35430     
35431     getChildContainer: function( )
35432     {
35433         if (this.boxesEl) {
35434             return this.boxesEl;
35435         }
35436         
35437         this.boxesEl = this.el.select('.mas-boxes').first();
35438         
35439         return this.boxesEl;
35440     },
35441     
35442     
35443     initEvents : function()
35444     {
35445         var _this = this;
35446         
35447         if(this.isAutoInitial){
35448             Roo.log('hook children rendered');
35449             this.on('childrenrendered', function() {
35450                 Roo.log('children rendered');
35451                 _this.initial();
35452             } ,this);
35453         }
35454         
35455     },
35456     
35457     initial : function()
35458     {
35459         this.reloadItems();
35460
35461         this.currentSize = this.el.getBox(true);
35462
35463         /// was window resize... - let's see if this works..
35464         Roo.EventManager.onWindowResize(this.resize, this); 
35465
35466         if(!this.isAutoInitial){
35467             this.layout();
35468             return;
35469         }
35470         
35471         this.layout.defer(500,this);
35472     },
35473     
35474     reloadItems: function()
35475     {
35476         this.bricks = this.el.select('.masonry-brick', true);
35477         
35478         this.bricks.each(function(b) {
35479             //Roo.log(b.getSize());
35480             if (!b.attr('originalwidth')) {
35481                 b.attr('originalwidth',  b.getSize().width);
35482             }
35483             
35484         });
35485         
35486         Roo.log(this.bricks.elements.length);
35487     },
35488     
35489     resize : function()
35490     {
35491         Roo.log('resize');
35492         var cs = this.el.getBox(true);
35493         
35494         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35495             Roo.log("no change in with or X");
35496             return;
35497         }
35498         this.currentSize = cs;
35499         this.layout();
35500     },
35501     
35502     layout : function()
35503     {
35504          Roo.log('layout');
35505         this._resetLayout();
35506         //this._manageStamps();
35507       
35508         // don't animate first layout
35509         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35510         this.layoutItems( isInstant );
35511       
35512         // flag for initalized
35513         this._isLayoutInited = true;
35514     },
35515     
35516     layoutItems : function( isInstant )
35517     {
35518         //var items = this._getItemsForLayout( this.items );
35519         // original code supports filtering layout items.. we just ignore it..
35520         
35521         this._layoutItems( this.bricks , isInstant );
35522       
35523         this._postLayout();
35524     },
35525     _layoutItems : function ( items , isInstant)
35526     {
35527        //this.fireEvent( 'layout', this, items );
35528     
35529
35530         if ( !items || !items.elements.length ) {
35531           // no items, emit event with empty array
35532             return;
35533         }
35534
35535         var queue = [];
35536         items.each(function(item) {
35537             Roo.log("layout item");
35538             Roo.log(item);
35539             // get x/y object from method
35540             var position = this._getItemLayoutPosition( item );
35541             // enqueue
35542             position.item = item;
35543             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35544             queue.push( position );
35545         }, this);
35546       
35547         this._processLayoutQueue( queue );
35548     },
35549     /** Sets position of item in DOM
35550     * @param {Element} item
35551     * @param {Number} x - horizontal position
35552     * @param {Number} y - vertical position
35553     * @param {Boolean} isInstant - disables transitions
35554     */
35555     _processLayoutQueue : function( queue )
35556     {
35557         for ( var i=0, len = queue.length; i < len; i++ ) {
35558             var obj = queue[i];
35559             obj.item.position('absolute');
35560             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35561         }
35562     },
35563       
35564     
35565     /**
35566     * Any logic you want to do after each layout,
35567     * i.e. size the container
35568     */
35569     _postLayout : function()
35570     {
35571         this.resizeContainer();
35572     },
35573     
35574     resizeContainer : function()
35575     {
35576         if ( !this.isResizingContainer ) {
35577             return;
35578         }
35579         var size = this._getContainerSize();
35580         if ( size ) {
35581             this.el.setSize(size.width,size.height);
35582             this.boxesEl.setSize(size.width,size.height);
35583         }
35584     },
35585     
35586     
35587     
35588     _resetLayout : function()
35589     {
35590         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35591         this.colWidth = this.el.getWidth();
35592         //this.gutter = this.el.getWidth(); 
35593         
35594         this.measureColumns();
35595
35596         // reset column Y
35597         var i = this.cols;
35598         this.colYs = [];
35599         while (i--) {
35600             this.colYs.push( 0 );
35601         }
35602     
35603         this.maxY = 0;
35604     },
35605
35606     measureColumns : function()
35607     {
35608         this.getContainerWidth();
35609       // if columnWidth is 0, default to outerWidth of first item
35610         if ( !this.columnWidth ) {
35611             var firstItem = this.bricks.first();
35612             Roo.log(firstItem);
35613             this.columnWidth  = this.containerWidth;
35614             if (firstItem && firstItem.attr('originalwidth') ) {
35615                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35616             }
35617             // columnWidth fall back to item of first element
35618             Roo.log("set column width?");
35619                         this.initialColumnWidth = this.columnWidth  ;
35620
35621             // if first elem has no width, default to size of container
35622             
35623         }
35624         
35625         
35626         if (this.initialColumnWidth) {
35627             this.columnWidth = this.initialColumnWidth;
35628         }
35629         
35630         
35631             
35632         // column width is fixed at the top - however if container width get's smaller we should
35633         // reduce it...
35634         
35635         // this bit calcs how man columns..
35636             
35637         var columnWidth = this.columnWidth += this.gutter;
35638       
35639         // calculate columns
35640         var containerWidth = this.containerWidth + this.gutter;
35641         
35642         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35643         // fix rounding errors, typically with gutters
35644         var excess = columnWidth - containerWidth % columnWidth;
35645         
35646         
35647         // if overshoot is less than a pixel, round up, otherwise floor it
35648         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35649         cols = Math[ mathMethod ]( cols );
35650         this.cols = Math.max( cols, 1 );
35651         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35652         
35653          // padding positioning..
35654         var totalColWidth = this.cols * this.columnWidth;
35655         var padavail = this.containerWidth - totalColWidth;
35656         // so for 2 columns - we need 3 'pads'
35657         
35658         var padNeeded = (1+this.cols) * this.padWidth;
35659         
35660         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35661         
35662         this.columnWidth += padExtra
35663         //this.padWidth = Math.floor(padavail /  ( this.cols));
35664         
35665         // adjust colum width so that padding is fixed??
35666         
35667         // we have 3 columns ... total = width * 3
35668         // we have X left over... that should be used by 
35669         
35670         //if (this.expandC) {
35671             
35672         //}
35673         
35674         
35675         
35676     },
35677     
35678     getContainerWidth : function()
35679     {
35680        /* // container is parent if fit width
35681         var container = this.isFitWidth ? this.element.parentNode : this.element;
35682         // check that this.size and size are there
35683         // IE8 triggers resize on body size change, so they might not be
35684         
35685         var size = getSize( container );  //FIXME
35686         this.containerWidth = size && size.innerWidth; //FIXME
35687         */
35688          
35689         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35690         
35691     },
35692     
35693     _getItemLayoutPosition : function( item )  // what is item?
35694     {
35695         // we resize the item to our columnWidth..
35696       
35697         item.setWidth(this.columnWidth);
35698         item.autoBoxAdjust  = false;
35699         
35700         var sz = item.getSize();
35701  
35702         // how many columns does this brick span
35703         var remainder = this.containerWidth % this.columnWidth;
35704         
35705         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35706         // round if off by 1 pixel, otherwise use ceil
35707         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35708         colSpan = Math.min( colSpan, this.cols );
35709         
35710         // normally this should be '1' as we dont' currently allow multi width columns..
35711         
35712         var colGroup = this._getColGroup( colSpan );
35713         // get the minimum Y value from the columns
35714         var minimumY = Math.min.apply( Math, colGroup );
35715         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35716         
35717         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35718          
35719         // position the brick
35720         var position = {
35721             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35722             y: this.currentSize.y + minimumY + this.padHeight
35723         };
35724         
35725         Roo.log(position);
35726         // apply setHeight to necessary columns
35727         var setHeight = minimumY + sz.height + this.padHeight;
35728         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35729         
35730         var setSpan = this.cols + 1 - colGroup.length;
35731         for ( var i = 0; i < setSpan; i++ ) {
35732           this.colYs[ shortColIndex + i ] = setHeight ;
35733         }
35734       
35735         return position;
35736     },
35737     
35738     /**
35739      * @param {Number} colSpan - number of columns the element spans
35740      * @returns {Array} colGroup
35741      */
35742     _getColGroup : function( colSpan )
35743     {
35744         if ( colSpan < 2 ) {
35745           // if brick spans only one column, use all the column Ys
35746           return this.colYs;
35747         }
35748       
35749         var colGroup = [];
35750         // how many different places could this brick fit horizontally
35751         var groupCount = this.cols + 1 - colSpan;
35752         // for each group potential horizontal position
35753         for ( var i = 0; i < groupCount; i++ ) {
35754           // make an array of colY values for that one group
35755           var groupColYs = this.colYs.slice( i, i + colSpan );
35756           // and get the max value of the array
35757           colGroup[i] = Math.max.apply( Math, groupColYs );
35758         }
35759         return colGroup;
35760     },
35761     /*
35762     _manageStamp : function( stamp )
35763     {
35764         var stampSize =  stamp.getSize();
35765         var offset = stamp.getBox();
35766         // get the columns that this stamp affects
35767         var firstX = this.isOriginLeft ? offset.x : offset.right;
35768         var lastX = firstX + stampSize.width;
35769         var firstCol = Math.floor( firstX / this.columnWidth );
35770         firstCol = Math.max( 0, firstCol );
35771         
35772         var lastCol = Math.floor( lastX / this.columnWidth );
35773         // lastCol should not go over if multiple of columnWidth #425
35774         lastCol -= lastX % this.columnWidth ? 0 : 1;
35775         lastCol = Math.min( this.cols - 1, lastCol );
35776         
35777         // set colYs to bottom of the stamp
35778         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35779             stampSize.height;
35780             
35781         for ( var i = firstCol; i <= lastCol; i++ ) {
35782           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35783         }
35784     },
35785     */
35786     
35787     _getContainerSize : function()
35788     {
35789         this.maxY = Math.max.apply( Math, this.colYs );
35790         var size = {
35791             height: this.maxY
35792         };
35793       
35794         if ( this.isFitWidth ) {
35795             size.width = this._getContainerFitWidth();
35796         }
35797       
35798         return size;
35799     },
35800     
35801     _getContainerFitWidth : function()
35802     {
35803         var unusedCols = 0;
35804         // count unused columns
35805         var i = this.cols;
35806         while ( --i ) {
35807           if ( this.colYs[i] !== 0 ) {
35808             break;
35809           }
35810           unusedCols++;
35811         }
35812         // fit container to columns that have been used
35813         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35814     },
35815     
35816     needsResizeLayout : function()
35817     {
35818         var previousWidth = this.containerWidth;
35819         this.getContainerWidth();
35820         return previousWidth !== this.containerWidth;
35821     }
35822  
35823 });
35824
35825  
35826
35827  /*
35828  * - LGPL
35829  *
35830  * element
35831  * 
35832  */
35833
35834 /**
35835  * @class Roo.bootstrap.MasonryBrick
35836  * @extends Roo.bootstrap.Component
35837  * Bootstrap MasonryBrick class
35838  * 
35839  * @constructor
35840  * Create a new MasonryBrick
35841  * @param {Object} config The config object
35842  */
35843
35844 Roo.bootstrap.MasonryBrick = function(config){
35845     
35846     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35847     
35848     Roo.bootstrap.MasonryBrick.register(this);
35849     
35850     this.addEvents({
35851         // raw events
35852         /**
35853          * @event click
35854          * When a MasonryBrick is clcik
35855          * @param {Roo.bootstrap.MasonryBrick} this
35856          * @param {Roo.EventObject} e
35857          */
35858         "click" : true
35859     });
35860 };
35861
35862 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35863     
35864     /**
35865      * @cfg {String} title
35866      */   
35867     title : '',
35868     /**
35869      * @cfg {String} html
35870      */   
35871     html : '',
35872     /**
35873      * @cfg {String} bgimage
35874      */   
35875     bgimage : '',
35876     /**
35877      * @cfg {String} videourl
35878      */   
35879     videourl : '',
35880     /**
35881      * @cfg {String} cls
35882      */   
35883     cls : '',
35884     /**
35885      * @cfg {String} href
35886      */   
35887     href : '',
35888     /**
35889      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35890      */   
35891     size : 'xs',
35892     
35893     /**
35894      * @cfg {String} placetitle (center|bottom)
35895      */   
35896     placetitle : '',
35897     
35898     /**
35899      * @cfg {Boolean} isFitContainer defalut true
35900      */   
35901     isFitContainer : true, 
35902     
35903     /**
35904      * @cfg {Boolean} preventDefault defalut false
35905      */   
35906     preventDefault : false, 
35907     
35908     /**
35909      * @cfg {Boolean} inverse defalut false
35910      */   
35911     maskInverse : false, 
35912     
35913     getAutoCreate : function()
35914     {
35915         if(!this.isFitContainer){
35916             return this.getSplitAutoCreate();
35917         }
35918         
35919         var cls = 'masonry-brick masonry-brick-full';
35920         
35921         if(this.href.length){
35922             cls += ' masonry-brick-link';
35923         }
35924         
35925         if(this.bgimage.length){
35926             cls += ' masonry-brick-image';
35927         }
35928         
35929         if(this.maskInverse){
35930             cls += ' mask-inverse';
35931         }
35932         
35933         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35934             cls += ' enable-mask';
35935         }
35936         
35937         if(this.size){
35938             cls += ' masonry-' + this.size + '-brick';
35939         }
35940         
35941         if(this.placetitle.length){
35942             
35943             switch (this.placetitle) {
35944                 case 'center' :
35945                     cls += ' masonry-center-title';
35946                     break;
35947                 case 'bottom' :
35948                     cls += ' masonry-bottom-title';
35949                     break;
35950                 default:
35951                     break;
35952             }
35953             
35954         } else {
35955             if(!this.html.length && !this.bgimage.length){
35956                 cls += ' masonry-center-title';
35957             }
35958
35959             if(!this.html.length && this.bgimage.length){
35960                 cls += ' masonry-bottom-title';
35961             }
35962         }
35963         
35964         if(this.cls){
35965             cls += ' ' + this.cls;
35966         }
35967         
35968         var cfg = {
35969             tag: (this.href.length) ? 'a' : 'div',
35970             cls: cls,
35971             cn: [
35972                 {
35973                     tag: 'div',
35974                     cls: 'masonry-brick-mask'
35975                 },
35976                 {
35977                     tag: 'div',
35978                     cls: 'masonry-brick-paragraph',
35979                     cn: []
35980                 }
35981             ]
35982         };
35983         
35984         if(this.href.length){
35985             cfg.href = this.href;
35986         }
35987         
35988         var cn = cfg.cn[1].cn;
35989         
35990         if(this.title.length){
35991             cn.push({
35992                 tag: 'h4',
35993                 cls: 'masonry-brick-title',
35994                 html: this.title
35995             });
35996         }
35997         
35998         if(this.html.length){
35999             cn.push({
36000                 tag: 'p',
36001                 cls: 'masonry-brick-text',
36002                 html: this.html
36003             });
36004         }
36005         
36006         if (!this.title.length && !this.html.length) {
36007             cfg.cn[1].cls += ' hide';
36008         }
36009         
36010         if(this.bgimage.length){
36011             cfg.cn.push({
36012                 tag: 'img',
36013                 cls: 'masonry-brick-image-view',
36014                 src: this.bgimage
36015             });
36016         }
36017         
36018         if(this.videourl.length){
36019             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36020             // youtube support only?
36021             cfg.cn.push({
36022                 tag: 'iframe',
36023                 cls: 'masonry-brick-image-view',
36024                 src: vurl,
36025                 frameborder : 0,
36026                 allowfullscreen : true
36027             });
36028         }
36029         
36030         return cfg;
36031         
36032     },
36033     
36034     getSplitAutoCreate : function()
36035     {
36036         var cls = 'masonry-brick masonry-brick-split';
36037         
36038         if(this.href.length){
36039             cls += ' masonry-brick-link';
36040         }
36041         
36042         if(this.bgimage.length){
36043             cls += ' masonry-brick-image';
36044         }
36045         
36046         if(this.size){
36047             cls += ' masonry-' + this.size + '-brick';
36048         }
36049         
36050         switch (this.placetitle) {
36051             case 'center' :
36052                 cls += ' masonry-center-title';
36053                 break;
36054             case 'bottom' :
36055                 cls += ' masonry-bottom-title';
36056                 break;
36057             default:
36058                 if(!this.bgimage.length){
36059                     cls += ' masonry-center-title';
36060                 }
36061
36062                 if(this.bgimage.length){
36063                     cls += ' masonry-bottom-title';
36064                 }
36065                 break;
36066         }
36067         
36068         if(this.cls){
36069             cls += ' ' + this.cls;
36070         }
36071         
36072         var cfg = {
36073             tag: (this.href.length) ? 'a' : 'div',
36074             cls: cls,
36075             cn: [
36076                 {
36077                     tag: 'div',
36078                     cls: 'masonry-brick-split-head',
36079                     cn: [
36080                         {
36081                             tag: 'div',
36082                             cls: 'masonry-brick-paragraph',
36083                             cn: []
36084                         }
36085                     ]
36086                 },
36087                 {
36088                     tag: 'div',
36089                     cls: 'masonry-brick-split-body',
36090                     cn: []
36091                 }
36092             ]
36093         };
36094         
36095         if(this.href.length){
36096             cfg.href = this.href;
36097         }
36098         
36099         if(this.title.length){
36100             cfg.cn[0].cn[0].cn.push({
36101                 tag: 'h4',
36102                 cls: 'masonry-brick-title',
36103                 html: this.title
36104             });
36105         }
36106         
36107         if(this.html.length){
36108             cfg.cn[1].cn.push({
36109                 tag: 'p',
36110                 cls: 'masonry-brick-text',
36111                 html: this.html
36112             });
36113         }
36114
36115         if(this.bgimage.length){
36116             cfg.cn[0].cn.push({
36117                 tag: 'img',
36118                 cls: 'masonry-brick-image-view',
36119                 src: this.bgimage
36120             });
36121         }
36122         
36123         if(this.videourl.length){
36124             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36125             // youtube support only?
36126             cfg.cn[0].cn.cn.push({
36127                 tag: 'iframe',
36128                 cls: 'masonry-brick-image-view',
36129                 src: vurl,
36130                 frameborder : 0,
36131                 allowfullscreen : true
36132             });
36133         }
36134         
36135         return cfg;
36136     },
36137     
36138     initEvents: function() 
36139     {
36140         switch (this.size) {
36141             case 'xs' :
36142                 this.x = 1;
36143                 this.y = 1;
36144                 break;
36145             case 'sm' :
36146                 this.x = 2;
36147                 this.y = 2;
36148                 break;
36149             case 'md' :
36150             case 'md-left' :
36151             case 'md-right' :
36152                 this.x = 3;
36153                 this.y = 3;
36154                 break;
36155             case 'tall' :
36156                 this.x = 2;
36157                 this.y = 3;
36158                 break;
36159             case 'wide' :
36160                 this.x = 3;
36161                 this.y = 2;
36162                 break;
36163             case 'wide-thin' :
36164                 this.x = 3;
36165                 this.y = 1;
36166                 break;
36167                         
36168             default :
36169                 break;
36170         }
36171         
36172         if(Roo.isTouch){
36173             this.el.on('touchstart', this.onTouchStart, this);
36174             this.el.on('touchmove', this.onTouchMove, this);
36175             this.el.on('touchend', this.onTouchEnd, this);
36176             this.el.on('contextmenu', this.onContextMenu, this);
36177         } else {
36178             this.el.on('mouseenter'  ,this.enter, this);
36179             this.el.on('mouseleave', this.leave, this);
36180             this.el.on('click', this.onClick, this);
36181         }
36182         
36183         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36184             this.parent().bricks.push(this);   
36185         }
36186         
36187     },
36188     
36189     onClick: function(e, el)
36190     {
36191         var time = this.endTimer - this.startTimer;
36192         // Roo.log(e.preventDefault());
36193         if(Roo.isTouch){
36194             if(time > 1000){
36195                 e.preventDefault();
36196                 return;
36197             }
36198         }
36199         
36200         if(!this.preventDefault){
36201             return;
36202         }
36203         
36204         e.preventDefault();
36205         
36206         if (this.activeClass != '') {
36207             this.selectBrick();
36208         }
36209         
36210         this.fireEvent('click', this, e);
36211     },
36212     
36213     enter: function(e, el)
36214     {
36215         e.preventDefault();
36216         
36217         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36218             return;
36219         }
36220         
36221         if(this.bgimage.length && this.html.length){
36222             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36223         }
36224     },
36225     
36226     leave: function(e, el)
36227     {
36228         e.preventDefault();
36229         
36230         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36231             return;
36232         }
36233         
36234         if(this.bgimage.length && this.html.length){
36235             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36236         }
36237     },
36238     
36239     onTouchStart: function(e, el)
36240     {
36241 //        e.preventDefault();
36242         
36243         this.touchmoved = false;
36244         
36245         if(!this.isFitContainer){
36246             return;
36247         }
36248         
36249         if(!this.bgimage.length || !this.html.length){
36250             return;
36251         }
36252         
36253         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36254         
36255         this.timer = new Date().getTime();
36256         
36257     },
36258     
36259     onTouchMove: function(e, el)
36260     {
36261         this.touchmoved = true;
36262     },
36263     
36264     onContextMenu : function(e,el)
36265     {
36266         e.preventDefault();
36267         e.stopPropagation();
36268         return false;
36269     },
36270     
36271     onTouchEnd: function(e, el)
36272     {
36273 //        e.preventDefault();
36274         
36275         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36276         
36277             this.leave(e,el);
36278             
36279             return;
36280         }
36281         
36282         if(!this.bgimage.length || !this.html.length){
36283             
36284             if(this.href.length){
36285                 window.location.href = this.href;
36286             }
36287             
36288             return;
36289         }
36290         
36291         if(!this.isFitContainer){
36292             return;
36293         }
36294         
36295         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36296         
36297         window.location.href = this.href;
36298     },
36299     
36300     //selection on single brick only
36301     selectBrick : function() {
36302         
36303         if (!this.parentId) {
36304             return;
36305         }
36306         
36307         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36308         var index = m.selectedBrick.indexOf(this.id);
36309         
36310         if ( index > -1) {
36311             m.selectedBrick.splice(index,1);
36312             this.el.removeClass(this.activeClass);
36313             return;
36314         }
36315         
36316         for(var i = 0; i < m.selectedBrick.length; i++) {
36317             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36318             b.el.removeClass(b.activeClass);
36319         }
36320         
36321         m.selectedBrick = [];
36322         
36323         m.selectedBrick.push(this.id);
36324         this.el.addClass(this.activeClass);
36325         return;
36326     },
36327     
36328     isSelected : function(){
36329         return this.el.hasClass(this.activeClass);
36330         
36331     }
36332 });
36333
36334 Roo.apply(Roo.bootstrap.MasonryBrick, {
36335     
36336     //groups: {},
36337     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36338      /**
36339     * register a Masonry Brick
36340     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36341     */
36342     
36343     register : function(brick)
36344     {
36345         //this.groups[brick.id] = brick;
36346         this.groups.add(brick.id, brick);
36347     },
36348     /**
36349     * fetch a  masonry brick based on the masonry brick ID
36350     * @param {string} the masonry brick to add
36351     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36352     */
36353     
36354     get: function(brick_id) 
36355     {
36356         // if (typeof(this.groups[brick_id]) == 'undefined') {
36357         //     return false;
36358         // }
36359         // return this.groups[brick_id] ;
36360         
36361         if(this.groups.key(brick_id)) {
36362             return this.groups.key(brick_id);
36363         }
36364         
36365         return false;
36366     }
36367     
36368     
36369     
36370 });
36371
36372  /*
36373  * - LGPL
36374  *
36375  * element
36376  * 
36377  */
36378
36379 /**
36380  * @class Roo.bootstrap.Brick
36381  * @extends Roo.bootstrap.Component
36382  * Bootstrap Brick class
36383  * 
36384  * @constructor
36385  * Create a new Brick
36386  * @param {Object} config The config object
36387  */
36388
36389 Roo.bootstrap.Brick = function(config){
36390     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36391     
36392     this.addEvents({
36393         // raw events
36394         /**
36395          * @event click
36396          * When a Brick is click
36397          * @param {Roo.bootstrap.Brick} this
36398          * @param {Roo.EventObject} e
36399          */
36400         "click" : true
36401     });
36402 };
36403
36404 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36405     
36406     /**
36407      * @cfg {String} title
36408      */   
36409     title : '',
36410     /**
36411      * @cfg {String} html
36412      */   
36413     html : '',
36414     /**
36415      * @cfg {String} bgimage
36416      */   
36417     bgimage : '',
36418     /**
36419      * @cfg {String} cls
36420      */   
36421     cls : '',
36422     /**
36423      * @cfg {String} href
36424      */   
36425     href : '',
36426     /**
36427      * @cfg {String} video
36428      */   
36429     video : '',
36430     /**
36431      * @cfg {Boolean} square
36432      */   
36433     square : true,
36434     
36435     getAutoCreate : function()
36436     {
36437         var cls = 'roo-brick';
36438         
36439         if(this.href.length){
36440             cls += ' roo-brick-link';
36441         }
36442         
36443         if(this.bgimage.length){
36444             cls += ' roo-brick-image';
36445         }
36446         
36447         if(!this.html.length && !this.bgimage.length){
36448             cls += ' roo-brick-center-title';
36449         }
36450         
36451         if(!this.html.length && this.bgimage.length){
36452             cls += ' roo-brick-bottom-title';
36453         }
36454         
36455         if(this.cls){
36456             cls += ' ' + this.cls;
36457         }
36458         
36459         var cfg = {
36460             tag: (this.href.length) ? 'a' : 'div',
36461             cls: cls,
36462             cn: [
36463                 {
36464                     tag: 'div',
36465                     cls: 'roo-brick-paragraph',
36466                     cn: []
36467                 }
36468             ]
36469         };
36470         
36471         if(this.href.length){
36472             cfg.href = this.href;
36473         }
36474         
36475         var cn = cfg.cn[0].cn;
36476         
36477         if(this.title.length){
36478             cn.push({
36479                 tag: 'h4',
36480                 cls: 'roo-brick-title',
36481                 html: this.title
36482             });
36483         }
36484         
36485         if(this.html.length){
36486             cn.push({
36487                 tag: 'p',
36488                 cls: 'roo-brick-text',
36489                 html: this.html
36490             });
36491         } else {
36492             cn.cls += ' hide';
36493         }
36494         
36495         if(this.bgimage.length){
36496             cfg.cn.push({
36497                 tag: 'img',
36498                 cls: 'roo-brick-image-view',
36499                 src: this.bgimage
36500             });
36501         }
36502         
36503         return cfg;
36504     },
36505     
36506     initEvents: function() 
36507     {
36508         if(this.title.length || this.html.length){
36509             this.el.on('mouseenter'  ,this.enter, this);
36510             this.el.on('mouseleave', this.leave, this);
36511         }
36512         
36513         Roo.EventManager.onWindowResize(this.resize, this); 
36514         
36515         if(this.bgimage.length){
36516             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36517             this.imageEl.on('load', this.onImageLoad, this);
36518             return;
36519         }
36520         
36521         this.resize();
36522     },
36523     
36524     onImageLoad : function()
36525     {
36526         this.resize();
36527     },
36528     
36529     resize : function()
36530     {
36531         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36532         
36533         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36534         
36535         if(this.bgimage.length){
36536             var image = this.el.select('.roo-brick-image-view', true).first();
36537             
36538             image.setWidth(paragraph.getWidth());
36539             
36540             if(this.square){
36541                 image.setHeight(paragraph.getWidth());
36542             }
36543             
36544             this.el.setHeight(image.getHeight());
36545             paragraph.setHeight(image.getHeight());
36546             
36547         }
36548         
36549     },
36550     
36551     enter: function(e, el)
36552     {
36553         e.preventDefault();
36554         
36555         if(this.bgimage.length){
36556             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36557             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36558         }
36559     },
36560     
36561     leave: function(e, el)
36562     {
36563         e.preventDefault();
36564         
36565         if(this.bgimage.length){
36566             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36567             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36568         }
36569     }
36570     
36571 });
36572
36573  
36574
36575  /*
36576  * - LGPL
36577  *
36578  * Number field 
36579  */
36580
36581 /**
36582  * @class Roo.bootstrap.form.NumberField
36583  * @extends Roo.bootstrap.form.Input
36584  * Bootstrap NumberField class
36585  * 
36586  * 
36587  * 
36588  * 
36589  * @constructor
36590  * Create a new NumberField
36591  * @param {Object} config The config object
36592  */
36593
36594 Roo.bootstrap.form.NumberField = function(config){
36595     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36596 };
36597
36598 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36599     
36600     /**
36601      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36602      */
36603     allowDecimals : true,
36604     /**
36605      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36606      */
36607     decimalSeparator : ".",
36608     /**
36609      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36610      */
36611     decimalPrecision : 2,
36612     /**
36613      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36614      */
36615     allowNegative : true,
36616     
36617     /**
36618      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36619      */
36620     allowZero: true,
36621     /**
36622      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36623      */
36624     minValue : Number.NEGATIVE_INFINITY,
36625     /**
36626      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36627      */
36628     maxValue : Number.MAX_VALUE,
36629     /**
36630      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36631      */
36632     minText : "The minimum value for this field is {0}",
36633     /**
36634      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36635      */
36636     maxText : "The maximum value for this field is {0}",
36637     /**
36638      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36639      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36640      */
36641     nanText : "{0} is not a valid number",
36642     /**
36643      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36644      */
36645     thousandsDelimiter : false,
36646     /**
36647      * @cfg {String} valueAlign alignment of value
36648      */
36649     valueAlign : "left",
36650
36651     getAutoCreate : function()
36652     {
36653         var hiddenInput = {
36654             tag: 'input',
36655             type: 'hidden',
36656             id: Roo.id(),
36657             cls: 'hidden-number-input'
36658         };
36659         
36660         if (this.name) {
36661             hiddenInput.name = this.name;
36662         }
36663         
36664         this.name = '';
36665         
36666         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36667         
36668         this.name = hiddenInput.name;
36669         
36670         if(cfg.cn.length > 0) {
36671             cfg.cn.push(hiddenInput);
36672         }
36673         
36674         return cfg;
36675     },
36676
36677     // private
36678     initEvents : function()
36679     {   
36680         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36681         
36682         var allowed = "0123456789";
36683         
36684         if(this.allowDecimals){
36685             allowed += this.decimalSeparator;
36686         }
36687         
36688         if(this.allowNegative){
36689             allowed += "-";
36690         }
36691         
36692         if(this.thousandsDelimiter) {
36693             allowed += ",";
36694         }
36695         
36696         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36697         
36698         var keyPress = function(e){
36699             
36700             var k = e.getKey();
36701             
36702             var c = e.getCharCode();
36703             
36704             if(
36705                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36706                     allowed.indexOf(String.fromCharCode(c)) === -1
36707             ){
36708                 e.stopEvent();
36709                 return;
36710             }
36711             
36712             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36713                 return;
36714             }
36715             
36716             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36717                 e.stopEvent();
36718             }
36719         };
36720         
36721         this.el.on("keypress", keyPress, this);
36722     },
36723     
36724     validateValue : function(value)
36725     {
36726         
36727         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36728             return false;
36729         }
36730         
36731         var num = this.parseValue(value);
36732         
36733         if(isNaN(num)){
36734             this.markInvalid(String.format(this.nanText, value));
36735             return false;
36736         }
36737         
36738         if(num < this.minValue){
36739             this.markInvalid(String.format(this.minText, this.minValue));
36740             return false;
36741         }
36742         
36743         if(num > this.maxValue){
36744             this.markInvalid(String.format(this.maxText, this.maxValue));
36745             return false;
36746         }
36747         
36748         return true;
36749     },
36750
36751     getValue : function()
36752     {
36753         var v = this.hiddenEl().getValue();
36754         
36755         return this.fixPrecision(this.parseValue(v));
36756     },
36757
36758     parseValue : function(value)
36759     {
36760         if(this.thousandsDelimiter) {
36761             value += "";
36762             r = new RegExp(",", "g");
36763             value = value.replace(r, "");
36764         }
36765         
36766         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36767         return isNaN(value) ? '' : value;
36768     },
36769
36770     fixPrecision : function(value)
36771     {
36772         if(this.thousandsDelimiter) {
36773             value += "";
36774             r = new RegExp(",", "g");
36775             value = value.replace(r, "");
36776         }
36777         
36778         var nan = isNaN(value);
36779         
36780         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36781             return nan ? '' : value;
36782         }
36783         return parseFloat(value).toFixed(this.decimalPrecision);
36784     },
36785
36786     setValue : function(v)
36787     {
36788         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36789         
36790         this.value = v;
36791         
36792         if(this.rendered){
36793             
36794             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36795             
36796             this.inputEl().dom.value = (v == '') ? '' :
36797                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36798             
36799             if(!this.allowZero && v === '0') {
36800                 this.hiddenEl().dom.value = '';
36801                 this.inputEl().dom.value = '';
36802             }
36803             
36804             this.validate();
36805         }
36806     },
36807
36808     decimalPrecisionFcn : function(v)
36809     {
36810         return Math.floor(v);
36811     },
36812
36813     beforeBlur : function()
36814     {
36815         var v = this.parseValue(this.getRawValue());
36816         
36817         if(v || v === 0 || v === ''){
36818             this.setValue(v);
36819         }
36820     },
36821     
36822     hiddenEl : function()
36823     {
36824         return this.el.select('input.hidden-number-input',true).first();
36825     }
36826     
36827 });
36828
36829  
36830
36831 /*
36832 * Licence: LGPL
36833 */
36834
36835 /**
36836  * @class Roo.bootstrap.DocumentSlider
36837  * @extends Roo.bootstrap.Component
36838  * Bootstrap DocumentSlider class
36839  * 
36840  * @constructor
36841  * Create a new DocumentViewer
36842  * @param {Object} config The config object
36843  */
36844
36845 Roo.bootstrap.DocumentSlider = function(config){
36846     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36847     
36848     this.files = [];
36849     
36850     this.addEvents({
36851         /**
36852          * @event initial
36853          * Fire after initEvent
36854          * @param {Roo.bootstrap.DocumentSlider} this
36855          */
36856         "initial" : true,
36857         /**
36858          * @event update
36859          * Fire after update
36860          * @param {Roo.bootstrap.DocumentSlider} this
36861          */
36862         "update" : true,
36863         /**
36864          * @event click
36865          * Fire after click
36866          * @param {Roo.bootstrap.DocumentSlider} this
36867          */
36868         "click" : true
36869     });
36870 };
36871
36872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36873     
36874     files : false,
36875     
36876     indicator : 0,
36877     
36878     getAutoCreate : function()
36879     {
36880         var cfg = {
36881             tag : 'div',
36882             cls : 'roo-document-slider',
36883             cn : [
36884                 {
36885                     tag : 'div',
36886                     cls : 'roo-document-slider-header',
36887                     cn : [
36888                         {
36889                             tag : 'div',
36890                             cls : 'roo-document-slider-header-title'
36891                         }
36892                     ]
36893                 },
36894                 {
36895                     tag : 'div',
36896                     cls : 'roo-document-slider-body',
36897                     cn : [
36898                         {
36899                             tag : 'div',
36900                             cls : 'roo-document-slider-prev',
36901                             cn : [
36902                                 {
36903                                     tag : 'i',
36904                                     cls : 'fa fa-chevron-left'
36905                                 }
36906                             ]
36907                         },
36908                         {
36909                             tag : 'div',
36910                             cls : 'roo-document-slider-thumb',
36911                             cn : [
36912                                 {
36913                                     tag : 'img',
36914                                     cls : 'roo-document-slider-image'
36915                                 }
36916                             ]
36917                         },
36918                         {
36919                             tag : 'div',
36920                             cls : 'roo-document-slider-next',
36921                             cn : [
36922                                 {
36923                                     tag : 'i',
36924                                     cls : 'fa fa-chevron-right'
36925                                 }
36926                             ]
36927                         }
36928                     ]
36929                 }
36930             ]
36931         };
36932         
36933         return cfg;
36934     },
36935     
36936     initEvents : function()
36937     {
36938         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36939         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36940         
36941         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36942         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36943         
36944         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36945         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36946         
36947         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36948         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36949         
36950         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36951         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36952         
36953         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36954         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36955         
36956         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36957         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.thumbEl.on('click', this.onClick, this);
36960         
36961         this.prevIndicator.on('click', this.prev, this);
36962         
36963         this.nextIndicator.on('click', this.next, this);
36964         
36965     },
36966     
36967     initial : function()
36968     {
36969         if(this.files.length){
36970             this.indicator = 1;
36971             this.update()
36972         }
36973         
36974         this.fireEvent('initial', this);
36975     },
36976     
36977     update : function()
36978     {
36979         this.imageEl.attr('src', this.files[this.indicator - 1]);
36980         
36981         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36982         
36983         this.prevIndicator.show();
36984         
36985         if(this.indicator == 1){
36986             this.prevIndicator.hide();
36987         }
36988         
36989         this.nextIndicator.show();
36990         
36991         if(this.indicator == this.files.length){
36992             this.nextIndicator.hide();
36993         }
36994         
36995         this.thumbEl.scrollTo('top');
36996         
36997         this.fireEvent('update', this);
36998     },
36999     
37000     onClick : function(e)
37001     {
37002         e.preventDefault();
37003         
37004         this.fireEvent('click', this);
37005     },
37006     
37007     prev : function(e)
37008     {
37009         e.preventDefault();
37010         
37011         this.indicator = Math.max(1, this.indicator - 1);
37012         
37013         this.update();
37014     },
37015     
37016     next : function(e)
37017     {
37018         e.preventDefault();
37019         
37020         this.indicator = Math.min(this.files.length, this.indicator + 1);
37021         
37022         this.update();
37023     }
37024 });
37025 /*
37026  * - LGPL
37027  *
37028  * RadioSet
37029  *
37030  *
37031  */
37032
37033 /**
37034  * @class Roo.bootstrap.form.RadioSet
37035  * @extends Roo.bootstrap.form.Input
37036  * @children Roo.bootstrap.form.Radio
37037  * Bootstrap RadioSet class
37038  * @cfg {String} indicatorpos (left|right) default left
37039  * @cfg {Boolean} inline (true|false) inline the element (default true)
37040  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37041  * @constructor
37042  * Create a new RadioSet
37043  * @param {Object} config The config object
37044  */
37045
37046 Roo.bootstrap.form.RadioSet = function(config){
37047     
37048     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37049     
37050     this.radioes = [];
37051     
37052     Roo.bootstrap.form.RadioSet.register(this);
37053     
37054     this.addEvents({
37055         /**
37056         * @event check
37057         * Fires when the element is checked or unchecked.
37058         * @param {Roo.bootstrap.form.RadioSet} this This radio
37059         * @param {Roo.bootstrap.form.Radio} item The checked item
37060         */
37061        check : true,
37062        /**
37063         * @event click
37064         * Fires when the element is click.
37065         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37066         * @param {Roo.bootstrap.form.Radio} item The checked item
37067         * @param {Roo.EventObject} e The event object
37068         */
37069        click : true
37070     });
37071     
37072 };
37073
37074 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37075
37076     radioes : false,
37077     
37078     inline : true,
37079     
37080     weight : '',
37081     
37082     indicatorpos : 'left',
37083     
37084     getAutoCreate : function()
37085     {
37086         var label = {
37087             tag : 'label',
37088             cls : 'roo-radio-set-label',
37089             cn : [
37090                 {
37091                     tag : 'span',
37092                     html : this.fieldLabel
37093                 }
37094             ]
37095         };
37096         if (Roo.bootstrap.version == 3) {
37097             
37098             
37099             if(this.indicatorpos == 'left'){
37100                 label.cn.unshift({
37101                     tag : 'i',
37102                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37103                     tooltip : 'This field is required'
37104                 });
37105             } else {
37106                 label.cn.push({
37107                     tag : 'i',
37108                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37109                     tooltip : 'This field is required'
37110                 });
37111             }
37112         }
37113         var items = {
37114             tag : 'div',
37115             cls : 'roo-radio-set-items'
37116         };
37117         
37118         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37119         
37120         if (align === 'left' && this.fieldLabel.length) {
37121             
37122             items = {
37123                 cls : "roo-radio-set-right", 
37124                 cn: [
37125                     items
37126                 ]
37127             };
37128             
37129             if(this.labelWidth > 12){
37130                 label.style = "width: " + this.labelWidth + 'px';
37131             }
37132             
37133             if(this.labelWidth < 13 && this.labelmd == 0){
37134                 this.labelmd = this.labelWidth;
37135             }
37136             
37137             if(this.labellg > 0){
37138                 label.cls += ' col-lg-' + this.labellg;
37139                 items.cls += ' col-lg-' + (12 - this.labellg);
37140             }
37141             
37142             if(this.labelmd > 0){
37143                 label.cls += ' col-md-' + this.labelmd;
37144                 items.cls += ' col-md-' + (12 - this.labelmd);
37145             }
37146             
37147             if(this.labelsm > 0){
37148                 label.cls += ' col-sm-' + this.labelsm;
37149                 items.cls += ' col-sm-' + (12 - this.labelsm);
37150             }
37151             
37152             if(this.labelxs > 0){
37153                 label.cls += ' col-xs-' + this.labelxs;
37154                 items.cls += ' col-xs-' + (12 - this.labelxs);
37155             }
37156         }
37157         
37158         var cfg = {
37159             tag : 'div',
37160             cls : 'roo-radio-set',
37161             cn : [
37162                 {
37163                     tag : 'input',
37164                     cls : 'roo-radio-set-input',
37165                     type : 'hidden',
37166                     name : this.name,
37167                     value : this.value ? this.value :  ''
37168                 },
37169                 label,
37170                 items
37171             ]
37172         };
37173         
37174         if(this.weight.length){
37175             cfg.cls += ' roo-radio-' + this.weight;
37176         }
37177         
37178         if(this.inline) {
37179             cfg.cls += ' roo-radio-set-inline';
37180         }
37181         
37182         var settings=this;
37183         ['xs','sm','md','lg'].map(function(size){
37184             if (settings[size]) {
37185                 cfg.cls += ' col-' + size + '-' + settings[size];
37186             }
37187         });
37188         
37189         return cfg;
37190         
37191     },
37192
37193     initEvents : function()
37194     {
37195         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37196         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37197         
37198         if(!this.fieldLabel.length){
37199             this.labelEl.hide();
37200         }
37201         
37202         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37203         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37204         
37205         this.indicator = this.indicatorEl();
37206         
37207         if(this.indicator){
37208             this.indicator.addClass('invisible');
37209         }
37210         
37211         this.originalValue = this.getValue();
37212         
37213     },
37214     
37215     inputEl: function ()
37216     {
37217         return this.el.select('.roo-radio-set-input', true).first();
37218     },
37219     
37220     getChildContainer : function()
37221     {
37222         return this.itemsEl;
37223     },
37224     
37225     register : function(item)
37226     {
37227         this.radioes.push(item);
37228         
37229     },
37230     
37231     validate : function()
37232     {   
37233         if(this.getVisibilityEl().hasClass('hidden')){
37234             return true;
37235         }
37236         
37237         var valid = false;
37238         
37239         Roo.each(this.radioes, function(i){
37240             if(!i.checked){
37241                 return;
37242             }
37243             
37244             valid = true;
37245             return false;
37246         });
37247         
37248         if(this.allowBlank) {
37249             return true;
37250         }
37251         
37252         if(this.disabled || valid){
37253             this.markValid();
37254             return true;
37255         }
37256         
37257         this.markInvalid();
37258         return false;
37259         
37260     },
37261     
37262     markValid : function()
37263     {
37264         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37265             this.indicatorEl().removeClass('visible');
37266             this.indicatorEl().addClass('invisible');
37267         }
37268         
37269         
37270         if (Roo.bootstrap.version == 3) {
37271             this.el.removeClass([this.invalidClass, this.validClass]);
37272             this.el.addClass(this.validClass);
37273         } else {
37274             this.el.removeClass(['is-invalid','is-valid']);
37275             this.el.addClass(['is-valid']);
37276         }
37277         this.fireEvent('valid', this);
37278     },
37279     
37280     markInvalid : function(msg)
37281     {
37282         if(this.allowBlank || this.disabled){
37283             return;
37284         }
37285         
37286         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37287             this.indicatorEl().removeClass('invisible');
37288             this.indicatorEl().addClass('visible');
37289         }
37290         if (Roo.bootstrap.version == 3) {
37291             this.el.removeClass([this.invalidClass, this.validClass]);
37292             this.el.addClass(this.invalidClass);
37293         } else {
37294             this.el.removeClass(['is-invalid','is-valid']);
37295             this.el.addClass(['is-invalid']);
37296         }
37297         
37298         this.fireEvent('invalid', this, msg);
37299         
37300     },
37301     
37302     setValue : function(v, suppressEvent)
37303     {   
37304         if(this.value === v){
37305             return;
37306         }
37307         
37308         this.value = v;
37309         
37310         if(this.rendered){
37311             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37312         }
37313         
37314         Roo.each(this.radioes, function(i){
37315             i.checked = false;
37316             i.el.removeClass('checked');
37317         });
37318         
37319         Roo.each(this.radioes, function(i){
37320             
37321             if(i.value === v || i.value.toString() === v.toString()){
37322                 i.checked = true;
37323                 i.el.addClass('checked');
37324                 
37325                 if(suppressEvent !== true){
37326                     this.fireEvent('check', this, i);
37327                 }
37328                 
37329                 return false;
37330             }
37331             
37332         }, this);
37333         
37334         this.validate();
37335     },
37336     
37337     clearInvalid : function(){
37338         
37339         if(!this.el || this.preventMark){
37340             return;
37341         }
37342         
37343         this.el.removeClass([this.invalidClass]);
37344         
37345         this.fireEvent('valid', this);
37346     }
37347     
37348 });
37349
37350 Roo.apply(Roo.bootstrap.form.RadioSet, {
37351     
37352     groups: {},
37353     
37354     register : function(set)
37355     {
37356         this.groups[set.name] = set;
37357     },
37358     
37359     get: function(name) 
37360     {
37361         if (typeof(this.groups[name]) == 'undefined') {
37362             return false;
37363         }
37364         
37365         return this.groups[name] ;
37366     }
37367     
37368 });
37369 /*
37370  * Based on:
37371  * Ext JS Library 1.1.1
37372  * Copyright(c) 2006-2007, Ext JS, LLC.
37373  *
37374  * Originally Released Under LGPL - original licence link has changed is not relivant.
37375  *
37376  * Fork - LGPL
37377  * <script type="text/javascript">
37378  */
37379
37380
37381 /**
37382  * @class Roo.bootstrap.SplitBar
37383  * @extends Roo.util.Observable
37384  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37385  * <br><br>
37386  * Usage:
37387  * <pre><code>
37388 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37389                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37390 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37391 split.minSize = 100;
37392 split.maxSize = 600;
37393 split.animate = true;
37394 split.on('moved', splitterMoved);
37395 </code></pre>
37396  * @constructor
37397  * Create a new SplitBar
37398  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37399  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37400  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37401  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37402                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37403                         position of the SplitBar).
37404  */
37405 Roo.bootstrap.SplitBar = function(cfg){
37406     
37407     /** @private */
37408     
37409     //{
37410     //  dragElement : elm
37411     //  resizingElement: el,
37412         // optional..
37413     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37414     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37415         // existingProxy ???
37416     //}
37417     
37418     this.el = Roo.get(cfg.dragElement, true);
37419     this.el.dom.unselectable = "on";
37420     /** @private */
37421     this.resizingEl = Roo.get(cfg.resizingElement, true);
37422
37423     /**
37424      * @private
37425      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37426      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37427      * @type Number
37428      */
37429     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37430     
37431     /**
37432      * The minimum size of the resizing element. (Defaults to 0)
37433      * @type Number
37434      */
37435     this.minSize = 0;
37436     
37437     /**
37438      * The maximum size of the resizing element. (Defaults to 2000)
37439      * @type Number
37440      */
37441     this.maxSize = 2000;
37442     
37443     /**
37444      * Whether to animate the transition to the new size
37445      * @type Boolean
37446      */
37447     this.animate = false;
37448     
37449     /**
37450      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37451      * @type Boolean
37452      */
37453     this.useShim = false;
37454     
37455     /** @private */
37456     this.shim = null;
37457     
37458     if(!cfg.existingProxy){
37459         /** @private */
37460         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37461     }else{
37462         this.proxy = Roo.get(cfg.existingProxy).dom;
37463     }
37464     /** @private */
37465     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37466     
37467     /** @private */
37468     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37469     
37470     /** @private */
37471     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37472     
37473     /** @private */
37474     this.dragSpecs = {};
37475     
37476     /**
37477      * @private The adapter to use to positon and resize elements
37478      */
37479     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37480     this.adapter.init(this);
37481     
37482     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483         /** @private */
37484         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37485         this.el.addClass("roo-splitbar-h");
37486     }else{
37487         /** @private */
37488         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37489         this.el.addClass("roo-splitbar-v");
37490     }
37491     
37492     this.addEvents({
37493         /**
37494          * @event resize
37495          * Fires when the splitter is moved (alias for {@link #event-moved})
37496          * @param {Roo.bootstrap.SplitBar} this
37497          * @param {Number} newSize the new width or height
37498          */
37499         "resize" : true,
37500         /**
37501          * @event moved
37502          * Fires when the splitter is moved
37503          * @param {Roo.bootstrap.SplitBar} this
37504          * @param {Number} newSize the new width or height
37505          */
37506         "moved" : true,
37507         /**
37508          * @event beforeresize
37509          * Fires before the splitter is dragged
37510          * @param {Roo.bootstrap.SplitBar} this
37511          */
37512         "beforeresize" : true,
37513
37514         "beforeapply" : true
37515     });
37516
37517     Roo.util.Observable.call(this);
37518 };
37519
37520 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37521     onStartProxyDrag : function(x, y){
37522         this.fireEvent("beforeresize", this);
37523         if(!this.overlay){
37524             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37525             o.unselectable();
37526             o.enableDisplayMode("block");
37527             // all splitbars share the same overlay
37528             Roo.bootstrap.SplitBar.prototype.overlay = o;
37529         }
37530         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37531         this.overlay.show();
37532         Roo.get(this.proxy).setDisplayed("block");
37533         var size = this.adapter.getElementSize(this);
37534         this.activeMinSize = this.getMinimumSize();;
37535         this.activeMaxSize = this.getMaximumSize();;
37536         var c1 = size - this.activeMinSize;
37537         var c2 = Math.max(this.activeMaxSize - size, 0);
37538         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37539             this.dd.resetConstraints();
37540             this.dd.setXConstraint(
37541                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37542                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37543             );
37544             this.dd.setYConstraint(0, 0);
37545         }else{
37546             this.dd.resetConstraints();
37547             this.dd.setXConstraint(0, 0);
37548             this.dd.setYConstraint(
37549                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37550                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37551             );
37552          }
37553         this.dragSpecs.startSize = size;
37554         this.dragSpecs.startPoint = [x, y];
37555         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37556     },
37557     
37558     /** 
37559      * @private Called after the drag operation by the DDProxy
37560      */
37561     onEndProxyDrag : function(e){
37562         Roo.get(this.proxy).setDisplayed(false);
37563         var endPoint = Roo.lib.Event.getXY(e);
37564         if(this.overlay){
37565             this.overlay.hide();
37566         }
37567         var newSize;
37568         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37569             newSize = this.dragSpecs.startSize + 
37570                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37571                     endPoint[0] - this.dragSpecs.startPoint[0] :
37572                     this.dragSpecs.startPoint[0] - endPoint[0]
37573                 );
37574         }else{
37575             newSize = this.dragSpecs.startSize + 
37576                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37577                     endPoint[1] - this.dragSpecs.startPoint[1] :
37578                     this.dragSpecs.startPoint[1] - endPoint[1]
37579                 );
37580         }
37581         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37582         if(newSize != this.dragSpecs.startSize){
37583             if(this.fireEvent('beforeapply', this, newSize) !== false){
37584                 this.adapter.setElementSize(this, newSize);
37585                 this.fireEvent("moved", this, newSize);
37586                 this.fireEvent("resize", this, newSize);
37587             }
37588         }
37589     },
37590     
37591     /**
37592      * Get the adapter this SplitBar uses
37593      * @return The adapter object
37594      */
37595     getAdapter : function(){
37596         return this.adapter;
37597     },
37598     
37599     /**
37600      * Set the adapter this SplitBar uses
37601      * @param {Object} adapter A SplitBar adapter object
37602      */
37603     setAdapter : function(adapter){
37604         this.adapter = adapter;
37605         this.adapter.init(this);
37606     },
37607     
37608     /**
37609      * Gets the minimum size for the resizing element
37610      * @return {Number} The minimum size
37611      */
37612     getMinimumSize : function(){
37613         return this.minSize;
37614     },
37615     
37616     /**
37617      * Sets the minimum size for the resizing element
37618      * @param {Number} minSize The minimum size
37619      */
37620     setMinimumSize : function(minSize){
37621         this.minSize = minSize;
37622     },
37623     
37624     /**
37625      * Gets the maximum size for the resizing element
37626      * @return {Number} The maximum size
37627      */
37628     getMaximumSize : function(){
37629         return this.maxSize;
37630     },
37631     
37632     /**
37633      * Sets the maximum size for the resizing element
37634      * @param {Number} maxSize The maximum size
37635      */
37636     setMaximumSize : function(maxSize){
37637         this.maxSize = maxSize;
37638     },
37639     
37640     /**
37641      * Sets the initialize size for the resizing element
37642      * @param {Number} size The initial size
37643      */
37644     setCurrentSize : function(size){
37645         var oldAnimate = this.animate;
37646         this.animate = false;
37647         this.adapter.setElementSize(this, size);
37648         this.animate = oldAnimate;
37649     },
37650     
37651     /**
37652      * Destroy this splitbar. 
37653      * @param {Boolean} removeEl True to remove the element
37654      */
37655     destroy : function(removeEl){
37656         if(this.shim){
37657             this.shim.remove();
37658         }
37659         this.dd.unreg();
37660         this.proxy.parentNode.removeChild(this.proxy);
37661         if(removeEl){
37662             this.el.remove();
37663         }
37664     }
37665 });
37666
37667 /**
37668  * @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.
37669  */
37670 Roo.bootstrap.SplitBar.createProxy = function(dir){
37671     var proxy = new Roo.Element(document.createElement("div"));
37672     proxy.unselectable();
37673     var cls = 'roo-splitbar-proxy';
37674     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37675     document.body.appendChild(proxy.dom);
37676     return proxy.dom;
37677 };
37678
37679 /** 
37680  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37681  * Default Adapter. It assumes the splitter and resizing element are not positioned
37682  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37683  */
37684 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37685 };
37686
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37688     // do nothing for now
37689     init : function(s){
37690     
37691     },
37692     /**
37693      * Called before drag operations to get the current size of the resizing element. 
37694      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37695      */
37696      getElementSize : function(s){
37697         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37698             return s.resizingEl.getWidth();
37699         }else{
37700             return s.resizingEl.getHeight();
37701         }
37702     },
37703     
37704     /**
37705      * Called after drag operations to set the size of the resizing element.
37706      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37707      * @param {Number} newSize The new size to set
37708      * @param {Function} onComplete A function to be invoked when resizing is complete
37709      */
37710     setElementSize : function(s, newSize, onComplete){
37711         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37712             if(!s.animate){
37713                 s.resizingEl.setWidth(newSize);
37714                 if(onComplete){
37715                     onComplete(s, newSize);
37716                 }
37717             }else{
37718                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37719             }
37720         }else{
37721             
37722             if(!s.animate){
37723                 s.resizingEl.setHeight(newSize);
37724                 if(onComplete){
37725                     onComplete(s, newSize);
37726                 }
37727             }else{
37728                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37729             }
37730         }
37731     }
37732 };
37733
37734 /** 
37735  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37736  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37737  * Adapter that  moves the splitter element to align with the resized sizing element. 
37738  * Used with an absolute positioned SplitBar.
37739  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37740  * document.body, make sure you assign an id to the body element.
37741  */
37742 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37743     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37744     this.container = Roo.get(container);
37745 };
37746
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37748     init : function(s){
37749         this.basic.init(s);
37750     },
37751     
37752     getElementSize : function(s){
37753         return this.basic.getElementSize(s);
37754     },
37755     
37756     setElementSize : function(s, newSize, onComplete){
37757         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37758     },
37759     
37760     moveSplitter : function(s){
37761         var yes = Roo.bootstrap.SplitBar;
37762         switch(s.placement){
37763             case yes.LEFT:
37764                 s.el.setX(s.resizingEl.getRight());
37765                 break;
37766             case yes.RIGHT:
37767                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37768                 break;
37769             case yes.TOP:
37770                 s.el.setY(s.resizingEl.getBottom());
37771                 break;
37772             case yes.BOTTOM:
37773                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37774                 break;
37775         }
37776     }
37777 };
37778
37779 /**
37780  * Orientation constant - Create a vertical SplitBar
37781  * @static
37782  * @type Number
37783  */
37784 Roo.bootstrap.SplitBar.VERTICAL = 1;
37785
37786 /**
37787  * Orientation constant - Create a horizontal SplitBar
37788  * @static
37789  * @type Number
37790  */
37791 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37792
37793 /**
37794  * Placement constant - The resizing element is to the left of the splitter element
37795  * @static
37796  * @type Number
37797  */
37798 Roo.bootstrap.SplitBar.LEFT = 1;
37799
37800 /**
37801  * Placement constant - The resizing element is to the right of the splitter element
37802  * @static
37803  * @type Number
37804  */
37805 Roo.bootstrap.SplitBar.RIGHT = 2;
37806
37807 /**
37808  * Placement constant - The resizing element is positioned above the splitter element
37809  * @static
37810  * @type Number
37811  */
37812 Roo.bootstrap.SplitBar.TOP = 3;
37813
37814 /**
37815  * Placement constant - The resizing element is positioned under splitter element
37816  * @static
37817  * @type Number
37818  */
37819 Roo.bootstrap.SplitBar.BOTTOM = 4;
37820 /*
37821  * Based on:
37822  * Ext JS Library 1.1.1
37823  * Copyright(c) 2006-2007, Ext JS, LLC.
37824  *
37825  * Originally Released Under LGPL - original licence link has changed is not relivant.
37826  *
37827  * Fork - LGPL
37828  * <script type="text/javascript">
37829  */
37830
37831 /**
37832  * @class Roo.bootstrap.layout.Manager
37833  * @extends Roo.bootstrap.Component
37834  * @abstract
37835  * Base class for layout managers.
37836  */
37837 Roo.bootstrap.layout.Manager = function(config)
37838 {
37839     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37840
37841
37842
37843
37844
37845     /** false to disable window resize monitoring @type Boolean */
37846     this.monitorWindowResize = true;
37847     this.regions = {};
37848     this.addEvents({
37849         /**
37850          * @event layout
37851          * Fires when a layout is performed.
37852          * @param {Roo.LayoutManager} this
37853          */
37854         "layout" : true,
37855         /**
37856          * @event regionresized
37857          * Fires when the user resizes a region.
37858          * @param {Roo.LayoutRegion} region The resized region
37859          * @param {Number} newSize The new size (width for east/west, height for north/south)
37860          */
37861         "regionresized" : true,
37862         /**
37863          * @event regioncollapsed
37864          * Fires when a region is collapsed.
37865          * @param {Roo.LayoutRegion} region The collapsed region
37866          */
37867         "regioncollapsed" : true,
37868         /**
37869          * @event regionexpanded
37870          * Fires when a region is expanded.
37871          * @param {Roo.LayoutRegion} region The expanded region
37872          */
37873         "regionexpanded" : true
37874     });
37875     this.updating = false;
37876
37877     if (config.el) {
37878         this.el = Roo.get(config.el);
37879         this.initEvents();
37880     }
37881
37882 };
37883
37884 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37885
37886
37887     regions : null,
37888
37889     monitorWindowResize : true,
37890
37891
37892     updating : false,
37893
37894
37895     onRender : function(ct, position)
37896     {
37897         if(!this.el){
37898             this.el = Roo.get(ct);
37899             this.initEvents();
37900         }
37901         //this.fireEvent('render',this);
37902     },
37903
37904
37905     initEvents: function()
37906     {
37907
37908
37909         // ie scrollbar fix
37910         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37911             document.body.scroll = "no";
37912         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37913             this.el.position('relative');
37914         }
37915         this.id = this.el.id;
37916         this.el.addClass("roo-layout-container");
37917         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37918         if(this.el.dom != document.body ) {
37919             this.el.on('resize', this.layout,this);
37920             this.el.on('show', this.layout,this);
37921         }
37922
37923     },
37924
37925     /**
37926      * Returns true if this layout is currently being updated
37927      * @return {Boolean}
37928      */
37929     isUpdating : function(){
37930         return this.updating;
37931     },
37932
37933     /**
37934      * Suspend the LayoutManager from doing auto-layouts while
37935      * making multiple add or remove calls
37936      */
37937     beginUpdate : function(){
37938         this.updating = true;
37939     },
37940
37941     /**
37942      * Restore auto-layouts and optionally disable the manager from performing a layout
37943      * @param {Boolean} noLayout true to disable a layout update
37944      */
37945     endUpdate : function(noLayout){
37946         this.updating = false;
37947         if(!noLayout){
37948             this.layout();
37949         }
37950     },
37951
37952     layout: function(){
37953         // abstract...
37954     },
37955
37956     onRegionResized : function(region, newSize){
37957         this.fireEvent("regionresized", region, newSize);
37958         this.layout();
37959     },
37960
37961     onRegionCollapsed : function(region){
37962         this.fireEvent("regioncollapsed", region);
37963     },
37964
37965     onRegionExpanded : function(region){
37966         this.fireEvent("regionexpanded", region);
37967     },
37968
37969     /**
37970      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37971      * performs box-model adjustments.
37972      * @return {Object} The size as an object {width: (the width), height: (the height)}
37973      */
37974     getViewSize : function()
37975     {
37976         var size;
37977         if(this.el.dom != document.body){
37978             size = this.el.getSize();
37979         }else{
37980             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37981         }
37982         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37983         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37984         return size;
37985     },
37986
37987     /**
37988      * Returns the Element this layout is bound to.
37989      * @return {Roo.Element}
37990      */
37991     getEl : function(){
37992         return this.el;
37993     },
37994
37995     /**
37996      * Returns the specified region.
37997      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37998      * @return {Roo.LayoutRegion}
37999      */
38000     getRegion : function(target){
38001         return this.regions[target.toLowerCase()];
38002     },
38003
38004     onWindowResize : function(){
38005         if(this.monitorWindowResize){
38006             this.layout();
38007         }
38008     }
38009 });
38010 /*
38011  * Based on:
38012  * Ext JS Library 1.1.1
38013  * Copyright(c) 2006-2007, Ext JS, LLC.
38014  *
38015  * Originally Released Under LGPL - original licence link has changed is not relivant.
38016  *
38017  * Fork - LGPL
38018  * <script type="text/javascript">
38019  */
38020 /**
38021  * @class Roo.bootstrap.layout.Border
38022  * @extends Roo.bootstrap.layout.Manager
38023  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38024  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38025  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38026  * please see: examples/bootstrap/nested.html<br><br>
38027  
38028 <b>The container the layout is rendered into can be either the body element or any other element.
38029 If it is not the body element, the container needs to either be an absolute positioned element,
38030 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38031 the container size if it is not the body element.</b>
38032
38033 * @constructor
38034 * Create a new Border
38035 * @param {Object} config Configuration options
38036  */
38037 Roo.bootstrap.layout.Border = function(config){
38038     config = config || {};
38039     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38040     
38041     
38042     
38043     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38044         if(config[region]){
38045             config[region].region = region;
38046             this.addRegion(config[region]);
38047         }
38048     },this);
38049     
38050 };
38051
38052 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38053
38054 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38055     
38056         /**
38057          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38058          */
38059         /**
38060          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38061          */
38062         /**
38063          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38064          */
38065         /**
38066          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38067          */
38068         /**
38069          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38070          */
38071         
38072         
38073         
38074         
38075     parent : false, // this might point to a 'nest' or a ???
38076     
38077     /**
38078      * Creates and adds a new region if it doesn't already exist.
38079      * @param {String} target The target region key (north, south, east, west or center).
38080      * @param {Object} config The regions config object
38081      * @return {BorderLayoutRegion} The new region
38082      */
38083     addRegion : function(config)
38084     {
38085         if(!this.regions[config.region]){
38086             var r = this.factory(config);
38087             this.bindRegion(r);
38088         }
38089         return this.regions[config.region];
38090     },
38091
38092     // private (kinda)
38093     bindRegion : function(r){
38094         this.regions[r.config.region] = r;
38095         
38096         r.on("visibilitychange",    this.layout, this);
38097         r.on("paneladded",          this.layout, this);
38098         r.on("panelremoved",        this.layout, this);
38099         r.on("invalidated",         this.layout, this);
38100         r.on("resized",             this.onRegionResized, this);
38101         r.on("collapsed",           this.onRegionCollapsed, this);
38102         r.on("expanded",            this.onRegionExpanded, this);
38103     },
38104
38105     /**
38106      * Performs a layout update.
38107      */
38108     layout : function()
38109     {
38110         if(this.updating) {
38111             return;
38112         }
38113         
38114         // render all the rebions if they have not been done alreayd?
38115         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38116             if(this.regions[region] && !this.regions[region].bodyEl){
38117                 this.regions[region].onRender(this.el)
38118             }
38119         },this);
38120         
38121         var size = this.getViewSize();
38122         var w = size.width;
38123         var h = size.height;
38124         var centerW = w;
38125         var centerH = h;
38126         var centerY = 0;
38127         var centerX = 0;
38128         //var x = 0, y = 0;
38129
38130         var rs = this.regions;
38131         var north = rs["north"];
38132         var south = rs["south"]; 
38133         var west = rs["west"];
38134         var east = rs["east"];
38135         var center = rs["center"];
38136         //if(this.hideOnLayout){ // not supported anymore
38137             //c.el.setStyle("display", "none");
38138         //}
38139         if(north && north.isVisible()){
38140             var b = north.getBox();
38141             var m = north.getMargins();
38142             b.width = w - (m.left+m.right);
38143             b.x = m.left;
38144             b.y = m.top;
38145             centerY = b.height + b.y + m.bottom;
38146             centerH -= centerY;
38147             north.updateBox(this.safeBox(b));
38148         }
38149         if(south && south.isVisible()){
38150             var b = south.getBox();
38151             var m = south.getMargins();
38152             b.width = w - (m.left+m.right);
38153             b.x = m.left;
38154             var totalHeight = (b.height + m.top + m.bottom);
38155             b.y = h - totalHeight + m.top;
38156             centerH -= totalHeight;
38157             south.updateBox(this.safeBox(b));
38158         }
38159         if(west && west.isVisible()){
38160             var b = west.getBox();
38161             var m = west.getMargins();
38162             b.height = centerH - (m.top+m.bottom);
38163             b.x = m.left;
38164             b.y = centerY + m.top;
38165             var totalWidth = (b.width + m.left + m.right);
38166             centerX += totalWidth;
38167             centerW -= totalWidth;
38168             west.updateBox(this.safeBox(b));
38169         }
38170         if(east && east.isVisible()){
38171             var b = east.getBox();
38172             var m = east.getMargins();
38173             b.height = centerH - (m.top+m.bottom);
38174             var totalWidth = (b.width + m.left + m.right);
38175             b.x = w - totalWidth + m.left;
38176             b.y = centerY + m.top;
38177             centerW -= totalWidth;
38178             east.updateBox(this.safeBox(b));
38179         }
38180         if(center){
38181             var m = center.getMargins();
38182             var centerBox = {
38183                 x: centerX + m.left,
38184                 y: centerY + m.top,
38185                 width: centerW - (m.left+m.right),
38186                 height: centerH - (m.top+m.bottom)
38187             };
38188             //if(this.hideOnLayout){
38189                 //center.el.setStyle("display", "block");
38190             //}
38191             center.updateBox(this.safeBox(centerBox));
38192         }
38193         this.el.repaint();
38194         this.fireEvent("layout", this);
38195     },
38196
38197     // private
38198     safeBox : function(box){
38199         box.width = Math.max(0, box.width);
38200         box.height = Math.max(0, box.height);
38201         return box;
38202     },
38203
38204     /**
38205      * Adds a ContentPanel (or subclass) to this layout.
38206      * @param {String} target The target region key (north, south, east, west or center).
38207      * @param {Roo.ContentPanel} panel The panel to add
38208      * @return {Roo.ContentPanel} The added panel
38209      */
38210     add : function(target, panel){
38211          
38212         target = target.toLowerCase();
38213         return this.regions[target].add(panel);
38214     },
38215
38216     /**
38217      * Remove a ContentPanel (or subclass) to this layout.
38218      * @param {String} target The target region key (north, south, east, west or center).
38219      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38220      * @return {Roo.ContentPanel} The removed panel
38221      */
38222     remove : function(target, panel){
38223         target = target.toLowerCase();
38224         return this.regions[target].remove(panel);
38225     },
38226
38227     /**
38228      * Searches all regions for a panel with the specified id
38229      * @param {String} panelId
38230      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38231      */
38232     findPanel : function(panelId){
38233         var rs = this.regions;
38234         for(var target in rs){
38235             if(typeof rs[target] != "function"){
38236                 var p = rs[target].getPanel(panelId);
38237                 if(p){
38238                     return p;
38239                 }
38240             }
38241         }
38242         return null;
38243     },
38244
38245     /**
38246      * Searches all regions for a panel with the specified id and activates (shows) it.
38247      * @param {String/ContentPanel} panelId The panels id or the panel itself
38248      * @return {Roo.ContentPanel} The shown panel or null
38249      */
38250     showPanel : function(panelId) {
38251       var rs = this.regions;
38252       for(var target in rs){
38253          var r = rs[target];
38254          if(typeof r != "function"){
38255             if(r.hasPanel(panelId)){
38256                return r.showPanel(panelId);
38257             }
38258          }
38259       }
38260       return null;
38261    },
38262
38263    /**
38264      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38265      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38266      */
38267    /*
38268     restoreState : function(provider){
38269         if(!provider){
38270             provider = Roo.state.Manager;
38271         }
38272         var sm = new Roo.LayoutStateManager();
38273         sm.init(this, provider);
38274     },
38275 */
38276  
38277  
38278     /**
38279      * Adds a xtype elements to the layout.
38280      * <pre><code>
38281
38282 layout.addxtype({
38283        xtype : 'ContentPanel',
38284        region: 'west',
38285        items: [ .... ]
38286    }
38287 );
38288
38289 layout.addxtype({
38290         xtype : 'NestedLayoutPanel',
38291         region: 'west',
38292         layout: {
38293            center: { },
38294            west: { }   
38295         },
38296         items : [ ... list of content panels or nested layout panels.. ]
38297    }
38298 );
38299 </code></pre>
38300      * @param {Object} cfg Xtype definition of item to add.
38301      */
38302     addxtype : function(cfg)
38303     {
38304         // basically accepts a pannel...
38305         // can accept a layout region..!?!?
38306         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38307         
38308         
38309         // theory?  children can only be panels??
38310         
38311         //if (!cfg.xtype.match(/Panel$/)) {
38312         //    return false;
38313         //}
38314         var ret = false;
38315         
38316         if (typeof(cfg.region) == 'undefined') {
38317             Roo.log("Failed to add Panel, region was not set");
38318             Roo.log(cfg);
38319             return false;
38320         }
38321         var region = cfg.region;
38322         delete cfg.region;
38323         
38324           
38325         var xitems = [];
38326         if (cfg.items) {
38327             xitems = cfg.items;
38328             delete cfg.items;
38329         }
38330         var nb = false;
38331         
38332         if ( region == 'center') {
38333             Roo.log("Center: " + cfg.title);
38334         }
38335         
38336         
38337         switch(cfg.xtype) 
38338         {
38339             case 'Content':  // ContentPanel (el, cfg)
38340             case 'Scroll':  // ContentPanel (el, cfg)
38341             case 'View': 
38342                 cfg.autoCreate = cfg.autoCreate || true;
38343                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38344                 //} else {
38345                 //    var el = this.el.createChild();
38346                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38347                 //}
38348                 
38349                 this.add(region, ret);
38350                 break;
38351             
38352             /*
38353             case 'TreePanel': // our new panel!
38354                 cfg.el = this.el.createChild();
38355                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38356                 this.add(region, ret);
38357                 break;
38358             */
38359             
38360             case 'Nest': 
38361                 // create a new Layout (which is  a Border Layout...
38362                 
38363                 var clayout = cfg.layout;
38364                 clayout.el  = this.el.createChild();
38365                 clayout.items   = clayout.items  || [];
38366                 
38367                 delete cfg.layout;
38368                 
38369                 // replace this exitems with the clayout ones..
38370                 xitems = clayout.items;
38371                  
38372                 // force background off if it's in center...
38373                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38374                     cfg.background = false;
38375                 }
38376                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38377                 
38378                 
38379                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38380                 //console.log('adding nested layout panel '  + cfg.toSource());
38381                 this.add(region, ret);
38382                 nb = {}; /// find first...
38383                 break;
38384             
38385             case 'Grid':
38386                 
38387                 // needs grid and region
38388                 
38389                 //var el = this.getRegion(region).el.createChild();
38390                 /*
38391                  *var el = this.el.createChild();
38392                 // create the grid first...
38393                 cfg.grid.container = el;
38394                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38395                 */
38396                 
38397                 if (region == 'center' && this.active ) {
38398                     cfg.background = false;
38399                 }
38400                 
38401                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38402                 
38403                 this.add(region, ret);
38404                 /*
38405                 if (cfg.background) {
38406                     // render grid on panel activation (if panel background)
38407                     ret.on('activate', function(gp) {
38408                         if (!gp.grid.rendered) {
38409                     //        gp.grid.render(el);
38410                         }
38411                     });
38412                 } else {
38413                   //  cfg.grid.render(el);
38414                 }
38415                 */
38416                 break;
38417            
38418            
38419             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38420                 // it was the old xcomponent building that caused this before.
38421                 // espeically if border is the top element in the tree.
38422                 ret = this;
38423                 break; 
38424                 
38425                     
38426                 
38427                 
38428                 
38429             default:
38430                 /*
38431                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38432                     
38433                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38434                     this.add(region, ret);
38435                 } else {
38436                 */
38437                     Roo.log(cfg);
38438                     throw "Can not add '" + cfg.xtype + "' to Border";
38439                     return null;
38440              
38441                                 
38442              
38443         }
38444         this.beginUpdate();
38445         // add children..
38446         var region = '';
38447         var abn = {};
38448         Roo.each(xitems, function(i)  {
38449             region = nb && i.region ? i.region : false;
38450             
38451             var add = ret.addxtype(i);
38452            
38453             if (region) {
38454                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38455                 if (!i.background) {
38456                     abn[region] = nb[region] ;
38457                 }
38458             }
38459             
38460         });
38461         this.endUpdate();
38462
38463         // make the last non-background panel active..
38464         //if (nb) { Roo.log(abn); }
38465         if (nb) {
38466             
38467             for(var r in abn) {
38468                 region = this.getRegion(r);
38469                 if (region) {
38470                     // tried using nb[r], but it does not work..
38471                      
38472                     region.showPanel(abn[r]);
38473                    
38474                 }
38475             }
38476         }
38477         return ret;
38478         
38479     },
38480     
38481     
38482 // private
38483     factory : function(cfg)
38484     {
38485         
38486         var validRegions = Roo.bootstrap.layout.Border.regions;
38487
38488         var target = cfg.region;
38489         cfg.mgr = this;
38490         
38491         var r = Roo.bootstrap.layout;
38492         Roo.log(target);
38493         switch(target){
38494             case "north":
38495                 return new r.North(cfg);
38496             case "south":
38497                 return new r.South(cfg);
38498             case "east":
38499                 return new r.East(cfg);
38500             case "west":
38501                 return new r.West(cfg);
38502             case "center":
38503                 return new r.Center(cfg);
38504         }
38505         throw 'Layout region "'+target+'" not supported.';
38506     }
38507     
38508     
38509 });
38510  /*
38511  * Based on:
38512  * Ext JS Library 1.1.1
38513  * Copyright(c) 2006-2007, Ext JS, LLC.
38514  *
38515  * Originally Released Under LGPL - original licence link has changed is not relivant.
38516  *
38517  * Fork - LGPL
38518  * <script type="text/javascript">
38519  */
38520  
38521 /**
38522  * @class Roo.bootstrap.layout.Basic
38523  * @extends Roo.util.Observable
38524  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38525  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38526  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38527  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38528  * @cfg {string}   region  the region that it inhabits..
38529  * @cfg {bool}   skipConfig skip config?
38530  * 
38531
38532  */
38533 Roo.bootstrap.layout.Basic = function(config){
38534     
38535     this.mgr = config.mgr;
38536     
38537     this.position = config.region;
38538     
38539     var skipConfig = config.skipConfig;
38540     
38541     this.events = {
38542         /**
38543          * @scope Roo.BasicLayoutRegion
38544          */
38545         
38546         /**
38547          * @event beforeremove
38548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38549          * @param {Roo.LayoutRegion} this
38550          * @param {Roo.ContentPanel} panel The panel
38551          * @param {Object} e The cancel event object
38552          */
38553         "beforeremove" : true,
38554         /**
38555          * @event invalidated
38556          * Fires when the layout for this region is changed.
38557          * @param {Roo.LayoutRegion} this
38558          */
38559         "invalidated" : true,
38560         /**
38561          * @event visibilitychange
38562          * Fires when this region is shown or hidden 
38563          * @param {Roo.LayoutRegion} this
38564          * @param {Boolean} visibility true or false
38565          */
38566         "visibilitychange" : true,
38567         /**
38568          * @event paneladded
38569          * Fires when a panel is added. 
38570          * @param {Roo.LayoutRegion} this
38571          * @param {Roo.ContentPanel} panel The panel
38572          */
38573         "paneladded" : true,
38574         /**
38575          * @event panelremoved
38576          * Fires when a panel is removed. 
38577          * @param {Roo.LayoutRegion} this
38578          * @param {Roo.ContentPanel} panel The panel
38579          */
38580         "panelremoved" : true,
38581         /**
38582          * @event beforecollapse
38583          * Fires when this region before collapse.
38584          * @param {Roo.LayoutRegion} this
38585          */
38586         "beforecollapse" : true,
38587         /**
38588          * @event collapsed
38589          * Fires when this region is collapsed.
38590          * @param {Roo.LayoutRegion} this
38591          */
38592         "collapsed" : true,
38593         /**
38594          * @event expanded
38595          * Fires when this region is expanded.
38596          * @param {Roo.LayoutRegion} this
38597          */
38598         "expanded" : true,
38599         /**
38600          * @event slideshow
38601          * Fires when this region is slid into view.
38602          * @param {Roo.LayoutRegion} this
38603          */
38604         "slideshow" : true,
38605         /**
38606          * @event slidehide
38607          * Fires when this region slides out of view. 
38608          * @param {Roo.LayoutRegion} this
38609          */
38610         "slidehide" : true,
38611         /**
38612          * @event panelactivated
38613          * Fires when a panel is activated. 
38614          * @param {Roo.LayoutRegion} this
38615          * @param {Roo.ContentPanel} panel The activated panel
38616          */
38617         "panelactivated" : true,
38618         /**
38619          * @event resized
38620          * Fires when the user resizes this region. 
38621          * @param {Roo.LayoutRegion} this
38622          * @param {Number} newSize The new size (width for east/west, height for north/south)
38623          */
38624         "resized" : true
38625     };
38626     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38627     this.panels = new Roo.util.MixedCollection();
38628     this.panels.getKey = this.getPanelId.createDelegate(this);
38629     this.box = null;
38630     this.activePanel = null;
38631     // ensure listeners are added...
38632     
38633     if (config.listeners || config.events) {
38634         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38635             listeners : config.listeners || {},
38636             events : config.events || {}
38637         });
38638     }
38639     
38640     if(skipConfig !== true){
38641         this.applyConfig(config);
38642     }
38643 };
38644
38645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38646 {
38647     getPanelId : function(p){
38648         return p.getId();
38649     },
38650     
38651     applyConfig : function(config){
38652         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38653         this.config = config;
38654         
38655     },
38656     
38657     /**
38658      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38659      * the width, for horizontal (north, south) the height.
38660      * @param {Number} newSize The new width or height
38661      */
38662     resizeTo : function(newSize){
38663         var el = this.el ? this.el :
38664                  (this.activePanel ? this.activePanel.getEl() : null);
38665         if(el){
38666             switch(this.position){
38667                 case "east":
38668                 case "west":
38669                     el.setWidth(newSize);
38670                     this.fireEvent("resized", this, newSize);
38671                 break;
38672                 case "north":
38673                 case "south":
38674                     el.setHeight(newSize);
38675                     this.fireEvent("resized", this, newSize);
38676                 break;                
38677             }
38678         }
38679     },
38680     
38681     getBox : function(){
38682         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38683     },
38684     
38685     getMargins : function(){
38686         return this.margins;
38687     },
38688     
38689     updateBox : function(box){
38690         this.box = box;
38691         var el = this.activePanel.getEl();
38692         el.dom.style.left = box.x + "px";
38693         el.dom.style.top = box.y + "px";
38694         this.activePanel.setSize(box.width, box.height);
38695     },
38696     
38697     /**
38698      * Returns the container element for this region.
38699      * @return {Roo.Element}
38700      */
38701     getEl : function(){
38702         return this.activePanel;
38703     },
38704     
38705     /**
38706      * Returns true if this region is currently visible.
38707      * @return {Boolean}
38708      */
38709     isVisible : function(){
38710         return this.activePanel ? true : false;
38711     },
38712     
38713     setActivePanel : function(panel){
38714         panel = this.getPanel(panel);
38715         if(this.activePanel && this.activePanel != panel){
38716             this.activePanel.setActiveState(false);
38717             this.activePanel.getEl().setLeftTop(-10000,-10000);
38718         }
38719         this.activePanel = panel;
38720         panel.setActiveState(true);
38721         if(this.box){
38722             panel.setSize(this.box.width, this.box.height);
38723         }
38724         this.fireEvent("panelactivated", this, panel);
38725         this.fireEvent("invalidated");
38726     },
38727     
38728     /**
38729      * Show the specified panel.
38730      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38731      * @return {Roo.ContentPanel} The shown panel or null
38732      */
38733     showPanel : function(panel){
38734         panel = this.getPanel(panel);
38735         if(panel){
38736             this.setActivePanel(panel);
38737         }
38738         return panel;
38739     },
38740     
38741     /**
38742      * Get the active panel for this region.
38743      * @return {Roo.ContentPanel} The active panel or null
38744      */
38745     getActivePanel : function(){
38746         return this.activePanel;
38747     },
38748     
38749     /**
38750      * Add the passed ContentPanel(s)
38751      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38752      * @return {Roo.ContentPanel} The panel added (if only one was added)
38753      */
38754     add : function(panel){
38755         if(arguments.length > 1){
38756             for(var i = 0, len = arguments.length; i < len; i++) {
38757                 this.add(arguments[i]);
38758             }
38759             return null;
38760         }
38761         if(this.hasPanel(panel)){
38762             this.showPanel(panel);
38763             return panel;
38764         }
38765         var el = panel.getEl();
38766         if(el.dom.parentNode != this.mgr.el.dom){
38767             this.mgr.el.dom.appendChild(el.dom);
38768         }
38769         if(panel.setRegion){
38770             panel.setRegion(this);
38771         }
38772         this.panels.add(panel);
38773         el.setStyle("position", "absolute");
38774         if(!panel.background){
38775             this.setActivePanel(panel);
38776             if(this.config.initialSize && this.panels.getCount()==1){
38777                 this.resizeTo(this.config.initialSize);
38778             }
38779         }
38780         this.fireEvent("paneladded", this, panel);
38781         return panel;
38782     },
38783     
38784     /**
38785      * Returns true if the panel is in this region.
38786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38787      * @return {Boolean}
38788      */
38789     hasPanel : function(panel){
38790         if(typeof panel == "object"){ // must be panel obj
38791             panel = panel.getId();
38792         }
38793         return this.getPanel(panel) ? true : false;
38794     },
38795     
38796     /**
38797      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38799      * @param {Boolean} preservePanel Overrides the config preservePanel option
38800      * @return {Roo.ContentPanel} The panel that was removed
38801      */
38802     remove : function(panel, preservePanel){
38803         panel = this.getPanel(panel);
38804         if(!panel){
38805             return null;
38806         }
38807         var e = {};
38808         this.fireEvent("beforeremove", this, panel, e);
38809         if(e.cancel === true){
38810             return null;
38811         }
38812         var panelId = panel.getId();
38813         this.panels.removeKey(panelId);
38814         return panel;
38815     },
38816     
38817     /**
38818      * Returns the panel specified or null if it's not in this region.
38819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820      * @return {Roo.ContentPanel}
38821      */
38822     getPanel : function(id){
38823         if(typeof id == "object"){ // must be panel obj
38824             return id;
38825         }
38826         return this.panels.get(id);
38827     },
38828     
38829     /**
38830      * Returns this regions position (north/south/east/west/center).
38831      * @return {String} 
38832      */
38833     getPosition: function(){
38834         return this.position;    
38835     }
38836 });/*
38837  * Based on:
38838  * Ext JS Library 1.1.1
38839  * Copyright(c) 2006-2007, Ext JS, LLC.
38840  *
38841  * Originally Released Under LGPL - original licence link has changed is not relivant.
38842  *
38843  * Fork - LGPL
38844  * <script type="text/javascript">
38845  */
38846  
38847 /**
38848  * @class Roo.bootstrap.layout.Region
38849  * @extends Roo.bootstrap.layout.Basic
38850  * This class represents a region in a layout manager.
38851  
38852  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38853  * @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})
38854  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38855  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38856  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38857  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38858  * @cfg {String}    title           The title for the region (overrides panel titles)
38859  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38860  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38861  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38862  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38863  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38864  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38865  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38866  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38867  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38868  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38869
38870  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38871  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38872  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38873  * @cfg {Number}    width           For East/West panels
38874  * @cfg {Number}    height          For North/South panels
38875  * @cfg {Boolean}   split           To show the splitter
38876  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38877  * 
38878  * @cfg {string}   cls             Extra CSS classes to add to region
38879  * 
38880  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38881  * @cfg {string}   region  the region that it inhabits..
38882  *
38883
38884  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38885  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38886
38887  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38888  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38889  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38890  */
38891 Roo.bootstrap.layout.Region = function(config)
38892 {
38893     this.applyConfig(config);
38894
38895     var mgr = config.mgr;
38896     var pos = config.region;
38897     config.skipConfig = true;
38898     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38899     
38900     if (mgr.el) {
38901         this.onRender(mgr.el);   
38902     }
38903      
38904     this.visible = true;
38905     this.collapsed = false;
38906     this.unrendered_panels = [];
38907 };
38908
38909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38910
38911     position: '', // set by wrapper (eg. north/south etc..)
38912     unrendered_panels : null,  // unrendered panels.
38913     
38914     tabPosition : false,
38915     
38916     mgr: false, // points to 'Border'
38917     
38918     
38919     createBody : function(){
38920         /** This region's body element 
38921         * @type Roo.Element */
38922         this.bodyEl = this.el.createChild({
38923                 tag: "div",
38924                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38925         });
38926     },
38927
38928     onRender: function(ctr, pos)
38929     {
38930         var dh = Roo.DomHelper;
38931         /** This region's container element 
38932         * @type Roo.Element */
38933         this.el = dh.append(ctr.dom, {
38934                 tag: "div",
38935                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38936             }, true);
38937         /** This region's title element 
38938         * @type Roo.Element */
38939     
38940         this.titleEl = dh.append(this.el.dom,  {
38941                 tag: "div",
38942                 unselectable: "on",
38943                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38944                 children:[
38945                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38946                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38947                 ]
38948             }, true);
38949         
38950         this.titleEl.enableDisplayMode();
38951         /** This region's title text element 
38952         * @type HTMLElement */
38953         this.titleTextEl = this.titleEl.dom.firstChild;
38954         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38955         /*
38956         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38957         this.closeBtn.enableDisplayMode();
38958         this.closeBtn.on("click", this.closeClicked, this);
38959         this.closeBtn.hide();
38960     */
38961         this.createBody(this.config);
38962         if(this.config.hideWhenEmpty){
38963             this.hide();
38964             this.on("paneladded", this.validateVisibility, this);
38965             this.on("panelremoved", this.validateVisibility, this);
38966         }
38967         if(this.autoScroll){
38968             this.bodyEl.setStyle("overflow", "auto");
38969         }else{
38970             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38971         }
38972         //if(c.titlebar !== false){
38973             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38974                 this.titleEl.hide();
38975             }else{
38976                 this.titleEl.show();
38977                 if(this.config.title){
38978                     this.titleTextEl.innerHTML = this.config.title;
38979                 }
38980             }
38981         //}
38982         if(this.config.collapsed){
38983             this.collapse(true);
38984         }
38985         if(this.config.hidden){
38986             this.hide();
38987         }
38988         
38989         if (this.unrendered_panels && this.unrendered_panels.length) {
38990             for (var i =0;i< this.unrendered_panels.length; i++) {
38991                 this.add(this.unrendered_panels[i]);
38992             }
38993             this.unrendered_panels = null;
38994             
38995         }
38996         
38997     },
38998     
38999     applyConfig : function(c)
39000     {
39001         /*
39002          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39003             var dh = Roo.DomHelper;
39004             if(c.titlebar !== false){
39005                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39006                 this.collapseBtn.on("click", this.collapse, this);
39007                 this.collapseBtn.enableDisplayMode();
39008                 /*
39009                 if(c.showPin === true || this.showPin){
39010                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39011                     this.stickBtn.enableDisplayMode();
39012                     this.stickBtn.on("click", this.expand, this);
39013                     this.stickBtn.hide();
39014                 }
39015                 
39016             }
39017             */
39018             /** This region's collapsed element
39019             * @type Roo.Element */
39020             /*
39021              *
39022             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39023                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39024             ]}, true);
39025             
39026             if(c.floatable !== false){
39027                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39028                this.collapsedEl.on("click", this.collapseClick, this);
39029             }
39030
39031             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39032                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39033                    id: "message", unselectable: "on", style:{"float":"left"}});
39034                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39035              }
39036             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39037             this.expandBtn.on("click", this.expand, this);
39038             
39039         }
39040         
39041         if(this.collapseBtn){
39042             this.collapseBtn.setVisible(c.collapsible == true);
39043         }
39044         
39045         this.cmargins = c.cmargins || this.cmargins ||
39046                          (this.position == "west" || this.position == "east" ?
39047                              {top: 0, left: 2, right:2, bottom: 0} :
39048                              {top: 2, left: 0, right:0, bottom: 2});
39049         */
39050         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39051         
39052         
39053         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39054         
39055         this.autoScroll = c.autoScroll || false;
39056         
39057         
39058        
39059         
39060         this.duration = c.duration || .30;
39061         this.slideDuration = c.slideDuration || .45;
39062         this.config = c;
39063        
39064     },
39065     /**
39066      * Returns true if this region is currently visible.
39067      * @return {Boolean}
39068      */
39069     isVisible : function(){
39070         return this.visible;
39071     },
39072
39073     /**
39074      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39075      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39076      */
39077     //setCollapsedTitle : function(title){
39078     //    title = title || "&#160;";
39079      //   if(this.collapsedTitleTextEl){
39080       //      this.collapsedTitleTextEl.innerHTML = title;
39081        // }
39082     //},
39083
39084     getBox : function(){
39085         var b;
39086       //  if(!this.collapsed){
39087             b = this.el.getBox(false, true);
39088        // }else{
39089           //  b = this.collapsedEl.getBox(false, true);
39090         //}
39091         return b;
39092     },
39093
39094     getMargins : function(){
39095         return this.margins;
39096         //return this.collapsed ? this.cmargins : this.margins;
39097     },
39098 /*
39099     highlight : function(){
39100         this.el.addClass("x-layout-panel-dragover");
39101     },
39102
39103     unhighlight : function(){
39104         this.el.removeClass("x-layout-panel-dragover");
39105     },
39106 */
39107     updateBox : function(box)
39108     {
39109         if (!this.bodyEl) {
39110             return; // not rendered yet..
39111         }
39112         
39113         this.box = box;
39114         if(!this.collapsed){
39115             this.el.dom.style.left = box.x + "px";
39116             this.el.dom.style.top = box.y + "px";
39117             this.updateBody(box.width, box.height);
39118         }else{
39119             this.collapsedEl.dom.style.left = box.x + "px";
39120             this.collapsedEl.dom.style.top = box.y + "px";
39121             this.collapsedEl.setSize(box.width, box.height);
39122         }
39123         if(this.tabs){
39124             this.tabs.autoSizeTabs();
39125         }
39126     },
39127
39128     updateBody : function(w, h)
39129     {
39130         if(w !== null){
39131             this.el.setWidth(w);
39132             w -= this.el.getBorderWidth("rl");
39133             if(this.config.adjustments){
39134                 w += this.config.adjustments[0];
39135             }
39136         }
39137         if(h !== null && h > 0){
39138             this.el.setHeight(h);
39139             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39140             h -= this.el.getBorderWidth("tb");
39141             if(this.config.adjustments){
39142                 h += this.config.adjustments[1];
39143             }
39144             this.bodyEl.setHeight(h);
39145             if(this.tabs){
39146                 h = this.tabs.syncHeight(h);
39147             }
39148         }
39149         if(this.panelSize){
39150             w = w !== null ? w : this.panelSize.width;
39151             h = h !== null ? h : this.panelSize.height;
39152         }
39153         if(this.activePanel){
39154             var el = this.activePanel.getEl();
39155             w = w !== null ? w : el.getWidth();
39156             h = h !== null ? h : el.getHeight();
39157             this.panelSize = {width: w, height: h};
39158             this.activePanel.setSize(w, h);
39159         }
39160         if(Roo.isIE && this.tabs){
39161             this.tabs.el.repaint();
39162         }
39163     },
39164
39165     /**
39166      * Returns the container element for this region.
39167      * @return {Roo.Element}
39168      */
39169     getEl : function(){
39170         return this.el;
39171     },
39172
39173     /**
39174      * Hides this region.
39175      */
39176     hide : function(){
39177         //if(!this.collapsed){
39178             this.el.dom.style.left = "-2000px";
39179             this.el.hide();
39180         //}else{
39181          //   this.collapsedEl.dom.style.left = "-2000px";
39182          //   this.collapsedEl.hide();
39183        // }
39184         this.visible = false;
39185         this.fireEvent("visibilitychange", this, false);
39186     },
39187
39188     /**
39189      * Shows this region if it was previously hidden.
39190      */
39191     show : function(){
39192         //if(!this.collapsed){
39193             this.el.show();
39194         //}else{
39195         //    this.collapsedEl.show();
39196        // }
39197         this.visible = true;
39198         this.fireEvent("visibilitychange", this, true);
39199     },
39200 /*
39201     closeClicked : function(){
39202         if(this.activePanel){
39203             this.remove(this.activePanel);
39204         }
39205     },
39206
39207     collapseClick : function(e){
39208         if(this.isSlid){
39209            e.stopPropagation();
39210            this.slideIn();
39211         }else{
39212            e.stopPropagation();
39213            this.slideOut();
39214         }
39215     },
39216 */
39217     /**
39218      * Collapses this region.
39219      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39220      */
39221     /*
39222     collapse : function(skipAnim, skipCheck = false){
39223         if(this.collapsed) {
39224             return;
39225         }
39226         
39227         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39228             
39229             this.collapsed = true;
39230             if(this.split){
39231                 this.split.el.hide();
39232             }
39233             if(this.config.animate && skipAnim !== true){
39234                 this.fireEvent("invalidated", this);
39235                 this.animateCollapse();
39236             }else{
39237                 this.el.setLocation(-20000,-20000);
39238                 this.el.hide();
39239                 this.collapsedEl.show();
39240                 this.fireEvent("collapsed", this);
39241                 this.fireEvent("invalidated", this);
39242             }
39243         }
39244         
39245     },
39246 */
39247     animateCollapse : function(){
39248         // overridden
39249     },
39250
39251     /**
39252      * Expands this region if it was previously collapsed.
39253      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39254      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39255      */
39256     /*
39257     expand : function(e, skipAnim){
39258         if(e) {
39259             e.stopPropagation();
39260         }
39261         if(!this.collapsed || this.el.hasActiveFx()) {
39262             return;
39263         }
39264         if(this.isSlid){
39265             this.afterSlideIn();
39266             skipAnim = true;
39267         }
39268         this.collapsed = false;
39269         if(this.config.animate && skipAnim !== true){
39270             this.animateExpand();
39271         }else{
39272             this.el.show();
39273             if(this.split){
39274                 this.split.el.show();
39275             }
39276             this.collapsedEl.setLocation(-2000,-2000);
39277             this.collapsedEl.hide();
39278             this.fireEvent("invalidated", this);
39279             this.fireEvent("expanded", this);
39280         }
39281     },
39282 */
39283     animateExpand : function(){
39284         // overridden
39285     },
39286
39287     initTabs : function()
39288     {
39289         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39290         
39291         var ts = new Roo.bootstrap.panel.Tabs({
39292             el: this.bodyEl.dom,
39293             region : this,
39294             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39295             disableTooltips: this.config.disableTabTips,
39296             toolbar : this.config.toolbar
39297         });
39298         
39299         if(this.config.hideTabs){
39300             ts.stripWrap.setDisplayed(false);
39301         }
39302         this.tabs = ts;
39303         ts.resizeTabs = this.config.resizeTabs === true;
39304         ts.minTabWidth = this.config.minTabWidth || 40;
39305         ts.maxTabWidth = this.config.maxTabWidth || 250;
39306         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39307         ts.monitorResize = false;
39308         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39309         ts.bodyEl.addClass('roo-layout-tabs-body');
39310         this.panels.each(this.initPanelAsTab, this);
39311     },
39312
39313     initPanelAsTab : function(panel){
39314         var ti = this.tabs.addTab(
39315             panel.getEl().id,
39316             panel.getTitle(),
39317             null,
39318             this.config.closeOnTab && panel.isClosable(),
39319             panel.tpl
39320         );
39321         if(panel.tabTip !== undefined){
39322             ti.setTooltip(panel.tabTip);
39323         }
39324         ti.on("activate", function(){
39325               this.setActivePanel(panel);
39326         }, this);
39327         
39328         if(this.config.closeOnTab){
39329             ti.on("beforeclose", function(t, e){
39330                 e.cancel = true;
39331                 this.remove(panel);
39332             }, this);
39333         }
39334         
39335         panel.tabItem = ti;
39336         
39337         return ti;
39338     },
39339
39340     updatePanelTitle : function(panel, title)
39341     {
39342         if(this.activePanel == panel){
39343             this.updateTitle(title);
39344         }
39345         if(this.tabs){
39346             var ti = this.tabs.getTab(panel.getEl().id);
39347             ti.setText(title);
39348             if(panel.tabTip !== undefined){
39349                 ti.setTooltip(panel.tabTip);
39350             }
39351         }
39352     },
39353
39354     updateTitle : function(title){
39355         if(this.titleTextEl && !this.config.title){
39356             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39357         }
39358     },
39359
39360     setActivePanel : function(panel)
39361     {
39362         panel = this.getPanel(panel);
39363         if(this.activePanel && this.activePanel != panel){
39364             if(this.activePanel.setActiveState(false) === false){
39365                 return;
39366             }
39367         }
39368         this.activePanel = panel;
39369         panel.setActiveState(true);
39370         if(this.panelSize){
39371             panel.setSize(this.panelSize.width, this.panelSize.height);
39372         }
39373         if(this.closeBtn){
39374             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39375         }
39376         this.updateTitle(panel.getTitle());
39377         if(this.tabs){
39378             this.fireEvent("invalidated", this);
39379         }
39380         this.fireEvent("panelactivated", this, panel);
39381     },
39382
39383     /**
39384      * Shows the specified panel.
39385      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39386      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39387      */
39388     showPanel : function(panel)
39389     {
39390         panel = this.getPanel(panel);
39391         if(panel){
39392             if(this.tabs){
39393                 var tab = this.tabs.getTab(panel.getEl().id);
39394                 if(tab.isHidden()){
39395                     this.tabs.unhideTab(tab.id);
39396                 }
39397                 tab.activate();
39398             }else{
39399                 this.setActivePanel(panel);
39400             }
39401         }
39402         return panel;
39403     },
39404
39405     /**
39406      * Get the active panel for this region.
39407      * @return {Roo.ContentPanel} The active panel or null
39408      */
39409     getActivePanel : function(){
39410         return this.activePanel;
39411     },
39412
39413     validateVisibility : function(){
39414         if(this.panels.getCount() < 1){
39415             this.updateTitle("&#160;");
39416             this.closeBtn.hide();
39417             this.hide();
39418         }else{
39419             if(!this.isVisible()){
39420                 this.show();
39421             }
39422         }
39423     },
39424
39425     /**
39426      * Adds the passed ContentPanel(s) to this region.
39427      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39428      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39429      */
39430     add : function(panel)
39431     {
39432         if(arguments.length > 1){
39433             for(var i = 0, len = arguments.length; i < len; i++) {
39434                 this.add(arguments[i]);
39435             }
39436             return null;
39437         }
39438         
39439         // if we have not been rendered yet, then we can not really do much of this..
39440         if (!this.bodyEl) {
39441             this.unrendered_panels.push(panel);
39442             return panel;
39443         }
39444         
39445         
39446         
39447         
39448         if(this.hasPanel(panel)){
39449             this.showPanel(panel);
39450             return panel;
39451         }
39452         panel.setRegion(this);
39453         this.panels.add(panel);
39454        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39455             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39456             // and hide them... ???
39457             this.bodyEl.dom.appendChild(panel.getEl().dom);
39458             if(panel.background !== true){
39459                 this.setActivePanel(panel);
39460             }
39461             this.fireEvent("paneladded", this, panel);
39462             return panel;
39463         }
39464         */
39465         if(!this.tabs){
39466             this.initTabs();
39467         }else{
39468             this.initPanelAsTab(panel);
39469         }
39470         
39471         
39472         if(panel.background !== true){
39473             this.tabs.activate(panel.getEl().id);
39474         }
39475         this.fireEvent("paneladded", this, panel);
39476         return panel;
39477     },
39478
39479     /**
39480      * Hides the tab for the specified panel.
39481      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39482      */
39483     hidePanel : function(panel){
39484         if(this.tabs && (panel = this.getPanel(panel))){
39485             this.tabs.hideTab(panel.getEl().id);
39486         }
39487     },
39488
39489     /**
39490      * Unhides the tab for a previously hidden panel.
39491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492      */
39493     unhidePanel : function(panel){
39494         if(this.tabs && (panel = this.getPanel(panel))){
39495             this.tabs.unhideTab(panel.getEl().id);
39496         }
39497     },
39498
39499     clearPanels : function(){
39500         while(this.panels.getCount() > 0){
39501              this.remove(this.panels.first());
39502         }
39503     },
39504
39505     /**
39506      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39507      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508      * @param {Boolean} preservePanel Overrides the config preservePanel option
39509      * @return {Roo.ContentPanel} The panel that was removed
39510      */
39511     remove : function(panel, preservePanel)
39512     {
39513         panel = this.getPanel(panel);
39514         if(!panel){
39515             return null;
39516         }
39517         var e = {};
39518         this.fireEvent("beforeremove", this, panel, e);
39519         if(e.cancel === true){
39520             return null;
39521         }
39522         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39523         var panelId = panel.getId();
39524         this.panels.removeKey(panelId);
39525         if(preservePanel){
39526             document.body.appendChild(panel.getEl().dom);
39527         }
39528         if(this.tabs){
39529             this.tabs.removeTab(panel.getEl().id);
39530         }else if (!preservePanel){
39531             this.bodyEl.dom.removeChild(panel.getEl().dom);
39532         }
39533         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39534             var p = this.panels.first();
39535             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39536             tempEl.appendChild(p.getEl().dom);
39537             this.bodyEl.update("");
39538             this.bodyEl.dom.appendChild(p.getEl().dom);
39539             tempEl = null;
39540             this.updateTitle(p.getTitle());
39541             this.tabs = null;
39542             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39543             this.setActivePanel(p);
39544         }
39545         panel.setRegion(null);
39546         if(this.activePanel == panel){
39547             this.activePanel = null;
39548         }
39549         if(this.config.autoDestroy !== false && preservePanel !== true){
39550             try{panel.destroy();}catch(e){}
39551         }
39552         this.fireEvent("panelremoved", this, panel);
39553         return panel;
39554     },
39555
39556     /**
39557      * Returns the TabPanel component used by this region
39558      * @return {Roo.TabPanel}
39559      */
39560     getTabs : function(){
39561         return this.tabs;
39562     },
39563
39564     createTool : function(parentEl, className){
39565         var btn = Roo.DomHelper.append(parentEl, {
39566             tag: "div",
39567             cls: "x-layout-tools-button",
39568             children: [ {
39569                 tag: "div",
39570                 cls: "roo-layout-tools-button-inner " + className,
39571                 html: "&#160;"
39572             }]
39573         }, true);
39574         btn.addClassOnOver("roo-layout-tools-button-over");
39575         return btn;
39576     }
39577 });/*
39578  * Based on:
39579  * Ext JS Library 1.1.1
39580  * Copyright(c) 2006-2007, Ext JS, LLC.
39581  *
39582  * Originally Released Under LGPL - original licence link has changed is not relivant.
39583  *
39584  * Fork - LGPL
39585  * <script type="text/javascript">
39586  */
39587  
39588
39589
39590 /**
39591  * @class Roo.SplitLayoutRegion
39592  * @extends Roo.LayoutRegion
39593  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39594  */
39595 Roo.bootstrap.layout.Split = function(config){
39596     this.cursor = config.cursor;
39597     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39598 };
39599
39600 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39601 {
39602     splitTip : "Drag to resize.",
39603     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39604     useSplitTips : false,
39605
39606     applyConfig : function(config){
39607         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39608     },
39609     
39610     onRender : function(ctr,pos) {
39611         
39612         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39613         if(!this.config.split){
39614             return;
39615         }
39616         if(!this.split){
39617             
39618             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39619                             tag: "div",
39620                             id: this.el.id + "-split",
39621                             cls: "roo-layout-split roo-layout-split-"+this.position,
39622                             html: "&#160;"
39623             });
39624             /** The SplitBar for this region 
39625             * @type Roo.SplitBar */
39626             // does not exist yet...
39627             Roo.log([this.position, this.orientation]);
39628             
39629             this.split = new Roo.bootstrap.SplitBar({
39630                 dragElement : splitEl,
39631                 resizingElement: this.el,
39632                 orientation : this.orientation
39633             });
39634             
39635             this.split.on("moved", this.onSplitMove, this);
39636             this.split.useShim = this.config.useShim === true;
39637             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39638             if(this.useSplitTips){
39639                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39640             }
39641             //if(config.collapsible){
39642             //    this.split.el.on("dblclick", this.collapse,  this);
39643             //}
39644         }
39645         if(typeof this.config.minSize != "undefined"){
39646             this.split.minSize = this.config.minSize;
39647         }
39648         if(typeof this.config.maxSize != "undefined"){
39649             this.split.maxSize = this.config.maxSize;
39650         }
39651         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39652             this.hideSplitter();
39653         }
39654         
39655     },
39656
39657     getHMaxSize : function(){
39658          var cmax = this.config.maxSize || 10000;
39659          var center = this.mgr.getRegion("center");
39660          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39661     },
39662
39663     getVMaxSize : function(){
39664          var cmax = this.config.maxSize || 10000;
39665          var center = this.mgr.getRegion("center");
39666          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39667     },
39668
39669     onSplitMove : function(split, newSize){
39670         this.fireEvent("resized", this, newSize);
39671     },
39672     
39673     /** 
39674      * Returns the {@link Roo.SplitBar} for this region.
39675      * @return {Roo.SplitBar}
39676      */
39677     getSplitBar : function(){
39678         return this.split;
39679     },
39680     
39681     hide : function(){
39682         this.hideSplitter();
39683         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39684     },
39685
39686     hideSplitter : function(){
39687         if(this.split){
39688             this.split.el.setLocation(-2000,-2000);
39689             this.split.el.hide();
39690         }
39691     },
39692
39693     show : function(){
39694         if(this.split){
39695             this.split.el.show();
39696         }
39697         Roo.bootstrap.layout.Split.superclass.show.call(this);
39698     },
39699     
39700     beforeSlide: function(){
39701         if(Roo.isGecko){// firefox overflow auto bug workaround
39702             this.bodyEl.clip();
39703             if(this.tabs) {
39704                 this.tabs.bodyEl.clip();
39705             }
39706             if(this.activePanel){
39707                 this.activePanel.getEl().clip();
39708                 
39709                 if(this.activePanel.beforeSlide){
39710                     this.activePanel.beforeSlide();
39711                 }
39712             }
39713         }
39714     },
39715     
39716     afterSlide : function(){
39717         if(Roo.isGecko){// firefox overflow auto bug workaround
39718             this.bodyEl.unclip();
39719             if(this.tabs) {
39720                 this.tabs.bodyEl.unclip();
39721             }
39722             if(this.activePanel){
39723                 this.activePanel.getEl().unclip();
39724                 if(this.activePanel.afterSlide){
39725                     this.activePanel.afterSlide();
39726                 }
39727             }
39728         }
39729     },
39730
39731     initAutoHide : function(){
39732         if(this.autoHide !== false){
39733             if(!this.autoHideHd){
39734                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39735                 this.autoHideHd = {
39736                     "mouseout": function(e){
39737                         if(!e.within(this.el, true)){
39738                             st.delay(500);
39739                         }
39740                     },
39741                     "mouseover" : function(e){
39742                         st.cancel();
39743                     },
39744                     scope : this
39745                 };
39746             }
39747             this.el.on(this.autoHideHd);
39748         }
39749     },
39750
39751     clearAutoHide : function(){
39752         if(this.autoHide !== false){
39753             this.el.un("mouseout", this.autoHideHd.mouseout);
39754             this.el.un("mouseover", this.autoHideHd.mouseover);
39755         }
39756     },
39757
39758     clearMonitor : function(){
39759         Roo.get(document).un("click", this.slideInIf, this);
39760     },
39761
39762     // these names are backwards but not changed for compat
39763     slideOut : function(){
39764         if(this.isSlid || this.el.hasActiveFx()){
39765             return;
39766         }
39767         this.isSlid = true;
39768         if(this.collapseBtn){
39769             this.collapseBtn.hide();
39770         }
39771         this.closeBtnState = this.closeBtn.getStyle('display');
39772         this.closeBtn.hide();
39773         if(this.stickBtn){
39774             this.stickBtn.show();
39775         }
39776         this.el.show();
39777         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39778         this.beforeSlide();
39779         this.el.setStyle("z-index", 10001);
39780         this.el.slideIn(this.getSlideAnchor(), {
39781             callback: function(){
39782                 this.afterSlide();
39783                 this.initAutoHide();
39784                 Roo.get(document).on("click", this.slideInIf, this);
39785                 this.fireEvent("slideshow", this);
39786             },
39787             scope: this,
39788             block: true
39789         });
39790     },
39791
39792     afterSlideIn : function(){
39793         this.clearAutoHide();
39794         this.isSlid = false;
39795         this.clearMonitor();
39796         this.el.setStyle("z-index", "");
39797         if(this.collapseBtn){
39798             this.collapseBtn.show();
39799         }
39800         this.closeBtn.setStyle('display', this.closeBtnState);
39801         if(this.stickBtn){
39802             this.stickBtn.hide();
39803         }
39804         this.fireEvent("slidehide", this);
39805     },
39806
39807     slideIn : function(cb){
39808         if(!this.isSlid || this.el.hasActiveFx()){
39809             Roo.callback(cb);
39810             return;
39811         }
39812         this.isSlid = false;
39813         this.beforeSlide();
39814         this.el.slideOut(this.getSlideAnchor(), {
39815             callback: function(){
39816                 this.el.setLeftTop(-10000, -10000);
39817                 this.afterSlide();
39818                 this.afterSlideIn();
39819                 Roo.callback(cb);
39820             },
39821             scope: this,
39822             block: true
39823         });
39824     },
39825     
39826     slideInIf : function(e){
39827         if(!e.within(this.el)){
39828             this.slideIn();
39829         }
39830     },
39831
39832     animateCollapse : function(){
39833         this.beforeSlide();
39834         this.el.setStyle("z-index", 20000);
39835         var anchor = this.getSlideAnchor();
39836         this.el.slideOut(anchor, {
39837             callback : function(){
39838                 this.el.setStyle("z-index", "");
39839                 this.collapsedEl.slideIn(anchor, {duration:.3});
39840                 this.afterSlide();
39841                 this.el.setLocation(-10000,-10000);
39842                 this.el.hide();
39843                 this.fireEvent("collapsed", this);
39844             },
39845             scope: this,
39846             block: true
39847         });
39848     },
39849
39850     animateExpand : function(){
39851         this.beforeSlide();
39852         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39853         this.el.setStyle("z-index", 20000);
39854         this.collapsedEl.hide({
39855             duration:.1
39856         });
39857         this.el.slideIn(this.getSlideAnchor(), {
39858             callback : function(){
39859                 this.el.setStyle("z-index", "");
39860                 this.afterSlide();
39861                 if(this.split){
39862                     this.split.el.show();
39863                 }
39864                 this.fireEvent("invalidated", this);
39865                 this.fireEvent("expanded", this);
39866             },
39867             scope: this,
39868             block: true
39869         });
39870     },
39871
39872     anchors : {
39873         "west" : "left",
39874         "east" : "right",
39875         "north" : "top",
39876         "south" : "bottom"
39877     },
39878
39879     sanchors : {
39880         "west" : "l",
39881         "east" : "r",
39882         "north" : "t",
39883         "south" : "b"
39884     },
39885
39886     canchors : {
39887         "west" : "tl-tr",
39888         "east" : "tr-tl",
39889         "north" : "tl-bl",
39890         "south" : "bl-tl"
39891     },
39892
39893     getAnchor : function(){
39894         return this.anchors[this.position];
39895     },
39896
39897     getCollapseAnchor : function(){
39898         return this.canchors[this.position];
39899     },
39900
39901     getSlideAnchor : function(){
39902         return this.sanchors[this.position];
39903     },
39904
39905     getAlignAdj : function(){
39906         var cm = this.cmargins;
39907         switch(this.position){
39908             case "west":
39909                 return [0, 0];
39910             break;
39911             case "east":
39912                 return [0, 0];
39913             break;
39914             case "north":
39915                 return [0, 0];
39916             break;
39917             case "south":
39918                 return [0, 0];
39919             break;
39920         }
39921     },
39922
39923     getExpandAdj : function(){
39924         var c = this.collapsedEl, cm = this.cmargins;
39925         switch(this.position){
39926             case "west":
39927                 return [-(cm.right+c.getWidth()+cm.left), 0];
39928             break;
39929             case "east":
39930                 return [cm.right+c.getWidth()+cm.left, 0];
39931             break;
39932             case "north":
39933                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39934             break;
39935             case "south":
39936                 return [0, cm.top+cm.bottom+c.getHeight()];
39937             break;
39938         }
39939     }
39940 });/*
39941  * Based on:
39942  * Ext JS Library 1.1.1
39943  * Copyright(c) 2006-2007, Ext JS, LLC.
39944  *
39945  * Originally Released Under LGPL - original licence link has changed is not relivant.
39946  *
39947  * Fork - LGPL
39948  * <script type="text/javascript">
39949  */
39950 /*
39951  * These classes are private internal classes
39952  */
39953 Roo.bootstrap.layout.Center = function(config){
39954     config.region = "center";
39955     Roo.bootstrap.layout.Region.call(this, config);
39956     this.visible = true;
39957     this.minWidth = config.minWidth || 20;
39958     this.minHeight = config.minHeight || 20;
39959 };
39960
39961 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39962     hide : function(){
39963         // center panel can't be hidden
39964     },
39965     
39966     show : function(){
39967         // center panel can't be hidden
39968     },
39969     
39970     getMinWidth: function(){
39971         return this.minWidth;
39972     },
39973     
39974     getMinHeight: function(){
39975         return this.minHeight;
39976     }
39977 });
39978
39979
39980
39981
39982  
39983
39984
39985
39986
39987
39988
39989 Roo.bootstrap.layout.North = function(config)
39990 {
39991     config.region = 'north';
39992     config.cursor = 'n-resize';
39993     
39994     Roo.bootstrap.layout.Split.call(this, config);
39995     
39996     
39997     if(this.split){
39998         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39999         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40000         this.split.el.addClass("roo-layout-split-v");
40001     }
40002     //var size = config.initialSize || config.height;
40003     //if(this.el && typeof size != "undefined"){
40004     //    this.el.setHeight(size);
40005     //}
40006 };
40007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40008 {
40009     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40010      
40011      
40012     onRender : function(ctr, pos)
40013     {
40014         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40015         var size = this.config.initialSize || this.config.height;
40016         if(this.el && typeof size != "undefined"){
40017             this.el.setHeight(size);
40018         }
40019     
40020     },
40021     
40022     getBox : function(){
40023         if(this.collapsed){
40024             return this.collapsedEl.getBox();
40025         }
40026         var box = this.el.getBox();
40027         if(this.split){
40028             box.height += this.split.el.getHeight();
40029         }
40030         return box;
40031     },
40032     
40033     updateBox : function(box){
40034         if(this.split && !this.collapsed){
40035             box.height -= this.split.el.getHeight();
40036             this.split.el.setLeft(box.x);
40037             this.split.el.setTop(box.y+box.height);
40038             this.split.el.setWidth(box.width);
40039         }
40040         if(this.collapsed){
40041             this.updateBody(box.width, null);
40042         }
40043         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40044     }
40045 });
40046
40047
40048
40049
40050
40051 Roo.bootstrap.layout.South = function(config){
40052     config.region = 'south';
40053     config.cursor = 's-resize';
40054     Roo.bootstrap.layout.Split.call(this, config);
40055     if(this.split){
40056         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40057         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40058         this.split.el.addClass("roo-layout-split-v");
40059     }
40060     
40061 };
40062
40063 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40064     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40065     
40066     onRender : function(ctr, pos)
40067     {
40068         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40069         var size = this.config.initialSize || this.config.height;
40070         if(this.el && typeof size != "undefined"){
40071             this.el.setHeight(size);
40072         }
40073     
40074     },
40075     
40076     getBox : function(){
40077         if(this.collapsed){
40078             return this.collapsedEl.getBox();
40079         }
40080         var box = this.el.getBox();
40081         if(this.split){
40082             var sh = this.split.el.getHeight();
40083             box.height += sh;
40084             box.y -= sh;
40085         }
40086         return box;
40087     },
40088     
40089     updateBox : function(box){
40090         if(this.split && !this.collapsed){
40091             var sh = this.split.el.getHeight();
40092             box.height -= sh;
40093             box.y += sh;
40094             this.split.el.setLeft(box.x);
40095             this.split.el.setTop(box.y-sh);
40096             this.split.el.setWidth(box.width);
40097         }
40098         if(this.collapsed){
40099             this.updateBody(box.width, null);
40100         }
40101         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40102     }
40103 });
40104
40105 Roo.bootstrap.layout.East = function(config){
40106     config.region = "east";
40107     config.cursor = "e-resize";
40108     Roo.bootstrap.layout.Split.call(this, config);
40109     if(this.split){
40110         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40111         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40112         this.split.el.addClass("roo-layout-split-h");
40113     }
40114     
40115 };
40116 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40117     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40118     
40119     onRender : function(ctr, pos)
40120     {
40121         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40122         var size = this.config.initialSize || this.config.width;
40123         if(this.el && typeof size != "undefined"){
40124             this.el.setWidth(size);
40125         }
40126     
40127     },
40128     
40129     getBox : function(){
40130         if(this.collapsed){
40131             return this.collapsedEl.getBox();
40132         }
40133         var box = this.el.getBox();
40134         if(this.split){
40135             var sw = this.split.el.getWidth();
40136             box.width += sw;
40137             box.x -= sw;
40138         }
40139         return box;
40140     },
40141
40142     updateBox : function(box){
40143         if(this.split && !this.collapsed){
40144             var sw = this.split.el.getWidth();
40145             box.width -= sw;
40146             this.split.el.setLeft(box.x);
40147             this.split.el.setTop(box.y);
40148             this.split.el.setHeight(box.height);
40149             box.x += sw;
40150         }
40151         if(this.collapsed){
40152             this.updateBody(null, box.height);
40153         }
40154         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40155     }
40156 });
40157
40158 Roo.bootstrap.layout.West = function(config){
40159     config.region = "west";
40160     config.cursor = "w-resize";
40161     
40162     Roo.bootstrap.layout.Split.call(this, config);
40163     if(this.split){
40164         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40165         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40166         this.split.el.addClass("roo-layout-split-h");
40167     }
40168     
40169 };
40170 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40171     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40172     
40173     onRender: function(ctr, pos)
40174     {
40175         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40176         var size = this.config.initialSize || this.config.width;
40177         if(typeof size != "undefined"){
40178             this.el.setWidth(size);
40179         }
40180     },
40181     
40182     getBox : function(){
40183         if(this.collapsed){
40184             return this.collapsedEl.getBox();
40185         }
40186         var box = this.el.getBox();
40187         if (box.width == 0) {
40188             box.width = this.config.width; // kludge?
40189         }
40190         if(this.split){
40191             box.width += this.split.el.getWidth();
40192         }
40193         return box;
40194     },
40195     
40196     updateBox : function(box){
40197         if(this.split && !this.collapsed){
40198             var sw = this.split.el.getWidth();
40199             box.width -= sw;
40200             this.split.el.setLeft(box.x+box.width);
40201             this.split.el.setTop(box.y);
40202             this.split.el.setHeight(box.height);
40203         }
40204         if(this.collapsed){
40205             this.updateBody(null, box.height);
40206         }
40207         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40208     }
40209 });/*
40210  * Based on:
40211  * Ext JS Library 1.1.1
40212  * Copyright(c) 2006-2007, Ext JS, LLC.
40213  *
40214  * Originally Released Under LGPL - original licence link has changed is not relivant.
40215  *
40216  * Fork - LGPL
40217  * <script type="text/javascript">
40218  */
40219 /**
40220  * @class Roo.bootstrap.paenl.Content
40221  * @extends Roo.util.Observable
40222  * @children Roo.bootstrap.Component
40223  * @parent builder Roo.bootstrap.layout.Border
40224  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40225  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40226  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40227  * @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
40228  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40229  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40230  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40231  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40232  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40233  * @cfg {String} title          The title for this panel
40234  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40235  * @cfg {String} url            Calls {@link #setUrl} with this value
40236  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40237  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40238  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40239  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40240  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40241  * @cfg {Boolean} badges render the badges
40242  * @cfg {String} cls  extra classes to use  
40243  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40244  
40245  * @constructor
40246  * Create a new ContentPanel.
40247  * @param {String/Object} config A string to set only the title or a config object
40248  
40249  */
40250 Roo.bootstrap.panel.Content = function( config){
40251     
40252     this.tpl = config.tpl || false;
40253     
40254     var el = config.el;
40255     var content = config.content;
40256
40257     if(config.autoCreate){ // xtype is available if this is called from factory
40258         el = Roo.id();
40259     }
40260     this.el = Roo.get(el);
40261     if(!this.el && config && config.autoCreate){
40262         if(typeof config.autoCreate == "object"){
40263             if(!config.autoCreate.id){
40264                 config.autoCreate.id = config.id||el;
40265             }
40266             this.el = Roo.DomHelper.append(document.body,
40267                         config.autoCreate, true);
40268         }else{
40269             var elcfg =  {
40270                 tag: "div",
40271                 cls: (config.cls || '') +
40272                     (config.background ? ' bg-' + config.background : '') +
40273                     " roo-layout-inactive-content",
40274                 id: config.id||el
40275             };
40276             if (config.iframe) {
40277                 elcfg.cn = [
40278                     {
40279                         tag : 'iframe',
40280                         style : 'border: 0px',
40281                         src : 'about:blank'
40282                     }
40283                 ];
40284             }
40285               
40286             if (config.html) {
40287                 elcfg.html = config.html;
40288                 
40289             }
40290                         
40291             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40292             if (config.iframe) {
40293                 this.iframeEl = this.el.select('iframe',true).first();
40294             }
40295             
40296         }
40297     } 
40298     this.closable = false;
40299     this.loaded = false;
40300     this.active = false;
40301    
40302       
40303     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40304         
40305         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40306         
40307         this.wrapEl = this.el; //this.el.wrap();
40308         var ti = [];
40309         if (config.toolbar.items) {
40310             ti = config.toolbar.items ;
40311             delete config.toolbar.items ;
40312         }
40313         
40314         var nitems = [];
40315         this.toolbar.render(this.wrapEl, 'before');
40316         for(var i =0;i < ti.length;i++) {
40317           //  Roo.log(['add child', items[i]]);
40318             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40319         }
40320         this.toolbar.items = nitems;
40321         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40322         delete config.toolbar;
40323         
40324     }
40325     /*
40326     // xtype created footer. - not sure if will work as we normally have to render first..
40327     if (this.footer && !this.footer.el && this.footer.xtype) {
40328         if (!this.wrapEl) {
40329             this.wrapEl = this.el.wrap();
40330         }
40331     
40332         this.footer.container = this.wrapEl.createChild();
40333          
40334         this.footer = Roo.factory(this.footer, Roo);
40335         
40336     }
40337     */
40338     
40339      if(typeof config == "string"){
40340         this.title = config;
40341     }else{
40342         Roo.apply(this, config);
40343     }
40344     
40345     if(this.resizeEl){
40346         this.resizeEl = Roo.get(this.resizeEl, true);
40347     }else{
40348         this.resizeEl = this.el;
40349     }
40350     // handle view.xtype
40351     
40352  
40353     
40354     
40355     this.addEvents({
40356         /**
40357          * @event activate
40358          * Fires when this panel is activated. 
40359          * @param {Roo.ContentPanel} this
40360          */
40361         "activate" : true,
40362         /**
40363          * @event deactivate
40364          * Fires when this panel is activated. 
40365          * @param {Roo.ContentPanel} this
40366          */
40367         "deactivate" : true,
40368
40369         /**
40370          * @event resize
40371          * Fires when this panel is resized if fitToFrame is true.
40372          * @param {Roo.ContentPanel} this
40373          * @param {Number} width The width after any component adjustments
40374          * @param {Number} height The height after any component adjustments
40375          */
40376         "resize" : true,
40377         
40378          /**
40379          * @event render
40380          * Fires when this tab is created
40381          * @param {Roo.ContentPanel} this
40382          */
40383         "render" : true,
40384         
40385           /**
40386          * @event scroll
40387          * Fires when this content is scrolled
40388          * @param {Roo.ContentPanel} this
40389          * @param {Event} scrollEvent
40390          */
40391         "scroll" : true
40392         
40393         
40394         
40395     });
40396     
40397
40398     
40399     
40400     if(this.autoScroll && !this.iframe){
40401         this.resizeEl.setStyle("overflow", "auto");
40402         this.resizeEl.on('scroll', this.onScroll, this);
40403     } else {
40404         // fix randome scrolling
40405         //this.el.on('scroll', function() {
40406         //    Roo.log('fix random scolling');
40407         //    this.scrollTo('top',0); 
40408         //});
40409     }
40410     content = content || this.content;
40411     if(content){
40412         this.setContent(content);
40413     }
40414     if(config && config.url){
40415         this.setUrl(this.url, this.params, this.loadOnce);
40416     }
40417     
40418     
40419     
40420     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40421     
40422     if (this.view && typeof(this.view.xtype) != 'undefined') {
40423         this.view.el = this.el.appendChild(document.createElement("div"));
40424         this.view = Roo.factory(this.view); 
40425         this.view.render  &&  this.view.render(false, '');  
40426     }
40427     
40428     
40429     this.fireEvent('render', this);
40430 };
40431
40432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40433     
40434     cls : '',
40435     background : '',
40436     
40437     tabTip : '',
40438     
40439     iframe : false,
40440     iframeEl : false,
40441     
40442     /* Resize Element - use this to work out scroll etc. */
40443     resizeEl : false,
40444     
40445     setRegion : function(region){
40446         this.region = region;
40447         this.setActiveClass(region && !this.background);
40448     },
40449     
40450     
40451     setActiveClass: function(state)
40452     {
40453         if(state){
40454            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40455            this.el.setStyle('position','relative');
40456         }else{
40457            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40458            this.el.setStyle('position', 'absolute');
40459         } 
40460     },
40461     
40462     /**
40463      * Returns the toolbar for this Panel if one was configured. 
40464      * @return {Roo.Toolbar} 
40465      */
40466     getToolbar : function(){
40467         return this.toolbar;
40468     },
40469     
40470     setActiveState : function(active)
40471     {
40472         this.active = active;
40473         this.setActiveClass(active);
40474         if(!active){
40475             if(this.fireEvent("deactivate", this) === false){
40476                 return false;
40477             }
40478             return true;
40479         }
40480         this.fireEvent("activate", this);
40481         return true;
40482     },
40483     /**
40484      * Updates this panel's element (not for iframe)
40485      * @param {String} content The new content
40486      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40487     */
40488     setContent : function(content, loadScripts){
40489         if (this.iframe) {
40490             return;
40491         }
40492         
40493         this.el.update(content, loadScripts);
40494     },
40495
40496     ignoreResize : function(w, h){
40497         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40498             return true;
40499         }else{
40500             this.lastSize = {width: w, height: h};
40501             return false;
40502         }
40503     },
40504     /**
40505      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40506      * @return {Roo.UpdateManager} The UpdateManager
40507      */
40508     getUpdateManager : function(){
40509         if (this.iframe) {
40510             return false;
40511         }
40512         return this.el.getUpdateManager();
40513     },
40514      /**
40515      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40516      * Does not work with IFRAME contents
40517      * @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:
40518 <pre><code>
40519 panel.load({
40520     url: "your-url.php",
40521     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40522     callback: yourFunction,
40523     scope: yourObject, //(optional scope)
40524     discardUrl: false,
40525     nocache: false,
40526     text: "Loading...",
40527     timeout: 30,
40528     scripts: false
40529 });
40530 </code></pre>
40531      
40532      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40533      * 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.
40534      * @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}
40535      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40536      * @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.
40537      * @return {Roo.ContentPanel} this
40538      */
40539     load : function(){
40540         
40541         if (this.iframe) {
40542             return this;
40543         }
40544         
40545         var um = this.el.getUpdateManager();
40546         um.update.apply(um, arguments);
40547         return this;
40548     },
40549
40550
40551     /**
40552      * 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.
40553      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40554      * @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)
40555      * @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)
40556      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40557      */
40558     setUrl : function(url, params, loadOnce){
40559         if (this.iframe) {
40560             this.iframeEl.dom.src = url;
40561             return false;
40562         }
40563         
40564         if(this.refreshDelegate){
40565             this.removeListener("activate", this.refreshDelegate);
40566         }
40567         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40568         this.on("activate", this.refreshDelegate);
40569         return this.el.getUpdateManager();
40570     },
40571     
40572     _handleRefresh : function(url, params, loadOnce){
40573         if(!loadOnce || !this.loaded){
40574             var updater = this.el.getUpdateManager();
40575             updater.update(url, params, this._setLoaded.createDelegate(this));
40576         }
40577     },
40578     
40579     _setLoaded : function(){
40580         this.loaded = true;
40581     }, 
40582     
40583     /**
40584      * Returns this panel's id
40585      * @return {String} 
40586      */
40587     getId : function(){
40588         return this.el.id;
40589     },
40590     
40591     /** 
40592      * Returns this panel's element - used by regiosn to add.
40593      * @return {Roo.Element} 
40594      */
40595     getEl : function(){
40596         return this.wrapEl || this.el;
40597     },
40598     
40599    
40600     
40601     adjustForComponents : function(width, height)
40602     {
40603         //Roo.log('adjustForComponents ');
40604         if(this.resizeEl != this.el){
40605             width -= this.el.getFrameWidth('lr');
40606             height -= this.el.getFrameWidth('tb');
40607         }
40608         if(this.toolbar){
40609             var te = this.toolbar.getEl();
40610             te.setWidth(width);
40611             height -= te.getHeight();
40612         }
40613         if(this.footer){
40614             var te = this.footer.getEl();
40615             te.setWidth(width);
40616             height -= te.getHeight();
40617         }
40618         
40619         
40620         if(this.adjustments){
40621             width += this.adjustments[0];
40622             height += this.adjustments[1];
40623         }
40624         return {"width": width, "height": height};
40625     },
40626     
40627     setSize : function(width, height){
40628         if(this.fitToFrame && !this.ignoreResize(width, height)){
40629             if(this.fitContainer && this.resizeEl != this.el){
40630                 this.el.setSize(width, height);
40631             }
40632             var size = this.adjustForComponents(width, height);
40633             if (this.iframe) {
40634                 this.iframeEl.setSize(width,height);
40635             }
40636             
40637             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40638             this.fireEvent('resize', this, size.width, size.height);
40639             
40640             
40641         }
40642     },
40643     
40644     /**
40645      * Returns this panel's title
40646      * @return {String} 
40647      */
40648     getTitle : function(){
40649         
40650         if (typeof(this.title) != 'object') {
40651             return this.title;
40652         }
40653         
40654         var t = '';
40655         for (var k in this.title) {
40656             if (!this.title.hasOwnProperty(k)) {
40657                 continue;
40658             }
40659             
40660             if (k.indexOf('-') >= 0) {
40661                 var s = k.split('-');
40662                 for (var i = 0; i<s.length; i++) {
40663                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40664                 }
40665             } else {
40666                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40667             }
40668         }
40669         return t;
40670     },
40671     
40672     /**
40673      * Set this panel's title
40674      * @param {String} title
40675      */
40676     setTitle : function(title){
40677         this.title = title;
40678         if(this.region){
40679             this.region.updatePanelTitle(this, title);
40680         }
40681     },
40682     
40683     /**
40684      * Returns true is this panel was configured to be closable
40685      * @return {Boolean} 
40686      */
40687     isClosable : function(){
40688         return this.closable;
40689     },
40690     
40691     beforeSlide : function(){
40692         this.el.clip();
40693         this.resizeEl.clip();
40694     },
40695     
40696     afterSlide : function(){
40697         this.el.unclip();
40698         this.resizeEl.unclip();
40699     },
40700     
40701     /**
40702      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40703      *   Will fail silently if the {@link #setUrl} method has not been called.
40704      *   This does not activate the panel, just updates its content.
40705      */
40706     refresh : function(){
40707         if(this.refreshDelegate){
40708            this.loaded = false;
40709            this.refreshDelegate();
40710         }
40711     },
40712     
40713     /**
40714      * Destroys this panel
40715      */
40716     destroy : function(){
40717         this.el.removeAllListeners();
40718         var tempEl = document.createElement("span");
40719         tempEl.appendChild(this.el.dom);
40720         tempEl.innerHTML = "";
40721         this.el.remove();
40722         this.el = null;
40723     },
40724     
40725     /**
40726      * form - if the content panel contains a form - this is a reference to it.
40727      * @type {Roo.form.Form}
40728      */
40729     form : false,
40730     /**
40731      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40732      *    This contains a reference to it.
40733      * @type {Roo.View}
40734      */
40735     view : false,
40736     
40737       /**
40738      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40739      * <pre><code>
40740
40741 layout.addxtype({
40742        xtype : 'Form',
40743        items: [ .... ]
40744    }
40745 );
40746
40747 </code></pre>
40748      * @param {Object} cfg Xtype definition of item to add.
40749      */
40750     
40751     
40752     getChildContainer: function () {
40753         return this.getEl();
40754     },
40755     
40756     
40757     onScroll : function(e)
40758     {
40759         this.fireEvent('scroll', this, e);
40760     }
40761     
40762     
40763     /*
40764         var  ret = new Roo.factory(cfg);
40765         return ret;
40766         
40767         
40768         // add form..
40769         if (cfg.xtype.match(/^Form$/)) {
40770             
40771             var el;
40772             //if (this.footer) {
40773             //    el = this.footer.container.insertSibling(false, 'before');
40774             //} else {
40775                 el = this.el.createChild();
40776             //}
40777
40778             this.form = new  Roo.form.Form(cfg);
40779             
40780             
40781             if ( this.form.allItems.length) {
40782                 this.form.render(el.dom);
40783             }
40784             return this.form;
40785         }
40786         // should only have one of theses..
40787         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40788             // views.. should not be just added - used named prop 'view''
40789             
40790             cfg.el = this.el.appendChild(document.createElement("div"));
40791             // factory?
40792             
40793             var ret = new Roo.factory(cfg);
40794              
40795              ret.render && ret.render(false, ''); // render blank..
40796             this.view = ret;
40797             return ret;
40798         }
40799         return false;
40800     }
40801     \*/
40802 });
40803  
40804 /**
40805  * @class Roo.bootstrap.panel.Grid
40806  * @extends Roo.bootstrap.panel.Content
40807  * @constructor
40808  * Create a new GridPanel.
40809  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40810  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40811  * @param {Object} config A the config object
40812   
40813  */
40814
40815
40816
40817 Roo.bootstrap.panel.Grid = function(config)
40818 {
40819     
40820       
40821     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40822         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40823
40824     config.el = this.wrapper;
40825     //this.el = this.wrapper;
40826     
40827       if (config.container) {
40828         // ctor'ed from a Border/panel.grid
40829         
40830         
40831         this.wrapper.setStyle("overflow", "hidden");
40832         this.wrapper.addClass('roo-grid-container');
40833
40834     }
40835     
40836     
40837     if(config.toolbar){
40838         var tool_el = this.wrapper.createChild();    
40839         this.toolbar = Roo.factory(config.toolbar);
40840         var ti = [];
40841         if (config.toolbar.items) {
40842             ti = config.toolbar.items ;
40843             delete config.toolbar.items ;
40844         }
40845         
40846         var nitems = [];
40847         this.toolbar.render(tool_el);
40848         for(var i =0;i < ti.length;i++) {
40849           //  Roo.log(['add child', items[i]]);
40850             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40851         }
40852         this.toolbar.items = nitems;
40853         
40854         delete config.toolbar;
40855     }
40856     
40857     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40858     config.grid.scrollBody = true;;
40859     config.grid.monitorWindowResize = false; // turn off autosizing
40860     config.grid.autoHeight = false;
40861     config.grid.autoWidth = false;
40862     
40863     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40864     
40865     if (config.background) {
40866         // render grid on panel activation (if panel background)
40867         this.on('activate', function(gp) {
40868             if (!gp.grid.rendered) {
40869                 gp.grid.render(this.wrapper);
40870                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40871             }
40872         });
40873             
40874     } else {
40875         this.grid.render(this.wrapper);
40876         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40877
40878     }
40879     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40880     // ??? needed ??? config.el = this.wrapper;
40881     
40882     
40883     
40884   
40885     // xtype created footer. - not sure if will work as we normally have to render first..
40886     if (this.footer && !this.footer.el && this.footer.xtype) {
40887         
40888         var ctr = this.grid.getView().getFooterPanel(true);
40889         this.footer.dataSource = this.grid.dataSource;
40890         this.footer = Roo.factory(this.footer, Roo);
40891         this.footer.render(ctr);
40892         
40893     }
40894     
40895     
40896     
40897     
40898      
40899 };
40900
40901 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40902     getId : function(){
40903         return this.grid.id;
40904     },
40905     
40906     /**
40907      * Returns the grid for this panel
40908      * @return {Roo.bootstrap.Table} 
40909      */
40910     getGrid : function(){
40911         return this.grid;    
40912     },
40913     
40914     setSize : function(width, height){
40915         if(!this.ignoreResize(width, height)){
40916             var grid = this.grid;
40917             var size = this.adjustForComponents(width, height);
40918             // tfoot is not a footer?
40919           
40920             
40921             var gridel = grid.getGridEl();
40922             gridel.setSize(size.width, size.height);
40923             
40924             var tbd = grid.getGridEl().select('tbody', true).first();
40925             var thd = grid.getGridEl().select('thead',true).first();
40926             var tbf= grid.getGridEl().select('tfoot', true).first();
40927
40928             if (tbf) {
40929                 size.height -= tbf.getHeight();
40930             }
40931             if (thd) {
40932                 size.height -= thd.getHeight();
40933             }
40934             
40935             tbd.setSize(size.width, size.height );
40936             // this is for the account management tab -seems to work there.
40937             var thd = grid.getGridEl().select('thead',true).first();
40938             //if (tbd) {
40939             //    tbd.setSize(size.width, size.height - thd.getHeight());
40940             //}
40941              
40942             grid.autoSize();
40943         }
40944     },
40945      
40946     
40947     
40948     beforeSlide : function(){
40949         this.grid.getView().scroller.clip();
40950     },
40951     
40952     afterSlide : function(){
40953         this.grid.getView().scroller.unclip();
40954     },
40955     
40956     destroy : function(){
40957         this.grid.destroy();
40958         delete this.grid;
40959         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40960     }
40961 });
40962
40963 /**
40964  * @class Roo.bootstrap.panel.Nest
40965  * @extends Roo.bootstrap.panel.Content
40966  * @constructor
40967  * Create a new Panel, that can contain a layout.Border.
40968  * 
40969  * 
40970  * @param {String/Object} config A string to set only the title or a config object
40971  */
40972 Roo.bootstrap.panel.Nest = function(config)
40973 {
40974     // construct with only one argument..
40975     /* FIXME - implement nicer consturctors
40976     if (layout.layout) {
40977         config = layout;
40978         layout = config.layout;
40979         delete config.layout;
40980     }
40981     if (layout.xtype && !layout.getEl) {
40982         // then layout needs constructing..
40983         layout = Roo.factory(layout, Roo);
40984     }
40985     */
40986     
40987     config.el =  config.layout.getEl();
40988     
40989     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40990     
40991     config.layout.monitorWindowResize = false; // turn off autosizing
40992     this.layout = config.layout;
40993     this.layout.getEl().addClass("roo-layout-nested-layout");
40994     this.layout.parent = this;
40995     
40996     
40997     
40998     
40999 };
41000
41001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41002     /**
41003     * @cfg {Roo.BorderLayout} layout The layout for this panel
41004     */
41005     layout : false,
41006
41007     setSize : function(width, height){
41008         if(!this.ignoreResize(width, height)){
41009             var size = this.adjustForComponents(width, height);
41010             var el = this.layout.getEl();
41011             if (size.height < 1) {
41012                 el.setWidth(size.width);   
41013             } else {
41014                 el.setSize(size.width, size.height);
41015             }
41016             var touch = el.dom.offsetWidth;
41017             this.layout.layout();
41018             // ie requires a double layout on the first pass
41019             if(Roo.isIE && !this.initialized){
41020                 this.initialized = true;
41021                 this.layout.layout();
41022             }
41023         }
41024     },
41025     
41026     // activate all subpanels if not currently active..
41027     
41028     setActiveState : function(active){
41029         this.active = active;
41030         this.setActiveClass(active);
41031         
41032         if(!active){
41033             this.fireEvent("deactivate", this);
41034             return;
41035         }
41036         
41037         this.fireEvent("activate", this);
41038         // not sure if this should happen before or after..
41039         if (!this.layout) {
41040             return; // should not happen..
41041         }
41042         var reg = false;
41043         for (var r in this.layout.regions) {
41044             reg = this.layout.getRegion(r);
41045             if (reg.getActivePanel()) {
41046                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41047                 reg.setActivePanel(reg.getActivePanel());
41048                 continue;
41049             }
41050             if (!reg.panels.length) {
41051                 continue;
41052             }
41053             reg.showPanel(reg.getPanel(0));
41054         }
41055         
41056         
41057         
41058         
41059     },
41060     
41061     /**
41062      * Returns the nested BorderLayout for this panel
41063      * @return {Roo.BorderLayout} 
41064      */
41065     getLayout : function(){
41066         return this.layout;
41067     },
41068     
41069      /**
41070      * Adds a xtype elements to the layout of the nested panel
41071      * <pre><code>
41072
41073 panel.addxtype({
41074        xtype : 'ContentPanel',
41075        region: 'west',
41076        items: [ .... ]
41077    }
41078 );
41079
41080 panel.addxtype({
41081         xtype : 'NestedLayoutPanel',
41082         region: 'west',
41083         layout: {
41084            center: { },
41085            west: { }   
41086         },
41087         items : [ ... list of content panels or nested layout panels.. ]
41088    }
41089 );
41090 </code></pre>
41091      * @param {Object} cfg Xtype definition of item to add.
41092      */
41093     addxtype : function(cfg) {
41094         return this.layout.addxtype(cfg);
41095     
41096     }
41097 });/*
41098  * Based on:
41099  * Ext JS Library 1.1.1
41100  * Copyright(c) 2006-2007, Ext JS, LLC.
41101  *
41102  * Originally Released Under LGPL - original licence link has changed is not relivant.
41103  *
41104  * Fork - LGPL
41105  * <script type="text/javascript">
41106  */
41107 /**
41108  * @class Roo.TabPanel
41109  * @extends Roo.util.Observable
41110  * A lightweight tab container.
41111  * <br><br>
41112  * Usage:
41113  * <pre><code>
41114 // basic tabs 1, built from existing content
41115 var tabs = new Roo.TabPanel("tabs1");
41116 tabs.addTab("script", "View Script");
41117 tabs.addTab("markup", "View Markup");
41118 tabs.activate("script");
41119
41120 // more advanced tabs, built from javascript
41121 var jtabs = new Roo.TabPanel("jtabs");
41122 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41123
41124 // set up the UpdateManager
41125 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41126 var updater = tab2.getUpdateManager();
41127 updater.setDefaultUrl("ajax1.htm");
41128 tab2.on('activate', updater.refresh, updater, true);
41129
41130 // Use setUrl for Ajax loading
41131 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41132 tab3.setUrl("ajax2.htm", null, true);
41133
41134 // Disabled tab
41135 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41136 tab4.disable();
41137
41138 jtabs.activate("jtabs-1");
41139  * </code></pre>
41140  * @constructor
41141  * Create a new TabPanel.
41142  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41143  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41144  */
41145 Roo.bootstrap.panel.Tabs = function(config){
41146     /**
41147     * The container element for this TabPanel.
41148     * @type Roo.Element
41149     */
41150     this.el = Roo.get(config.el);
41151     delete config.el;
41152     if(config){
41153         if(typeof config == "boolean"){
41154             this.tabPosition = config ? "bottom" : "top";
41155         }else{
41156             Roo.apply(this, config);
41157         }
41158     }
41159     
41160     if(this.tabPosition == "bottom"){
41161         // if tabs are at the bottom = create the body first.
41162         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41163         this.el.addClass("roo-tabs-bottom");
41164     }
41165     // next create the tabs holders
41166     
41167     if (this.tabPosition == "west"){
41168         
41169         var reg = this.region; // fake it..
41170         while (reg) {
41171             if (!reg.mgr.parent) {
41172                 break;
41173             }
41174             reg = reg.mgr.parent.region;
41175         }
41176         Roo.log("got nest?");
41177         Roo.log(reg);
41178         if (reg.mgr.getRegion('west')) {
41179             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41180             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41181             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41182             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41183             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41184         
41185             
41186         }
41187         
41188         
41189     } else {
41190      
41191         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41192         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41193         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41194         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41195     }
41196     
41197     
41198     if(Roo.isIE){
41199         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41200     }
41201     
41202     // finally - if tabs are at the top, then create the body last..
41203     if(this.tabPosition != "bottom"){
41204         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41205          * @type Roo.Element
41206          */
41207         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41208         this.el.addClass("roo-tabs-top");
41209     }
41210     this.items = [];
41211
41212     this.bodyEl.setStyle("position", "relative");
41213
41214     this.active = null;
41215     this.activateDelegate = this.activate.createDelegate(this);
41216
41217     this.addEvents({
41218         /**
41219          * @event tabchange
41220          * Fires when the active tab changes
41221          * @param {Roo.TabPanel} this
41222          * @param {Roo.TabPanelItem} activePanel The new active tab
41223          */
41224         "tabchange": true,
41225         /**
41226          * @event beforetabchange
41227          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41228          * @param {Roo.TabPanel} this
41229          * @param {Object} e Set cancel to true on this object to cancel the tab change
41230          * @param {Roo.TabPanelItem} tab The tab being changed to
41231          */
41232         "beforetabchange" : true
41233     });
41234
41235     Roo.EventManager.onWindowResize(this.onResize, this);
41236     this.cpad = this.el.getPadding("lr");
41237     this.hiddenCount = 0;
41238
41239
41240     // toolbar on the tabbar support...
41241     if (this.toolbar) {
41242         alert("no toolbar support yet");
41243         this.toolbar  = false;
41244         /*
41245         var tcfg = this.toolbar;
41246         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41247         this.toolbar = new Roo.Toolbar(tcfg);
41248         if (Roo.isSafari) {
41249             var tbl = tcfg.container.child('table', true);
41250             tbl.setAttribute('width', '100%');
41251         }
41252         */
41253         
41254     }
41255    
41256
41257
41258     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41259 };
41260
41261 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41262     /*
41263      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41264      */
41265     tabPosition : "top",
41266     /*
41267      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41268      */
41269     currentTabWidth : 0,
41270     /*
41271      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41272      */
41273     minTabWidth : 40,
41274     /*
41275      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41276      */
41277     maxTabWidth : 250,
41278     /*
41279      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41280      */
41281     preferredTabWidth : 175,
41282     /*
41283      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41284      */
41285     resizeTabs : false,
41286     /*
41287      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41288      */
41289     monitorResize : true,
41290     /*
41291      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41292      */
41293     toolbar : false,  // set by caller..
41294     
41295     region : false, /// set by caller
41296     
41297     disableTooltips : true, // not used yet...
41298
41299     /**
41300      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41301      * @param {String} id The id of the div to use <b>or create</b>
41302      * @param {String} text The text for the tab
41303      * @param {String} content (optional) Content to put in the TabPanelItem body
41304      * @param {Boolean} closable (optional) True to create a close icon on the tab
41305      * @return {Roo.TabPanelItem} The created TabPanelItem
41306      */
41307     addTab : function(id, text, content, closable, tpl)
41308     {
41309         var item = new Roo.bootstrap.panel.TabItem({
41310             panel: this,
41311             id : id,
41312             text : text,
41313             closable : closable,
41314             tpl : tpl
41315         });
41316         this.addTabItem(item);
41317         if(content){
41318             item.setContent(content);
41319         }
41320         return item;
41321     },
41322
41323     /**
41324      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41325      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41326      * @return {Roo.TabPanelItem}
41327      */
41328     getTab : function(id){
41329         return this.items[id];
41330     },
41331
41332     /**
41333      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41334      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41335      */
41336     hideTab : function(id){
41337         var t = this.items[id];
41338         if(!t.isHidden()){
41339            t.setHidden(true);
41340            this.hiddenCount++;
41341            this.autoSizeTabs();
41342         }
41343     },
41344
41345     /**
41346      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41347      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41348      */
41349     unhideTab : function(id){
41350         var t = this.items[id];
41351         if(t.isHidden()){
41352            t.setHidden(false);
41353            this.hiddenCount--;
41354            this.autoSizeTabs();
41355         }
41356     },
41357
41358     /**
41359      * Adds an existing {@link Roo.TabPanelItem}.
41360      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41361      */
41362     addTabItem : function(item)
41363     {
41364         this.items[item.id] = item;
41365         this.items.push(item);
41366         this.autoSizeTabs();
41367       //  if(this.resizeTabs){
41368     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41369   //         this.autoSizeTabs();
41370 //        }else{
41371 //            item.autoSize();
41372        // }
41373     },
41374
41375     /**
41376      * Removes a {@link Roo.TabPanelItem}.
41377      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41378      */
41379     removeTab : function(id){
41380         var items = this.items;
41381         var tab = items[id];
41382         if(!tab) { return; }
41383         var index = items.indexOf(tab);
41384         if(this.active == tab && items.length > 1){
41385             var newTab = this.getNextAvailable(index);
41386             if(newTab) {
41387                 newTab.activate();
41388             }
41389         }
41390         this.stripEl.dom.removeChild(tab.pnode.dom);
41391         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41392             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41393         }
41394         items.splice(index, 1);
41395         delete this.items[tab.id];
41396         tab.fireEvent("close", tab);
41397         tab.purgeListeners();
41398         this.autoSizeTabs();
41399     },
41400
41401     getNextAvailable : function(start){
41402         var items = this.items;
41403         var index = start;
41404         // look for a next tab that will slide over to
41405         // replace the one being removed
41406         while(index < items.length){
41407             var item = items[++index];
41408             if(item && !item.isHidden()){
41409                 return item;
41410             }
41411         }
41412         // if one isn't found select the previous tab (on the left)
41413         index = start;
41414         while(index >= 0){
41415             var item = items[--index];
41416             if(item && !item.isHidden()){
41417                 return item;
41418             }
41419         }
41420         return null;
41421     },
41422
41423     /**
41424      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41425      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41426      */
41427     disableTab : function(id){
41428         var tab = this.items[id];
41429         if(tab && this.active != tab){
41430             tab.disable();
41431         }
41432     },
41433
41434     /**
41435      * Enables a {@link Roo.TabPanelItem} that is disabled.
41436      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41437      */
41438     enableTab : function(id){
41439         var tab = this.items[id];
41440         tab.enable();
41441     },
41442
41443     /**
41444      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41445      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41446      * @return {Roo.TabPanelItem} The TabPanelItem.
41447      */
41448     activate : function(id)
41449     {
41450         //Roo.log('activite:'  + id);
41451         
41452         var tab = this.items[id];
41453         if(!tab){
41454             return null;
41455         }
41456         if(tab == this.active || tab.disabled){
41457             return tab;
41458         }
41459         var e = {};
41460         this.fireEvent("beforetabchange", this, e, tab);
41461         if(e.cancel !== true && !tab.disabled){
41462             if(this.active){
41463                 this.active.hide();
41464             }
41465             this.active = this.items[id];
41466             this.active.show();
41467             this.fireEvent("tabchange", this, this.active);
41468         }
41469         return tab;
41470     },
41471
41472     /**
41473      * Gets the active {@link Roo.TabPanelItem}.
41474      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41475      */
41476     getActiveTab : function(){
41477         return this.active;
41478     },
41479
41480     /**
41481      * Updates the tab body element to fit the height of the container element
41482      * for overflow scrolling
41483      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41484      */
41485     syncHeight : function(targetHeight){
41486         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41487         var bm = this.bodyEl.getMargins();
41488         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41489         this.bodyEl.setHeight(newHeight);
41490         return newHeight;
41491     },
41492
41493     onResize : function(){
41494         if(this.monitorResize){
41495             this.autoSizeTabs();
41496         }
41497     },
41498
41499     /**
41500      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41501      */
41502     beginUpdate : function(){
41503         this.updating = true;
41504     },
41505
41506     /**
41507      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41508      */
41509     endUpdate : function(){
41510         this.updating = false;
41511         this.autoSizeTabs();
41512     },
41513
41514     /**
41515      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41516      */
41517     autoSizeTabs : function()
41518     {
41519         var count = this.items.length;
41520         var vcount = count - this.hiddenCount;
41521         
41522         if (vcount < 2) {
41523             this.stripEl.hide();
41524         } else {
41525             this.stripEl.show();
41526         }
41527         
41528         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41529             return;
41530         }
41531         
41532         
41533         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41534         var availWidth = Math.floor(w / vcount);
41535         var b = this.stripBody;
41536         if(b.getWidth() > w){
41537             var tabs = this.items;
41538             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41539             if(availWidth < this.minTabWidth){
41540                 /*if(!this.sleft){    // incomplete scrolling code
41541                     this.createScrollButtons();
41542                 }
41543                 this.showScroll();
41544                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41545             }
41546         }else{
41547             if(this.currentTabWidth < this.preferredTabWidth){
41548                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41549             }
41550         }
41551     },
41552
41553     /**
41554      * Returns the number of tabs in this TabPanel.
41555      * @return {Number}
41556      */
41557      getCount : function(){
41558          return this.items.length;
41559      },
41560
41561     /**
41562      * Resizes all the tabs to the passed width
41563      * @param {Number} The new width
41564      */
41565     setTabWidth : function(width){
41566         this.currentTabWidth = width;
41567         for(var i = 0, len = this.items.length; i < len; i++) {
41568                 if(!this.items[i].isHidden()) {
41569                 this.items[i].setWidth(width);
41570             }
41571         }
41572     },
41573
41574     /**
41575      * Destroys this TabPanel
41576      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41577      */
41578     destroy : function(removeEl){
41579         Roo.EventManager.removeResizeListener(this.onResize, this);
41580         for(var i = 0, len = this.items.length; i < len; i++){
41581             this.items[i].purgeListeners();
41582         }
41583         if(removeEl === true){
41584             this.el.update("");
41585             this.el.remove();
41586         }
41587     },
41588     
41589     createStrip : function(container)
41590     {
41591         var strip = document.createElement("nav");
41592         strip.className = Roo.bootstrap.version == 4 ?
41593             "navbar-light bg-light" : 
41594             "navbar navbar-default"; //"x-tabs-wrap";
41595         container.appendChild(strip);
41596         return strip;
41597     },
41598     
41599     createStripList : function(strip)
41600     {
41601         // div wrapper for retard IE
41602         // returns the "tr" element.
41603         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41604         //'<div class="x-tabs-strip-wrap">'+
41605           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41606           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41607         return strip.firstChild; //.firstChild.firstChild.firstChild;
41608     },
41609     createBody : function(container)
41610     {
41611         var body = document.createElement("div");
41612         Roo.id(body, "tab-body");
41613         //Roo.fly(body).addClass("x-tabs-body");
41614         Roo.fly(body).addClass("tab-content");
41615         container.appendChild(body);
41616         return body;
41617     },
41618     createItemBody :function(bodyEl, id){
41619         var body = Roo.getDom(id);
41620         if(!body){
41621             body = document.createElement("div");
41622             body.id = id;
41623         }
41624         //Roo.fly(body).addClass("x-tabs-item-body");
41625         Roo.fly(body).addClass("tab-pane");
41626          bodyEl.insertBefore(body, bodyEl.firstChild);
41627         return body;
41628     },
41629     /** @private */
41630     createStripElements :  function(stripEl, text, closable, tpl)
41631     {
41632         var td = document.createElement("li"); // was td..
41633         td.className = 'nav-item';
41634         
41635         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41636         
41637         
41638         stripEl.appendChild(td);
41639         /*if(closable){
41640             td.className = "x-tabs-closable";
41641             if(!this.closeTpl){
41642                 this.closeTpl = new Roo.Template(
41643                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41645                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41646                 );
41647             }
41648             var el = this.closeTpl.overwrite(td, {"text": text});
41649             var close = el.getElementsByTagName("div")[0];
41650             var inner = el.getElementsByTagName("em")[0];
41651             return {"el": el, "close": close, "inner": inner};
41652         } else {
41653         */
41654         // not sure what this is..
41655 //            if(!this.tabTpl){
41656                 //this.tabTpl = new Roo.Template(
41657                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41658                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41659                 //);
41660 //                this.tabTpl = new Roo.Template(
41661 //                   '<a href="#">' +
41662 //                   '<span unselectable="on"' +
41663 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41664 //                            ' >{text}</span></a>'
41665 //                );
41666 //                
41667 //            }
41668
41669
41670             var template = tpl || this.tabTpl || false;
41671             
41672             if(!template){
41673                 template =  new Roo.Template(
41674                         Roo.bootstrap.version == 4 ? 
41675                             (
41676                                 '<a class="nav-link" href="#" unselectable="on"' +
41677                                      (this.disableTooltips ? '' : ' title="{text}"') +
41678                                      ' >{text}</a>'
41679                             ) : (
41680                                 '<a class="nav-link" href="#">' +
41681                                 '<span unselectable="on"' +
41682                                          (this.disableTooltips ? '' : ' title="{text}"') +
41683                                     ' >{text}</span></a>'
41684                             )
41685                 );
41686             }
41687             
41688             switch (typeof(template)) {
41689                 case 'object' :
41690                     break;
41691                 case 'string' :
41692                     template = new Roo.Template(template);
41693                     break;
41694                 default :
41695                     break;
41696             }
41697             
41698             var el = template.overwrite(td, {"text": text});
41699             
41700             var inner = el.getElementsByTagName("span")[0];
41701             
41702             return {"el": el, "inner": inner};
41703             
41704     }
41705         
41706     
41707 });
41708
41709 /**
41710  * @class Roo.TabPanelItem
41711  * @extends Roo.util.Observable
41712  * Represents an individual item (tab plus body) in a TabPanel.
41713  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41714  * @param {String} id The id of this TabPanelItem
41715  * @param {String} text The text for the tab of this TabPanelItem
41716  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41717  */
41718 Roo.bootstrap.panel.TabItem = function(config){
41719     /**
41720      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41721      * @type Roo.TabPanel
41722      */
41723     this.tabPanel = config.panel;
41724     /**
41725      * The id for this TabPanelItem
41726      * @type String
41727      */
41728     this.id = config.id;
41729     /** @private */
41730     this.disabled = false;
41731     /** @private */
41732     this.text = config.text;
41733     /** @private */
41734     this.loaded = false;
41735     this.closable = config.closable;
41736
41737     /**
41738      * The body element for this TabPanelItem.
41739      * @type Roo.Element
41740      */
41741     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41742     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41743     this.bodyEl.setStyle("display", "block");
41744     this.bodyEl.setStyle("zoom", "1");
41745     //this.hideAction();
41746
41747     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41748     /** @private */
41749     this.el = Roo.get(els.el);
41750     this.inner = Roo.get(els.inner, true);
41751      this.textEl = Roo.bootstrap.version == 4 ?
41752         this.el : Roo.get(this.el.dom.firstChild, true);
41753
41754     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41755     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41756
41757     
41758 //    this.el.on("mousedown", this.onTabMouseDown, this);
41759     this.el.on("click", this.onTabClick, this);
41760     /** @private */
41761     if(config.closable){
41762         var c = Roo.get(els.close, true);
41763         c.dom.title = this.closeText;
41764         c.addClassOnOver("close-over");
41765         c.on("click", this.closeClick, this);
41766      }
41767
41768     this.addEvents({
41769          /**
41770          * @event activate
41771          * Fires when this tab becomes the active tab.
41772          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773          * @param {Roo.TabPanelItem} this
41774          */
41775         "activate": true,
41776         /**
41777          * @event beforeclose
41778          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41779          * @param {Roo.TabPanelItem} this
41780          * @param {Object} e Set cancel to true on this object to cancel the close.
41781          */
41782         "beforeclose": true,
41783         /**
41784          * @event close
41785          * Fires when this tab is closed.
41786          * @param {Roo.TabPanelItem} this
41787          */
41788          "close": true,
41789         /**
41790          * @event deactivate
41791          * Fires when this tab is no longer the active tab.
41792          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793          * @param {Roo.TabPanelItem} this
41794          */
41795          "deactivate" : true
41796     });
41797     this.hidden = false;
41798
41799     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41800 };
41801
41802 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41803            {
41804     purgeListeners : function(){
41805        Roo.util.Observable.prototype.purgeListeners.call(this);
41806        this.el.removeAllListeners();
41807     },
41808     /**
41809      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41810      */
41811     show : function(){
41812         this.status_node.addClass("active");
41813         this.showAction();
41814         if(Roo.isOpera){
41815             this.tabPanel.stripWrap.repaint();
41816         }
41817         this.fireEvent("activate", this.tabPanel, this);
41818     },
41819
41820     /**
41821      * Returns true if this tab is the active tab.
41822      * @return {Boolean}
41823      */
41824     isActive : function(){
41825         return this.tabPanel.getActiveTab() == this;
41826     },
41827
41828     /**
41829      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41830      */
41831     hide : function(){
41832         this.status_node.removeClass("active");
41833         this.hideAction();
41834         this.fireEvent("deactivate", this.tabPanel, this);
41835     },
41836
41837     hideAction : function(){
41838         this.bodyEl.hide();
41839         this.bodyEl.setStyle("position", "absolute");
41840         this.bodyEl.setLeft("-20000px");
41841         this.bodyEl.setTop("-20000px");
41842     },
41843
41844     showAction : function(){
41845         this.bodyEl.setStyle("position", "relative");
41846         this.bodyEl.setTop("");
41847         this.bodyEl.setLeft("");
41848         this.bodyEl.show();
41849     },
41850
41851     /**
41852      * Set the tooltip for the tab.
41853      * @param {String} tooltip The tab's tooltip
41854      */
41855     setTooltip : function(text){
41856         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41857             this.textEl.dom.qtip = text;
41858             this.textEl.dom.removeAttribute('title');
41859         }else{
41860             this.textEl.dom.title = text;
41861         }
41862     },
41863
41864     onTabClick : function(e){
41865         e.preventDefault();
41866         this.tabPanel.activate(this.id);
41867     },
41868
41869     onTabMouseDown : function(e){
41870         e.preventDefault();
41871         this.tabPanel.activate(this.id);
41872     },
41873 /*
41874     getWidth : function(){
41875         return this.inner.getWidth();
41876     },
41877
41878     setWidth : function(width){
41879         var iwidth = width - this.linode.getPadding("lr");
41880         this.inner.setWidth(iwidth);
41881         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41882         this.linode.setWidth(width);
41883     },
41884 */
41885     /**
41886      * Show or hide the tab
41887      * @param {Boolean} hidden True to hide or false to show.
41888      */
41889     setHidden : function(hidden){
41890         this.hidden = hidden;
41891         this.linode.setStyle("display", hidden ? "none" : "");
41892     },
41893
41894     /**
41895      * Returns true if this tab is "hidden"
41896      * @return {Boolean}
41897      */
41898     isHidden : function(){
41899         return this.hidden;
41900     },
41901
41902     /**
41903      * Returns the text for this tab
41904      * @return {String}
41905      */
41906     getText : function(){
41907         return this.text;
41908     },
41909     /*
41910     autoSize : function(){
41911         //this.el.beginMeasure();
41912         this.textEl.setWidth(1);
41913         /*
41914          *  #2804 [new] Tabs in Roojs
41915          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41916          */
41917         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41918         //this.el.endMeasure();
41919     //},
41920
41921     /**
41922      * Sets the text for the tab (Note: this also sets the tooltip text)
41923      * @param {String} text The tab's text and tooltip
41924      */
41925     setText : function(text){
41926         this.text = text;
41927         this.textEl.update(text);
41928         this.setTooltip(text);
41929         //if(!this.tabPanel.resizeTabs){
41930         //    this.autoSize();
41931         //}
41932     },
41933     /**
41934      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41935      */
41936     activate : function(){
41937         this.tabPanel.activate(this.id);
41938     },
41939
41940     /**
41941      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41942      */
41943     disable : function(){
41944         if(this.tabPanel.active != this){
41945             this.disabled = true;
41946             this.status_node.addClass("disabled");
41947         }
41948     },
41949
41950     /**
41951      * Enables this TabPanelItem if it was previously disabled.
41952      */
41953     enable : function(){
41954         this.disabled = false;
41955         this.status_node.removeClass("disabled");
41956     },
41957
41958     /**
41959      * Sets the content for this TabPanelItem.
41960      * @param {String} content The content
41961      * @param {Boolean} loadScripts true to look for and load scripts
41962      */
41963     setContent : function(content, loadScripts){
41964         this.bodyEl.update(content, loadScripts);
41965     },
41966
41967     /**
41968      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41969      * @return {Roo.UpdateManager} The UpdateManager
41970      */
41971     getUpdateManager : function(){
41972         return this.bodyEl.getUpdateManager();
41973     },
41974
41975     /**
41976      * Set a URL to be used to load the content for this TabPanelItem.
41977      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41978      * @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)
41979      * @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)
41980      * @return {Roo.UpdateManager} The UpdateManager
41981      */
41982     setUrl : function(url, params, loadOnce){
41983         if(this.refreshDelegate){
41984             this.un('activate', this.refreshDelegate);
41985         }
41986         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41987         this.on("activate", this.refreshDelegate);
41988         return this.bodyEl.getUpdateManager();
41989     },
41990
41991     /** @private */
41992     _handleRefresh : function(url, params, loadOnce){
41993         if(!loadOnce || !this.loaded){
41994             var updater = this.bodyEl.getUpdateManager();
41995             updater.update(url, params, this._setLoaded.createDelegate(this));
41996         }
41997     },
41998
41999     /**
42000      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42001      *   Will fail silently if the setUrl method has not been called.
42002      *   This does not activate the panel, just updates its content.
42003      */
42004     refresh : function(){
42005         if(this.refreshDelegate){
42006            this.loaded = false;
42007            this.refreshDelegate();
42008         }
42009     },
42010
42011     /** @private */
42012     _setLoaded : function(){
42013         this.loaded = true;
42014     },
42015
42016     /** @private */
42017     closeClick : function(e){
42018         var o = {};
42019         e.stopEvent();
42020         this.fireEvent("beforeclose", this, o);
42021         if(o.cancel !== true){
42022             this.tabPanel.removeTab(this.id);
42023         }
42024     },
42025     /**
42026      * The text displayed in the tooltip for the close icon.
42027      * @type String
42028      */
42029     closeText : "Close this tab"
42030 });
42031 /**
42032 *    This script refer to:
42033 *    Title: International Telephone Input
42034 *    Author: Jack O'Connor
42035 *    Code version:  v12.1.12
42036 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42037 **/
42038
42039 Roo.bootstrap.form.PhoneInputData = function() {
42040     var d = [
42041       [
42042         "Afghanistan (‫افغانستان‬‎)",
42043         "af",
42044         "93"
42045       ],
42046       [
42047         "Albania (Shqipëri)",
42048         "al",
42049         "355"
42050       ],
42051       [
42052         "Algeria (‫الجزائر‬‎)",
42053         "dz",
42054         "213"
42055       ],
42056       [
42057         "American Samoa",
42058         "as",
42059         "1684"
42060       ],
42061       [
42062         "Andorra",
42063         "ad",
42064         "376"
42065       ],
42066       [
42067         "Angola",
42068         "ao",
42069         "244"
42070       ],
42071       [
42072         "Anguilla",
42073         "ai",
42074         "1264"
42075       ],
42076       [
42077         "Antigua and Barbuda",
42078         "ag",
42079         "1268"
42080       ],
42081       [
42082         "Argentina",
42083         "ar",
42084         "54"
42085       ],
42086       [
42087         "Armenia (Հայաստան)",
42088         "am",
42089         "374"
42090       ],
42091       [
42092         "Aruba",
42093         "aw",
42094         "297"
42095       ],
42096       [
42097         "Australia",
42098         "au",
42099         "61",
42100         0
42101       ],
42102       [
42103         "Austria (Österreich)",
42104         "at",
42105         "43"
42106       ],
42107       [
42108         "Azerbaijan (Azərbaycan)",
42109         "az",
42110         "994"
42111       ],
42112       [
42113         "Bahamas",
42114         "bs",
42115         "1242"
42116       ],
42117       [
42118         "Bahrain (‫البحرين‬‎)",
42119         "bh",
42120         "973"
42121       ],
42122       [
42123         "Bangladesh (বাংলাদেশ)",
42124         "bd",
42125         "880"
42126       ],
42127       [
42128         "Barbados",
42129         "bb",
42130         "1246"
42131       ],
42132       [
42133         "Belarus (Беларусь)",
42134         "by",
42135         "375"
42136       ],
42137       [
42138         "Belgium (België)",
42139         "be",
42140         "32"
42141       ],
42142       [
42143         "Belize",
42144         "bz",
42145         "501"
42146       ],
42147       [
42148         "Benin (Bénin)",
42149         "bj",
42150         "229"
42151       ],
42152       [
42153         "Bermuda",
42154         "bm",
42155         "1441"
42156       ],
42157       [
42158         "Bhutan (འབྲུག)",
42159         "bt",
42160         "975"
42161       ],
42162       [
42163         "Bolivia",
42164         "bo",
42165         "591"
42166       ],
42167       [
42168         "Bosnia and Herzegovina (Босна и Херцеговина)",
42169         "ba",
42170         "387"
42171       ],
42172       [
42173         "Botswana",
42174         "bw",
42175         "267"
42176       ],
42177       [
42178         "Brazil (Brasil)",
42179         "br",
42180         "55"
42181       ],
42182       [
42183         "British Indian Ocean Territory",
42184         "io",
42185         "246"
42186       ],
42187       [
42188         "British Virgin Islands",
42189         "vg",
42190         "1284"
42191       ],
42192       [
42193         "Brunei",
42194         "bn",
42195         "673"
42196       ],
42197       [
42198         "Bulgaria (България)",
42199         "bg",
42200         "359"
42201       ],
42202       [
42203         "Burkina Faso",
42204         "bf",
42205         "226"
42206       ],
42207       [
42208         "Burundi (Uburundi)",
42209         "bi",
42210         "257"
42211       ],
42212       [
42213         "Cambodia (កម្ពុជា)",
42214         "kh",
42215         "855"
42216       ],
42217       [
42218         "Cameroon (Cameroun)",
42219         "cm",
42220         "237"
42221       ],
42222       [
42223         "Canada",
42224         "ca",
42225         "1",
42226         1,
42227         ["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"]
42228       ],
42229       [
42230         "Cape Verde (Kabu Verdi)",
42231         "cv",
42232         "238"
42233       ],
42234       [
42235         "Caribbean Netherlands",
42236         "bq",
42237         "599",
42238         1
42239       ],
42240       [
42241         "Cayman Islands",
42242         "ky",
42243         "1345"
42244       ],
42245       [
42246         "Central African Republic (République centrafricaine)",
42247         "cf",
42248         "236"
42249       ],
42250       [
42251         "Chad (Tchad)",
42252         "td",
42253         "235"
42254       ],
42255       [
42256         "Chile",
42257         "cl",
42258         "56"
42259       ],
42260       [
42261         "China (中国)",
42262         "cn",
42263         "86"
42264       ],
42265       [
42266         "Christmas Island",
42267         "cx",
42268         "61",
42269         2
42270       ],
42271       [
42272         "Cocos (Keeling) Islands",
42273         "cc",
42274         "61",
42275         1
42276       ],
42277       [
42278         "Colombia",
42279         "co",
42280         "57"
42281       ],
42282       [
42283         "Comoros (‫جزر القمر‬‎)",
42284         "km",
42285         "269"
42286       ],
42287       [
42288         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42289         "cd",
42290         "243"
42291       ],
42292       [
42293         "Congo (Republic) (Congo-Brazzaville)",
42294         "cg",
42295         "242"
42296       ],
42297       [
42298         "Cook Islands",
42299         "ck",
42300         "682"
42301       ],
42302       [
42303         "Costa Rica",
42304         "cr",
42305         "506"
42306       ],
42307       [
42308         "Côte d’Ivoire",
42309         "ci",
42310         "225"
42311       ],
42312       [
42313         "Croatia (Hrvatska)",
42314         "hr",
42315         "385"
42316       ],
42317       [
42318         "Cuba",
42319         "cu",
42320         "53"
42321       ],
42322       [
42323         "Curaçao",
42324         "cw",
42325         "599",
42326         0
42327       ],
42328       [
42329         "Cyprus (Κύπρος)",
42330         "cy",
42331         "357"
42332       ],
42333       [
42334         "Czech Republic (Česká republika)",
42335         "cz",
42336         "420"
42337       ],
42338       [
42339         "Denmark (Danmark)",
42340         "dk",
42341         "45"
42342       ],
42343       [
42344         "Djibouti",
42345         "dj",
42346         "253"
42347       ],
42348       [
42349         "Dominica",
42350         "dm",
42351         "1767"
42352       ],
42353       [
42354         "Dominican Republic (República Dominicana)",
42355         "do",
42356         "1",
42357         2,
42358         ["809", "829", "849"]
42359       ],
42360       [
42361         "Ecuador",
42362         "ec",
42363         "593"
42364       ],
42365       [
42366         "Egypt (‫مصر‬‎)",
42367         "eg",
42368         "20"
42369       ],
42370       [
42371         "El Salvador",
42372         "sv",
42373         "503"
42374       ],
42375       [
42376         "Equatorial Guinea (Guinea Ecuatorial)",
42377         "gq",
42378         "240"
42379       ],
42380       [
42381         "Eritrea",
42382         "er",
42383         "291"
42384       ],
42385       [
42386         "Estonia (Eesti)",
42387         "ee",
42388         "372"
42389       ],
42390       [
42391         "Ethiopia",
42392         "et",
42393         "251"
42394       ],
42395       [
42396         "Falkland Islands (Islas Malvinas)",
42397         "fk",
42398         "500"
42399       ],
42400       [
42401         "Faroe Islands (Føroyar)",
42402         "fo",
42403         "298"
42404       ],
42405       [
42406         "Fiji",
42407         "fj",
42408         "679"
42409       ],
42410       [
42411         "Finland (Suomi)",
42412         "fi",
42413         "358",
42414         0
42415       ],
42416       [
42417         "France",
42418         "fr",
42419         "33"
42420       ],
42421       [
42422         "French Guiana (Guyane française)",
42423         "gf",
42424         "594"
42425       ],
42426       [
42427         "French Polynesia (Polynésie française)",
42428         "pf",
42429         "689"
42430       ],
42431       [
42432         "Gabon",
42433         "ga",
42434         "241"
42435       ],
42436       [
42437         "Gambia",
42438         "gm",
42439         "220"
42440       ],
42441       [
42442         "Georgia (საქართველო)",
42443         "ge",
42444         "995"
42445       ],
42446       [
42447         "Germany (Deutschland)",
42448         "de",
42449         "49"
42450       ],
42451       [
42452         "Ghana (Gaana)",
42453         "gh",
42454         "233"
42455       ],
42456       [
42457         "Gibraltar",
42458         "gi",
42459         "350"
42460       ],
42461       [
42462         "Greece (Ελλάδα)",
42463         "gr",
42464         "30"
42465       ],
42466       [
42467         "Greenland (Kalaallit Nunaat)",
42468         "gl",
42469         "299"
42470       ],
42471       [
42472         "Grenada",
42473         "gd",
42474         "1473"
42475       ],
42476       [
42477         "Guadeloupe",
42478         "gp",
42479         "590",
42480         0
42481       ],
42482       [
42483         "Guam",
42484         "gu",
42485         "1671"
42486       ],
42487       [
42488         "Guatemala",
42489         "gt",
42490         "502"
42491       ],
42492       [
42493         "Guernsey",
42494         "gg",
42495         "44",
42496         1
42497       ],
42498       [
42499         "Guinea (Guinée)",
42500         "gn",
42501         "224"
42502       ],
42503       [
42504         "Guinea-Bissau (Guiné Bissau)",
42505         "gw",
42506         "245"
42507       ],
42508       [
42509         "Guyana",
42510         "gy",
42511         "592"
42512       ],
42513       [
42514         "Haiti",
42515         "ht",
42516         "509"
42517       ],
42518       [
42519         "Honduras",
42520         "hn",
42521         "504"
42522       ],
42523       [
42524         "Hong Kong (香港)",
42525         "hk",
42526         "852"
42527       ],
42528       [
42529         "Hungary (Magyarország)",
42530         "hu",
42531         "36"
42532       ],
42533       [
42534         "Iceland (Ísland)",
42535         "is",
42536         "354"
42537       ],
42538       [
42539         "India (भारत)",
42540         "in",
42541         "91"
42542       ],
42543       [
42544         "Indonesia",
42545         "id",
42546         "62"
42547       ],
42548       [
42549         "Iran (‫ایران‬‎)",
42550         "ir",
42551         "98"
42552       ],
42553       [
42554         "Iraq (‫العراق‬‎)",
42555         "iq",
42556         "964"
42557       ],
42558       [
42559         "Ireland",
42560         "ie",
42561         "353"
42562       ],
42563       [
42564         "Isle of Man",
42565         "im",
42566         "44",
42567         2
42568       ],
42569       [
42570         "Israel (‫ישראל‬‎)",
42571         "il",
42572         "972"
42573       ],
42574       [
42575         "Italy (Italia)",
42576         "it",
42577         "39",
42578         0
42579       ],
42580       [
42581         "Jamaica",
42582         "jm",
42583         "1876"
42584       ],
42585       [
42586         "Japan (日本)",
42587         "jp",
42588         "81"
42589       ],
42590       [
42591         "Jersey",
42592         "je",
42593         "44",
42594         3
42595       ],
42596       [
42597         "Jordan (‫الأردن‬‎)",
42598         "jo",
42599         "962"
42600       ],
42601       [
42602         "Kazakhstan (Казахстан)",
42603         "kz",
42604         "7",
42605         1
42606       ],
42607       [
42608         "Kenya",
42609         "ke",
42610         "254"
42611       ],
42612       [
42613         "Kiribati",
42614         "ki",
42615         "686"
42616       ],
42617       [
42618         "Kosovo",
42619         "xk",
42620         "383"
42621       ],
42622       [
42623         "Kuwait (‫الكويت‬‎)",
42624         "kw",
42625         "965"
42626       ],
42627       [
42628         "Kyrgyzstan (Кыргызстан)",
42629         "kg",
42630         "996"
42631       ],
42632       [
42633         "Laos (ລາວ)",
42634         "la",
42635         "856"
42636       ],
42637       [
42638         "Latvia (Latvija)",
42639         "lv",
42640         "371"
42641       ],
42642       [
42643         "Lebanon (‫لبنان‬‎)",
42644         "lb",
42645         "961"
42646       ],
42647       [
42648         "Lesotho",
42649         "ls",
42650         "266"
42651       ],
42652       [
42653         "Liberia",
42654         "lr",
42655         "231"
42656       ],
42657       [
42658         "Libya (‫ليبيا‬‎)",
42659         "ly",
42660         "218"
42661       ],
42662       [
42663         "Liechtenstein",
42664         "li",
42665         "423"
42666       ],
42667       [
42668         "Lithuania (Lietuva)",
42669         "lt",
42670         "370"
42671       ],
42672       [
42673         "Luxembourg",
42674         "lu",
42675         "352"
42676       ],
42677       [
42678         "Macau (澳門)",
42679         "mo",
42680         "853"
42681       ],
42682       [
42683         "Macedonia (FYROM) (Македонија)",
42684         "mk",
42685         "389"
42686       ],
42687       [
42688         "Madagascar (Madagasikara)",
42689         "mg",
42690         "261"
42691       ],
42692       [
42693         "Malawi",
42694         "mw",
42695         "265"
42696       ],
42697       [
42698         "Malaysia",
42699         "my",
42700         "60"
42701       ],
42702       [
42703         "Maldives",
42704         "mv",
42705         "960"
42706       ],
42707       [
42708         "Mali",
42709         "ml",
42710         "223"
42711       ],
42712       [
42713         "Malta",
42714         "mt",
42715         "356"
42716       ],
42717       [
42718         "Marshall Islands",
42719         "mh",
42720         "692"
42721       ],
42722       [
42723         "Martinique",
42724         "mq",
42725         "596"
42726       ],
42727       [
42728         "Mauritania (‫موريتانيا‬‎)",
42729         "mr",
42730         "222"
42731       ],
42732       [
42733         "Mauritius (Moris)",
42734         "mu",
42735         "230"
42736       ],
42737       [
42738         "Mayotte",
42739         "yt",
42740         "262",
42741         1
42742       ],
42743       [
42744         "Mexico (México)",
42745         "mx",
42746         "52"
42747       ],
42748       [
42749         "Micronesia",
42750         "fm",
42751         "691"
42752       ],
42753       [
42754         "Moldova (Republica Moldova)",
42755         "md",
42756         "373"
42757       ],
42758       [
42759         "Monaco",
42760         "mc",
42761         "377"
42762       ],
42763       [
42764         "Mongolia (Монгол)",
42765         "mn",
42766         "976"
42767       ],
42768       [
42769         "Montenegro (Crna Gora)",
42770         "me",
42771         "382"
42772       ],
42773       [
42774         "Montserrat",
42775         "ms",
42776         "1664"
42777       ],
42778       [
42779         "Morocco (‫المغرب‬‎)",
42780         "ma",
42781         "212",
42782         0
42783       ],
42784       [
42785         "Mozambique (Moçambique)",
42786         "mz",
42787         "258"
42788       ],
42789       [
42790         "Myanmar (Burma) (မြန်မာ)",
42791         "mm",
42792         "95"
42793       ],
42794       [
42795         "Namibia (Namibië)",
42796         "na",
42797         "264"
42798       ],
42799       [
42800         "Nauru",
42801         "nr",
42802         "674"
42803       ],
42804       [
42805         "Nepal (नेपाल)",
42806         "np",
42807         "977"
42808       ],
42809       [
42810         "Netherlands (Nederland)",
42811         "nl",
42812         "31"
42813       ],
42814       [
42815         "New Caledonia (Nouvelle-Calédonie)",
42816         "nc",
42817         "687"
42818       ],
42819       [
42820         "New Zealand",
42821         "nz",
42822         "64"
42823       ],
42824       [
42825         "Nicaragua",
42826         "ni",
42827         "505"
42828       ],
42829       [
42830         "Niger (Nijar)",
42831         "ne",
42832         "227"
42833       ],
42834       [
42835         "Nigeria",
42836         "ng",
42837         "234"
42838       ],
42839       [
42840         "Niue",
42841         "nu",
42842         "683"
42843       ],
42844       [
42845         "Norfolk Island",
42846         "nf",
42847         "672"
42848       ],
42849       [
42850         "North Korea (조선 민주주의 인민 공화국)",
42851         "kp",
42852         "850"
42853       ],
42854       [
42855         "Northern Mariana Islands",
42856         "mp",
42857         "1670"
42858       ],
42859       [
42860         "Norway (Norge)",
42861         "no",
42862         "47",
42863         0
42864       ],
42865       [
42866         "Oman (‫عُمان‬‎)",
42867         "om",
42868         "968"
42869       ],
42870       [
42871         "Pakistan (‫پاکستان‬‎)",
42872         "pk",
42873         "92"
42874       ],
42875       [
42876         "Palau",
42877         "pw",
42878         "680"
42879       ],
42880       [
42881         "Palestine (‫فلسطين‬‎)",
42882         "ps",
42883         "970"
42884       ],
42885       [
42886         "Panama (Panamá)",
42887         "pa",
42888         "507"
42889       ],
42890       [
42891         "Papua New Guinea",
42892         "pg",
42893         "675"
42894       ],
42895       [
42896         "Paraguay",
42897         "py",
42898         "595"
42899       ],
42900       [
42901         "Peru (Perú)",
42902         "pe",
42903         "51"
42904       ],
42905       [
42906         "Philippines",
42907         "ph",
42908         "63"
42909       ],
42910       [
42911         "Poland (Polska)",
42912         "pl",
42913         "48"
42914       ],
42915       [
42916         "Portugal",
42917         "pt",
42918         "351"
42919       ],
42920       [
42921         "Puerto Rico",
42922         "pr",
42923         "1",
42924         3,
42925         ["787", "939"]
42926       ],
42927       [
42928         "Qatar (‫قطر‬‎)",
42929         "qa",
42930         "974"
42931       ],
42932       [
42933         "Réunion (La Réunion)",
42934         "re",
42935         "262",
42936         0
42937       ],
42938       [
42939         "Romania (România)",
42940         "ro",
42941         "40"
42942       ],
42943       [
42944         "Russia (Россия)",
42945         "ru",
42946         "7",
42947         0
42948       ],
42949       [
42950         "Rwanda",
42951         "rw",
42952         "250"
42953       ],
42954       [
42955         "Saint Barthélemy",
42956         "bl",
42957         "590",
42958         1
42959       ],
42960       [
42961         "Saint Helena",
42962         "sh",
42963         "290"
42964       ],
42965       [
42966         "Saint Kitts and Nevis",
42967         "kn",
42968         "1869"
42969       ],
42970       [
42971         "Saint Lucia",
42972         "lc",
42973         "1758"
42974       ],
42975       [
42976         "Saint Martin (Saint-Martin (partie française))",
42977         "mf",
42978         "590",
42979         2
42980       ],
42981       [
42982         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42983         "pm",
42984         "508"
42985       ],
42986       [
42987         "Saint Vincent and the Grenadines",
42988         "vc",
42989         "1784"
42990       ],
42991       [
42992         "Samoa",
42993         "ws",
42994         "685"
42995       ],
42996       [
42997         "San Marino",
42998         "sm",
42999         "378"
43000       ],
43001       [
43002         "São Tomé and Príncipe (São Tomé e Príncipe)",
43003         "st",
43004         "239"
43005       ],
43006       [
43007         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43008         "sa",
43009         "966"
43010       ],
43011       [
43012         "Senegal (Sénégal)",
43013         "sn",
43014         "221"
43015       ],
43016       [
43017         "Serbia (Србија)",
43018         "rs",
43019         "381"
43020       ],
43021       [
43022         "Seychelles",
43023         "sc",
43024         "248"
43025       ],
43026       [
43027         "Sierra Leone",
43028         "sl",
43029         "232"
43030       ],
43031       [
43032         "Singapore",
43033         "sg",
43034         "65"
43035       ],
43036       [
43037         "Sint Maarten",
43038         "sx",
43039         "1721"
43040       ],
43041       [
43042         "Slovakia (Slovensko)",
43043         "sk",
43044         "421"
43045       ],
43046       [
43047         "Slovenia (Slovenija)",
43048         "si",
43049         "386"
43050       ],
43051       [
43052         "Solomon Islands",
43053         "sb",
43054         "677"
43055       ],
43056       [
43057         "Somalia (Soomaaliya)",
43058         "so",
43059         "252"
43060       ],
43061       [
43062         "South Africa",
43063         "za",
43064         "27"
43065       ],
43066       [
43067         "South Korea (대한민국)",
43068         "kr",
43069         "82"
43070       ],
43071       [
43072         "South Sudan (‫جنوب السودان‬‎)",
43073         "ss",
43074         "211"
43075       ],
43076       [
43077         "Spain (España)",
43078         "es",
43079         "34"
43080       ],
43081       [
43082         "Sri Lanka (ශ්‍රී ලංකාව)",
43083         "lk",
43084         "94"
43085       ],
43086       [
43087         "Sudan (‫السودان‬‎)",
43088         "sd",
43089         "249"
43090       ],
43091       [
43092         "Suriname",
43093         "sr",
43094         "597"
43095       ],
43096       [
43097         "Svalbard and Jan Mayen",
43098         "sj",
43099         "47",
43100         1
43101       ],
43102       [
43103         "Swaziland",
43104         "sz",
43105         "268"
43106       ],
43107       [
43108         "Sweden (Sverige)",
43109         "se",
43110         "46"
43111       ],
43112       [
43113         "Switzerland (Schweiz)",
43114         "ch",
43115         "41"
43116       ],
43117       [
43118         "Syria (‫سوريا‬‎)",
43119         "sy",
43120         "963"
43121       ],
43122       [
43123         "Taiwan (台灣)",
43124         "tw",
43125         "886"
43126       ],
43127       [
43128         "Tajikistan",
43129         "tj",
43130         "992"
43131       ],
43132       [
43133         "Tanzania",
43134         "tz",
43135         "255"
43136       ],
43137       [
43138         "Thailand (ไทย)",
43139         "th",
43140         "66"
43141       ],
43142       [
43143         "Timor-Leste",
43144         "tl",
43145         "670"
43146       ],
43147       [
43148         "Togo",
43149         "tg",
43150         "228"
43151       ],
43152       [
43153         "Tokelau",
43154         "tk",
43155         "690"
43156       ],
43157       [
43158         "Tonga",
43159         "to",
43160         "676"
43161       ],
43162       [
43163         "Trinidad and Tobago",
43164         "tt",
43165         "1868"
43166       ],
43167       [
43168         "Tunisia (‫تونس‬‎)",
43169         "tn",
43170         "216"
43171       ],
43172       [
43173         "Turkey (Türkiye)",
43174         "tr",
43175         "90"
43176       ],
43177       [
43178         "Turkmenistan",
43179         "tm",
43180         "993"
43181       ],
43182       [
43183         "Turks and Caicos Islands",
43184         "tc",
43185         "1649"
43186       ],
43187       [
43188         "Tuvalu",
43189         "tv",
43190         "688"
43191       ],
43192       [
43193         "U.S. Virgin Islands",
43194         "vi",
43195         "1340"
43196       ],
43197       [
43198         "Uganda",
43199         "ug",
43200         "256"
43201       ],
43202       [
43203         "Ukraine (Україна)",
43204         "ua",
43205         "380"
43206       ],
43207       [
43208         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43209         "ae",
43210         "971"
43211       ],
43212       [
43213         "United Kingdom",
43214         "gb",
43215         "44",
43216         0
43217       ],
43218       [
43219         "United States",
43220         "us",
43221         "1",
43222         0
43223       ],
43224       [
43225         "Uruguay",
43226         "uy",
43227         "598"
43228       ],
43229       [
43230         "Uzbekistan (Oʻzbekiston)",
43231         "uz",
43232         "998"
43233       ],
43234       [
43235         "Vanuatu",
43236         "vu",
43237         "678"
43238       ],
43239       [
43240         "Vatican City (Città del Vaticano)",
43241         "va",
43242         "39",
43243         1
43244       ],
43245       [
43246         "Venezuela",
43247         "ve",
43248         "58"
43249       ],
43250       [
43251         "Vietnam (Việt Nam)",
43252         "vn",
43253         "84"
43254       ],
43255       [
43256         "Wallis and Futuna (Wallis-et-Futuna)",
43257         "wf",
43258         "681"
43259       ],
43260       [
43261         "Western Sahara (‫الصحراء الغربية‬‎)",
43262         "eh",
43263         "212",
43264         1
43265       ],
43266       [
43267         "Yemen (‫اليمن‬‎)",
43268         "ye",
43269         "967"
43270       ],
43271       [
43272         "Zambia",
43273         "zm",
43274         "260"
43275       ],
43276       [
43277         "Zimbabwe",
43278         "zw",
43279         "263"
43280       ],
43281       [
43282         "Åland Islands",
43283         "ax",
43284         "358",
43285         1
43286       ]
43287   ];
43288   
43289   return d;
43290 }/**
43291 *    This script refer to:
43292 *    Title: International Telephone Input
43293 *    Author: Jack O'Connor
43294 *    Code version:  v12.1.12
43295 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43296 **/
43297
43298 /**
43299  * @class Roo.bootstrap.form.PhoneInput
43300  * @extends Roo.bootstrap.form.TriggerField
43301  * An input with International dial-code selection
43302  
43303  * @cfg {String} defaultDialCode default '+852'
43304  * @cfg {Array} preferedCountries default []
43305   
43306  * @constructor
43307  * Create a new PhoneInput.
43308  * @param {Object} config Configuration options
43309  */
43310
43311 Roo.bootstrap.form.PhoneInput = function(config) {
43312     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43313 };
43314
43315 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43316         /**
43317         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43318         */
43319         listWidth: undefined,
43320         
43321         selectedClass: 'active',
43322         
43323         invalidClass : "has-warning",
43324         
43325         validClass: 'has-success',
43326         
43327         allowed: '0123456789',
43328         
43329         max_length: 15,
43330         
43331         /**
43332          * @cfg {String} defaultDialCode The default dial code when initializing the input
43333          */
43334         defaultDialCode: '+852',
43335         
43336         /**
43337          * @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
43338          */
43339         preferedCountries: false,
43340         
43341         getAutoCreate : function()
43342         {
43343             var data = Roo.bootstrap.form.PhoneInputData();
43344             var align = this.labelAlign || this.parentLabelAlign();
43345             var id = Roo.id();
43346             
43347             this.allCountries = [];
43348             this.dialCodeMapping = [];
43349             
43350             for (var i = 0; i < data.length; i++) {
43351               var c = data[i];
43352               this.allCountries[i] = {
43353                 name: c[0],
43354                 iso2: c[1],
43355                 dialCode: c[2],
43356                 priority: c[3] || 0,
43357                 areaCodes: c[4] || null
43358               };
43359               this.dialCodeMapping[c[2]] = {
43360                   name: c[0],
43361                   iso2: c[1],
43362                   priority: c[3] || 0,
43363                   areaCodes: c[4] || null
43364               };
43365             }
43366             
43367             var cfg = {
43368                 cls: 'form-group',
43369                 cn: []
43370             };
43371             
43372             var input =  {
43373                 tag: 'input',
43374                 id : id,
43375                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43376                 maxlength: this.max_length,
43377                 cls : 'form-control tel-input',
43378                 autocomplete: 'new-password'
43379             };
43380             
43381             var hiddenInput = {
43382                 tag: 'input',
43383                 type: 'hidden',
43384                 cls: 'hidden-tel-input'
43385             };
43386             
43387             if (this.name) {
43388                 hiddenInput.name = this.name;
43389             }
43390             
43391             if (this.disabled) {
43392                 input.disabled = true;
43393             }
43394             
43395             var flag_container = {
43396                 tag: 'div',
43397                 cls: 'flag-box',
43398                 cn: [
43399                     {
43400                         tag: 'div',
43401                         cls: 'flag'
43402                     },
43403                     {
43404                         tag: 'div',
43405                         cls: 'caret'
43406                     }
43407                 ]
43408             };
43409             
43410             var box = {
43411                 tag: 'div',
43412                 cls: this.hasFeedback ? 'has-feedback' : '',
43413                 cn: [
43414                     hiddenInput,
43415                     input,
43416                     {
43417                         tag: 'input',
43418                         cls: 'dial-code-holder',
43419                         disabled: true
43420                     }
43421                 ]
43422             };
43423             
43424             var container = {
43425                 cls: 'roo-select2-container input-group',
43426                 cn: [
43427                     flag_container,
43428                     box
43429                 ]
43430             };
43431             
43432             if (this.fieldLabel.length) {
43433                 var indicator = {
43434                     tag: 'i',
43435                     tooltip: 'This field is required'
43436                 };
43437                 
43438                 var label = {
43439                     tag: 'label',
43440                     'for':  id,
43441                     cls: 'control-label',
43442                     cn: []
43443                 };
43444                 
43445                 var label_text = {
43446                     tag: 'span',
43447                     html: this.fieldLabel
43448                 };
43449                 
43450                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43451                 label.cn = [
43452                     indicator,
43453                     label_text
43454                 ];
43455                 
43456                 if(this.indicatorpos == 'right') {
43457                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43458                     label.cn = [
43459                         label_text,
43460                         indicator
43461                     ];
43462                 }
43463                 
43464                 if(align == 'left') {
43465                     container = {
43466                         tag: 'div',
43467                         cn: [
43468                             container
43469                         ]
43470                     };
43471                     
43472                     if(this.labelWidth > 12){
43473                         label.style = "width: " + this.labelWidth + 'px';
43474                     }
43475                     if(this.labelWidth < 13 && this.labelmd == 0){
43476                         this.labelmd = this.labelWidth;
43477                     }
43478                     if(this.labellg > 0){
43479                         label.cls += ' col-lg-' + this.labellg;
43480                         input.cls += ' col-lg-' + (12 - this.labellg);
43481                     }
43482                     if(this.labelmd > 0){
43483                         label.cls += ' col-md-' + this.labelmd;
43484                         container.cls += ' col-md-' + (12 - this.labelmd);
43485                     }
43486                     if(this.labelsm > 0){
43487                         label.cls += ' col-sm-' + this.labelsm;
43488                         container.cls += ' col-sm-' + (12 - this.labelsm);
43489                     }
43490                     if(this.labelxs > 0){
43491                         label.cls += ' col-xs-' + this.labelxs;
43492                         container.cls += ' col-xs-' + (12 - this.labelxs);
43493                     }
43494                 }
43495             }
43496             
43497             cfg.cn = [
43498                 label,
43499                 container
43500             ];
43501             
43502             var settings = this;
43503             
43504             ['xs','sm','md','lg'].map(function(size){
43505                 if (settings[size]) {
43506                     cfg.cls += ' col-' + size + '-' + settings[size];
43507                 }
43508             });
43509             
43510             this.store = new Roo.data.Store({
43511                 proxy : new Roo.data.MemoryProxy({}),
43512                 reader : new Roo.data.JsonReader({
43513                     fields : [
43514                         {
43515                             'name' : 'name',
43516                             'type' : 'string'
43517                         },
43518                         {
43519                             'name' : 'iso2',
43520                             'type' : 'string'
43521                         },
43522                         {
43523                             'name' : 'dialCode',
43524                             'type' : 'string'
43525                         },
43526                         {
43527                             'name' : 'priority',
43528                             'type' : 'string'
43529                         },
43530                         {
43531                             'name' : 'areaCodes',
43532                             'type' : 'string'
43533                         }
43534                     ]
43535                 })
43536             });
43537             
43538             if(!this.preferedCountries) {
43539                 this.preferedCountries = [
43540                     'hk',
43541                     'gb',
43542                     'us'
43543                 ];
43544             }
43545             
43546             var p = this.preferedCountries.reverse();
43547             
43548             if(p) {
43549                 for (var i = 0; i < p.length; i++) {
43550                     for (var j = 0; j < this.allCountries.length; j++) {
43551                         if(this.allCountries[j].iso2 == p[i]) {
43552                             var t = this.allCountries[j];
43553                             this.allCountries.splice(j,1);
43554                             this.allCountries.unshift(t);
43555                         }
43556                     } 
43557                 }
43558             }
43559             
43560             this.store.proxy.data = {
43561                 success: true,
43562                 data: this.allCountries
43563             };
43564             
43565             return cfg;
43566         },
43567         
43568         initEvents : function()
43569         {
43570             this.createList();
43571             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43572             
43573             this.indicator = this.indicatorEl();
43574             this.flag = this.flagEl();
43575             this.dialCodeHolder = this.dialCodeHolderEl();
43576             
43577             this.trigger = this.el.select('div.flag-box',true).first();
43578             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43579             
43580             var _this = this;
43581             
43582             (function(){
43583                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43584                 _this.list.setWidth(lw);
43585             }).defer(100);
43586             
43587             this.list.on('mouseover', this.onViewOver, this);
43588             this.list.on('mousemove', this.onViewMove, this);
43589             this.inputEl().on("keyup", this.onKeyUp, this);
43590             this.inputEl().on("keypress", this.onKeyPress, this);
43591             
43592             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43593
43594             this.view = new Roo.View(this.list, this.tpl, {
43595                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43596             });
43597             
43598             this.view.on('click', this.onViewClick, this);
43599             this.setValue(this.defaultDialCode);
43600         },
43601         
43602         onTriggerClick : function(e)
43603         {
43604             Roo.log('trigger click');
43605             if(this.disabled){
43606                 return;
43607             }
43608             
43609             if(this.isExpanded()){
43610                 this.collapse();
43611                 this.hasFocus = false;
43612             }else {
43613                 this.store.load({});
43614                 this.hasFocus = true;
43615                 this.expand();
43616             }
43617         },
43618         
43619         isExpanded : function()
43620         {
43621             return this.list.isVisible();
43622         },
43623         
43624         collapse : function()
43625         {
43626             if(!this.isExpanded()){
43627                 return;
43628             }
43629             this.list.hide();
43630             Roo.get(document).un('mousedown', this.collapseIf, this);
43631             Roo.get(document).un('mousewheel', this.collapseIf, this);
43632             this.fireEvent('collapse', this);
43633             this.validate();
43634         },
43635         
43636         expand : function()
43637         {
43638             Roo.log('expand');
43639
43640             if(this.isExpanded() || !this.hasFocus){
43641                 return;
43642             }
43643             
43644             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43645             this.list.setWidth(lw);
43646             
43647             this.list.show();
43648             this.restrictHeight();
43649             
43650             Roo.get(document).on('mousedown', this.collapseIf, this);
43651             Roo.get(document).on('mousewheel', this.collapseIf, this);
43652             
43653             this.fireEvent('expand', this);
43654         },
43655         
43656         restrictHeight : function()
43657         {
43658             this.list.alignTo(this.inputEl(), this.listAlign);
43659             this.list.alignTo(this.inputEl(), this.listAlign);
43660         },
43661         
43662         onViewOver : function(e, t)
43663         {
43664             if(this.inKeyMode){
43665                 return;
43666             }
43667             var item = this.view.findItemFromChild(t);
43668             
43669             if(item){
43670                 var index = this.view.indexOf(item);
43671                 this.select(index, false);
43672             }
43673         },
43674
43675         // private
43676         onViewClick : function(view, doFocus, el, e)
43677         {
43678             var index = this.view.getSelectedIndexes()[0];
43679             
43680             var r = this.store.getAt(index);
43681             
43682             if(r){
43683                 this.onSelect(r, index);
43684             }
43685             if(doFocus !== false && !this.blockFocus){
43686                 this.inputEl().focus();
43687             }
43688         },
43689         
43690         onViewMove : function(e, t)
43691         {
43692             this.inKeyMode = false;
43693         },
43694         
43695         select : function(index, scrollIntoView)
43696         {
43697             this.selectedIndex = index;
43698             this.view.select(index);
43699             if(scrollIntoView !== false){
43700                 var el = this.view.getNode(index);
43701                 if(el){
43702                     this.list.scrollChildIntoView(el, false);
43703                 }
43704             }
43705         },
43706         
43707         createList : function()
43708         {
43709             this.list = Roo.get(document.body).createChild({
43710                 tag: 'ul',
43711                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43712                 style: 'display:none'
43713             });
43714             
43715             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43716         },
43717         
43718         collapseIf : function(e)
43719         {
43720             var in_combo  = e.within(this.el);
43721             var in_list =  e.within(this.list);
43722             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43723             
43724             if (in_combo || in_list || is_list) {
43725                 return;
43726             }
43727             this.collapse();
43728         },
43729         
43730         onSelect : function(record, index)
43731         {
43732             if(this.fireEvent('beforeselect', this, record, index) !== false){
43733                 
43734                 this.setFlagClass(record.data.iso2);
43735                 this.setDialCode(record.data.dialCode);
43736                 this.hasFocus = false;
43737                 this.collapse();
43738                 this.fireEvent('select', this, record, index);
43739             }
43740         },
43741         
43742         flagEl : function()
43743         {
43744             var flag = this.el.select('div.flag',true).first();
43745             if(!flag){
43746                 return false;
43747             }
43748             return flag;
43749         },
43750         
43751         dialCodeHolderEl : function()
43752         {
43753             var d = this.el.select('input.dial-code-holder',true).first();
43754             if(!d){
43755                 return false;
43756             }
43757             return d;
43758         },
43759         
43760         setDialCode : function(v)
43761         {
43762             this.dialCodeHolder.dom.value = '+'+v;
43763         },
43764         
43765         setFlagClass : function(n)
43766         {
43767             this.flag.dom.className = 'flag '+n;
43768         },
43769         
43770         getValue : function()
43771         {
43772             var v = this.inputEl().getValue();
43773             if(this.dialCodeHolder) {
43774                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43775             }
43776             return v;
43777         },
43778         
43779         setValue : function(v)
43780         {
43781             var d = this.getDialCode(v);
43782             
43783             //invalid dial code
43784             if(v.length == 0 || !d || d.length == 0) {
43785                 if(this.rendered){
43786                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43787                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43788                 }
43789                 return;
43790             }
43791             
43792             //valid dial code
43793             this.setFlagClass(this.dialCodeMapping[d].iso2);
43794             this.setDialCode(d);
43795             this.inputEl().dom.value = v.replace('+'+d,'');
43796             this.hiddenEl().dom.value = this.getValue();
43797             
43798             this.validate();
43799         },
43800         
43801         getDialCode : function(v)
43802         {
43803             v = v ||  '';
43804             
43805             if (v.length == 0) {
43806                 return this.dialCodeHolder.dom.value;
43807             }
43808             
43809             var dialCode = "";
43810             if (v.charAt(0) != "+") {
43811                 return false;
43812             }
43813             var numericChars = "";
43814             for (var i = 1; i < v.length; i++) {
43815               var c = v.charAt(i);
43816               if (!isNaN(c)) {
43817                 numericChars += c;
43818                 if (this.dialCodeMapping[numericChars]) {
43819                   dialCode = v.substr(1, i);
43820                 }
43821                 if (numericChars.length == 4) {
43822                   break;
43823                 }
43824               }
43825             }
43826             return dialCode;
43827         },
43828         
43829         reset : function()
43830         {
43831             this.setValue(this.defaultDialCode);
43832             this.validate();
43833         },
43834         
43835         hiddenEl : function()
43836         {
43837             return this.el.select('input.hidden-tel-input',true).first();
43838         },
43839         
43840         // after setting val
43841         onKeyUp : function(e){
43842             this.setValue(this.getValue());
43843         },
43844         
43845         onKeyPress : function(e){
43846             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43847                 e.stopEvent();
43848             }
43849         }
43850         
43851 });
43852 /**
43853  * @class Roo.bootstrap.form.MoneyField
43854  * @extends Roo.bootstrap.form.ComboBox
43855  * Bootstrap MoneyField class
43856  * 
43857  * @constructor
43858  * Create a new MoneyField.
43859  * @param {Object} config Configuration options
43860  */
43861
43862 Roo.bootstrap.form.MoneyField = function(config) {
43863     
43864     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43865     
43866 };
43867
43868 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43869     
43870     /**
43871      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43872      */
43873     allowDecimals : true,
43874     /**
43875      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43876      */
43877     decimalSeparator : ".",
43878     /**
43879      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43880      */
43881     decimalPrecision : 0,
43882     /**
43883      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43884      */
43885     allowNegative : true,
43886     /**
43887      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43888      */
43889     allowZero: true,
43890     /**
43891      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43892      */
43893     minValue : Number.NEGATIVE_INFINITY,
43894     /**
43895      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43896      */
43897     maxValue : Number.MAX_VALUE,
43898     /**
43899      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43900      */
43901     minText : "The minimum value for this field is {0}",
43902     /**
43903      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43904      */
43905     maxText : "The maximum value for this field is {0}",
43906     /**
43907      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43908      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43909      */
43910     nanText : "{0} is not a valid number",
43911     /**
43912      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43913      */
43914     castInt : true,
43915     /**
43916      * @cfg {String} defaults currency of the MoneyField
43917      * value should be in lkey
43918      */
43919     defaultCurrency : false,
43920     /**
43921      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43922      */
43923     thousandsDelimiter : false,
43924     /**
43925      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43926      */
43927     max_length: false,
43928     
43929     inputlg : 9,
43930     inputmd : 9,
43931     inputsm : 9,
43932     inputxs : 6,
43933      /**
43934      * @cfg {Roo.data.Store} store  Store to lookup currency??
43935      */
43936     store : false,
43937     
43938     getAutoCreate : function()
43939     {
43940         var align = this.labelAlign || this.parentLabelAlign();
43941         
43942         var id = Roo.id();
43943
43944         var cfg = {
43945             cls: 'form-group',
43946             cn: []
43947         };
43948
43949         var input =  {
43950             tag: 'input',
43951             id : id,
43952             cls : 'form-control roo-money-amount-input',
43953             autocomplete: 'new-password'
43954         };
43955         
43956         var hiddenInput = {
43957             tag: 'input',
43958             type: 'hidden',
43959             id: Roo.id(),
43960             cls: 'hidden-number-input'
43961         };
43962         
43963         if(this.max_length) {
43964             input.maxlength = this.max_length; 
43965         }
43966         
43967         if (this.name) {
43968             hiddenInput.name = this.name;
43969         }
43970
43971         if (this.disabled) {
43972             input.disabled = true;
43973         }
43974
43975         var clg = 12 - this.inputlg;
43976         var cmd = 12 - this.inputmd;
43977         var csm = 12 - this.inputsm;
43978         var cxs = 12 - this.inputxs;
43979         
43980         var container = {
43981             tag : 'div',
43982             cls : 'row roo-money-field',
43983             cn : [
43984                 {
43985                     tag : 'div',
43986                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43987                     cn : [
43988                         {
43989                             tag : 'div',
43990                             cls: 'roo-select2-container input-group',
43991                             cn: [
43992                                 {
43993                                     tag : 'input',
43994                                     cls : 'form-control roo-money-currency-input',
43995                                     autocomplete: 'new-password',
43996                                     readOnly : 1,
43997                                     name : this.currencyName
43998                                 },
43999                                 {
44000                                     tag :'span',
44001                                     cls : 'input-group-addon',
44002                                     cn : [
44003                                         {
44004                                             tag: 'span',
44005                                             cls: 'caret'
44006                                         }
44007                                     ]
44008                                 }
44009                             ]
44010                         }
44011                     ]
44012                 },
44013                 {
44014                     tag : 'div',
44015                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44016                     cn : [
44017                         {
44018                             tag: 'div',
44019                             cls: this.hasFeedback ? 'has-feedback' : '',
44020                             cn: [
44021                                 input
44022                             ]
44023                         }
44024                     ]
44025                 }
44026             ]
44027             
44028         };
44029         
44030         if (this.fieldLabel.length) {
44031             var indicator = {
44032                 tag: 'i',
44033                 tooltip: 'This field is required'
44034             };
44035
44036             var label = {
44037                 tag: 'label',
44038                 'for':  id,
44039                 cls: 'control-label',
44040                 cn: []
44041             };
44042
44043             var label_text = {
44044                 tag: 'span',
44045                 html: this.fieldLabel
44046             };
44047
44048             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44049             label.cn = [
44050                 indicator,
44051                 label_text
44052             ];
44053
44054             if(this.indicatorpos == 'right') {
44055                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44056                 label.cn = [
44057                     label_text,
44058                     indicator
44059                 ];
44060             }
44061
44062             if(align == 'left') {
44063                 container = {
44064                     tag: 'div',
44065                     cn: [
44066                         container
44067                     ]
44068                 };
44069
44070                 if(this.labelWidth > 12){
44071                     label.style = "width: " + this.labelWidth + 'px';
44072                 }
44073                 if(this.labelWidth < 13 && this.labelmd == 0){
44074                     this.labelmd = this.labelWidth;
44075                 }
44076                 if(this.labellg > 0){
44077                     label.cls += ' col-lg-' + this.labellg;
44078                     input.cls += ' col-lg-' + (12 - this.labellg);
44079                 }
44080                 if(this.labelmd > 0){
44081                     label.cls += ' col-md-' + this.labelmd;
44082                     container.cls += ' col-md-' + (12 - this.labelmd);
44083                 }
44084                 if(this.labelsm > 0){
44085                     label.cls += ' col-sm-' + this.labelsm;
44086                     container.cls += ' col-sm-' + (12 - this.labelsm);
44087                 }
44088                 if(this.labelxs > 0){
44089                     label.cls += ' col-xs-' + this.labelxs;
44090                     container.cls += ' col-xs-' + (12 - this.labelxs);
44091                 }
44092             }
44093         }
44094
44095         cfg.cn = [
44096             label,
44097             container,
44098             hiddenInput
44099         ];
44100         
44101         var settings = this;
44102
44103         ['xs','sm','md','lg'].map(function(size){
44104             if (settings[size]) {
44105                 cfg.cls += ' col-' + size + '-' + settings[size];
44106             }
44107         });
44108         
44109         return cfg;
44110     },
44111     
44112     initEvents : function()
44113     {
44114         this.indicator = this.indicatorEl();
44115         
44116         this.initCurrencyEvent();
44117         
44118         this.initNumberEvent();
44119     },
44120     
44121     initCurrencyEvent : function()
44122     {
44123         if (!this.store) {
44124             throw "can not find store for combo";
44125         }
44126         
44127         this.store = Roo.factory(this.store, Roo.data);
44128         this.store.parent = this;
44129         
44130         this.createList();
44131         
44132         this.triggerEl = this.el.select('.input-group-addon', true).first();
44133         
44134         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44135         
44136         var _this = this;
44137         
44138         (function(){
44139             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44140             _this.list.setWidth(lw);
44141         }).defer(100);
44142         
44143         this.list.on('mouseover', this.onViewOver, this);
44144         this.list.on('mousemove', this.onViewMove, this);
44145         this.list.on('scroll', this.onViewScroll, this);
44146         
44147         if(!this.tpl){
44148             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44149         }
44150         
44151         this.view = new Roo.View(this.list, this.tpl, {
44152             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44153         });
44154         
44155         this.view.on('click', this.onViewClick, this);
44156         
44157         this.store.on('beforeload', this.onBeforeLoad, this);
44158         this.store.on('load', this.onLoad, this);
44159         this.store.on('loadexception', this.onLoadException, this);
44160         
44161         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44162             "up" : function(e){
44163                 this.inKeyMode = true;
44164                 this.selectPrev();
44165             },
44166
44167             "down" : function(e){
44168                 if(!this.isExpanded()){
44169                     this.onTriggerClick();
44170                 }else{
44171                     this.inKeyMode = true;
44172                     this.selectNext();
44173                 }
44174             },
44175
44176             "enter" : function(e){
44177                 this.collapse();
44178                 
44179                 if(this.fireEvent("specialkey", this, e)){
44180                     this.onViewClick(false);
44181                 }
44182                 
44183                 return true;
44184             },
44185
44186             "esc" : function(e){
44187                 this.collapse();
44188             },
44189
44190             "tab" : function(e){
44191                 this.collapse();
44192                 
44193                 if(this.fireEvent("specialkey", this, e)){
44194                     this.onViewClick(false);
44195                 }
44196                 
44197                 return true;
44198             },
44199
44200             scope : this,
44201
44202             doRelay : function(foo, bar, hname){
44203                 if(hname == 'down' || this.scope.isExpanded()){
44204                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44205                 }
44206                 return true;
44207             },
44208
44209             forceKeyDown: true
44210         });
44211         
44212         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44213         
44214     },
44215     
44216     initNumberEvent : function(e)
44217     {
44218         this.inputEl().on("keydown" , this.fireKey,  this);
44219         this.inputEl().on("focus", this.onFocus,  this);
44220         this.inputEl().on("blur", this.onBlur,  this);
44221         
44222         this.inputEl().relayEvent('keyup', this);
44223         
44224         if(this.indicator){
44225             this.indicator.addClass('invisible');
44226         }
44227  
44228         this.originalValue = this.getValue();
44229         
44230         if(this.validationEvent == 'keyup'){
44231             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44232             this.inputEl().on('keyup', this.filterValidation, this);
44233         }
44234         else if(this.validationEvent !== false){
44235             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44236         }
44237         
44238         if(this.selectOnFocus){
44239             this.on("focus", this.preFocus, this);
44240             
44241         }
44242         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44243             this.inputEl().on("keypress", this.filterKeys, this);
44244         } else {
44245             this.inputEl().relayEvent('keypress', this);
44246         }
44247         
44248         var allowed = "0123456789";
44249         
44250         if(this.allowDecimals){
44251             allowed += this.decimalSeparator;
44252         }
44253         
44254         if(this.allowNegative){
44255             allowed += "-";
44256         }
44257         
44258         if(this.thousandsDelimiter) {
44259             allowed += ",";
44260         }
44261         
44262         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44263         
44264         var keyPress = function(e){
44265             
44266             var k = e.getKey();
44267             
44268             var c = e.getCharCode();
44269             
44270             if(
44271                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44272                     allowed.indexOf(String.fromCharCode(c)) === -1
44273             ){
44274                 e.stopEvent();
44275                 return;
44276             }
44277             
44278             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44279                 return;
44280             }
44281             
44282             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44283                 e.stopEvent();
44284             }
44285         };
44286         
44287         this.inputEl().on("keypress", keyPress, this);
44288         
44289     },
44290     
44291     onTriggerClick : function(e)
44292     {   
44293         if(this.disabled){
44294             return;
44295         }
44296         
44297         this.page = 0;
44298         this.loadNext = false;
44299         
44300         if(this.isExpanded()){
44301             this.collapse();
44302             return;
44303         }
44304         
44305         this.hasFocus = true;
44306         
44307         if(this.triggerAction == 'all') {
44308             this.doQuery(this.allQuery, true);
44309             return;
44310         }
44311         
44312         this.doQuery(this.getRawValue());
44313     },
44314     
44315     getCurrency : function()
44316     {   
44317         var v = this.currencyEl().getValue();
44318         
44319         return v;
44320     },
44321     
44322     restrictHeight : function()
44323     {
44324         this.list.alignTo(this.currencyEl(), this.listAlign);
44325         this.list.alignTo(this.currencyEl(), this.listAlign);
44326     },
44327     
44328     onViewClick : function(view, doFocus, el, e)
44329     {
44330         var index = this.view.getSelectedIndexes()[0];
44331         
44332         var r = this.store.getAt(index);
44333         
44334         if(r){
44335             this.onSelect(r, index);
44336         }
44337     },
44338     
44339     onSelect : function(record, index){
44340         
44341         if(this.fireEvent('beforeselect', this, record, index) !== false){
44342         
44343             this.setFromCurrencyData(index > -1 ? record.data : false);
44344             
44345             this.collapse();
44346             
44347             this.fireEvent('select', this, record, index);
44348         }
44349     },
44350     
44351     setFromCurrencyData : function(o)
44352     {
44353         var currency = '';
44354         
44355         this.lastCurrency = o;
44356         
44357         if (this.currencyField) {
44358             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44359         } else {
44360             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44361         }
44362         
44363         this.lastSelectionText = currency;
44364         
44365         //setting default currency
44366         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44367             this.setCurrency(this.defaultCurrency);
44368             return;
44369         }
44370         
44371         this.setCurrency(currency);
44372     },
44373     
44374     setFromData : function(o)
44375     {
44376         var c = {};
44377         
44378         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44379         
44380         this.setFromCurrencyData(c);
44381         
44382         var value = '';
44383         
44384         if (this.name) {
44385             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44386         } else {
44387             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44388         }
44389         
44390         this.setValue(value);
44391         
44392     },
44393     
44394     setCurrency : function(v)
44395     {   
44396         this.currencyValue = v;
44397         
44398         if(this.rendered){
44399             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44400             this.validate();
44401         }
44402     },
44403     
44404     setValue : function(v)
44405     {
44406         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44407         
44408         this.value = v;
44409         
44410         if(this.rendered){
44411             
44412             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44413             
44414             this.inputEl().dom.value = (v == '') ? '' :
44415                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44416             
44417             if(!this.allowZero && v === '0') {
44418                 this.hiddenEl().dom.value = '';
44419                 this.inputEl().dom.value = '';
44420             }
44421             
44422             this.validate();
44423         }
44424     },
44425     
44426     getRawValue : function()
44427     {
44428         var v = this.inputEl().getValue();
44429         
44430         return v;
44431     },
44432     
44433     getValue : function()
44434     {
44435         return this.fixPrecision(this.parseValue(this.getRawValue()));
44436     },
44437     
44438     parseValue : function(value)
44439     {
44440         if(this.thousandsDelimiter) {
44441             value += "";
44442             r = new RegExp(",", "g");
44443             value = value.replace(r, "");
44444         }
44445         
44446         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44447         return isNaN(value) ? '' : value;
44448         
44449     },
44450     
44451     fixPrecision : function(value)
44452     {
44453         if(this.thousandsDelimiter) {
44454             value += "";
44455             r = new RegExp(",", "g");
44456             value = value.replace(r, "");
44457         }
44458         
44459         var nan = isNaN(value);
44460         
44461         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44462             return nan ? '' : value;
44463         }
44464         return parseFloat(value).toFixed(this.decimalPrecision);
44465     },
44466     
44467     decimalPrecisionFcn : function(v)
44468     {
44469         return Math.floor(v);
44470     },
44471     
44472     validateValue : function(value)
44473     {
44474         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44475             return false;
44476         }
44477         
44478         var num = this.parseValue(value);
44479         
44480         if(isNaN(num)){
44481             this.markInvalid(String.format(this.nanText, value));
44482             return false;
44483         }
44484         
44485         if(num < this.minValue){
44486             this.markInvalid(String.format(this.minText, this.minValue));
44487             return false;
44488         }
44489         
44490         if(num > this.maxValue){
44491             this.markInvalid(String.format(this.maxText, this.maxValue));
44492             return false;
44493         }
44494         
44495         return true;
44496     },
44497     
44498     validate : function()
44499     {
44500         if(this.disabled || this.allowBlank){
44501             this.markValid();
44502             return true;
44503         }
44504         
44505         var currency = this.getCurrency();
44506         
44507         if(this.validateValue(this.getRawValue()) && currency.length){
44508             this.markValid();
44509             return true;
44510         }
44511         
44512         this.markInvalid();
44513         return false;
44514     },
44515     
44516     getName: function()
44517     {
44518         return this.name;
44519     },
44520     
44521     beforeBlur : function()
44522     {
44523         if(!this.castInt){
44524             return;
44525         }
44526         
44527         var v = this.parseValue(this.getRawValue());
44528         
44529         if(v || v == 0){
44530             this.setValue(v);
44531         }
44532     },
44533     
44534     onBlur : function()
44535     {
44536         this.beforeBlur();
44537         
44538         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44539             //this.el.removeClass(this.focusClass);
44540         }
44541         
44542         this.hasFocus = false;
44543         
44544         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44545             this.validate();
44546         }
44547         
44548         var v = this.getValue();
44549         
44550         if(String(v) !== String(this.startValue)){
44551             this.fireEvent('change', this, v, this.startValue);
44552         }
44553         
44554         this.fireEvent("blur", this);
44555     },
44556     
44557     inputEl : function()
44558     {
44559         return this.el.select('.roo-money-amount-input', true).first();
44560     },
44561     
44562     currencyEl : function()
44563     {
44564         return this.el.select('.roo-money-currency-input', true).first();
44565     },
44566     
44567     hiddenEl : function()
44568     {
44569         return this.el.select('input.hidden-number-input',true).first();
44570     }
44571     
44572 });/**
44573  * @class Roo.bootstrap.BezierSignature
44574  * @extends Roo.bootstrap.Component
44575  * Bootstrap BezierSignature class
44576  * This script refer to:
44577  *    Title: Signature Pad
44578  *    Author: szimek
44579  *    Availability: https://github.com/szimek/signature_pad
44580  *
44581  * @constructor
44582  * Create a new BezierSignature
44583  * @param {Object} config The config object
44584  */
44585
44586 Roo.bootstrap.BezierSignature = function(config){
44587     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44588     this.addEvents({
44589         "resize" : true
44590     });
44591 };
44592
44593 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44594 {
44595      
44596     curve_data: [],
44597     
44598     is_empty: true,
44599     
44600     mouse_btn_down: true,
44601     
44602     /**
44603      * @cfg {int} canvas height
44604      */
44605     canvas_height: '200px',
44606     
44607     /**
44608      * @cfg {float|function} Radius of a single dot.
44609      */ 
44610     dot_size: false,
44611     
44612     /**
44613      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44614      */
44615     min_width: 0.5,
44616     
44617     /**
44618      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44619      */
44620     max_width: 2.5,
44621     
44622     /**
44623      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44624      */
44625     throttle: 16,
44626     
44627     /**
44628      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44629      */
44630     min_distance: 5,
44631     
44632     /**
44633      * @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.
44634      */
44635     bg_color: 'rgba(0, 0, 0, 0)',
44636     
44637     /**
44638      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44639      */
44640     dot_color: 'black',
44641     
44642     /**
44643      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44644      */ 
44645     velocity_filter_weight: 0.7,
44646     
44647     /**
44648      * @cfg {function} Callback when stroke begin. 
44649      */
44650     onBegin: false,
44651     
44652     /**
44653      * @cfg {function} Callback when stroke end.
44654      */
44655     onEnd: false,
44656     
44657     getAutoCreate : function()
44658     {
44659         var cls = 'roo-signature column';
44660         
44661         if(this.cls){
44662             cls += ' ' + this.cls;
44663         }
44664         
44665         var col_sizes = [
44666             'lg',
44667             'md',
44668             'sm',
44669             'xs'
44670         ];
44671         
44672         for(var i = 0; i < col_sizes.length; i++) {
44673             if(this[col_sizes[i]]) {
44674                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44675             }
44676         }
44677         
44678         var cfg = {
44679             tag: 'div',
44680             cls: cls,
44681             cn: [
44682                 {
44683                     tag: 'div',
44684                     cls: 'roo-signature-body',
44685                     cn: [
44686                         {
44687                             tag: 'canvas',
44688                             cls: 'roo-signature-body-canvas',
44689                             height: this.canvas_height,
44690                             width: this.canvas_width
44691                         }
44692                     ]
44693                 },
44694                 {
44695                     tag: 'input',
44696                     type: 'file',
44697                     style: 'display: none'
44698                 }
44699             ]
44700         };
44701         
44702         return cfg;
44703     },
44704     
44705     initEvents: function() 
44706     {
44707         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44708         
44709         var canvas = this.canvasEl();
44710         
44711         // mouse && touch event swapping...
44712         canvas.dom.style.touchAction = 'none';
44713         canvas.dom.style.msTouchAction = 'none';
44714         
44715         this.mouse_btn_down = false;
44716         canvas.on('mousedown', this._handleMouseDown, this);
44717         canvas.on('mousemove', this._handleMouseMove, this);
44718         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44719         
44720         if (window.PointerEvent) {
44721             canvas.on('pointerdown', this._handleMouseDown, this);
44722             canvas.on('pointermove', this._handleMouseMove, this);
44723             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44724         }
44725         
44726         if ('ontouchstart' in window) {
44727             canvas.on('touchstart', this._handleTouchStart, this);
44728             canvas.on('touchmove', this._handleTouchMove, this);
44729             canvas.on('touchend', this._handleTouchEnd, this);
44730         }
44731         
44732         Roo.EventManager.onWindowResize(this.resize, this, true);
44733         
44734         // file input event
44735         this.fileEl().on('change', this.uploadImage, this);
44736         
44737         this.clear();
44738         
44739         this.resize();
44740     },
44741     
44742     resize: function(){
44743         
44744         var canvas = this.canvasEl().dom;
44745         var ctx = this.canvasElCtx();
44746         var img_data = false;
44747         
44748         if(canvas.width > 0) {
44749             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44750         }
44751         // setting canvas width will clean img data
44752         canvas.width = 0;
44753         
44754         var style = window.getComputedStyle ? 
44755             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44756             
44757         var padding_left = parseInt(style.paddingLeft) || 0;
44758         var padding_right = parseInt(style.paddingRight) || 0;
44759         
44760         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44761         
44762         if(img_data) {
44763             ctx.putImageData(img_data, 0, 0);
44764         }
44765     },
44766     
44767     _handleMouseDown: function(e)
44768     {
44769         if (e.browserEvent.which === 1) {
44770             this.mouse_btn_down = true;
44771             this.strokeBegin(e);
44772         }
44773     },
44774     
44775     _handleMouseMove: function (e)
44776     {
44777         if (this.mouse_btn_down) {
44778             this.strokeMoveUpdate(e);
44779         }
44780     },
44781     
44782     _handleMouseUp: function (e)
44783     {
44784         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44785             this.mouse_btn_down = false;
44786             this.strokeEnd(e);
44787         }
44788     },
44789     
44790     _handleTouchStart: function (e) {
44791         
44792         e.preventDefault();
44793         if (e.browserEvent.targetTouches.length === 1) {
44794             // var touch = e.browserEvent.changedTouches[0];
44795             // this.strokeBegin(touch);
44796             
44797              this.strokeBegin(e); // assume e catching the correct xy...
44798         }
44799     },
44800     
44801     _handleTouchMove: function (e) {
44802         e.preventDefault();
44803         // var touch = event.targetTouches[0];
44804         // _this._strokeMoveUpdate(touch);
44805         this.strokeMoveUpdate(e);
44806     },
44807     
44808     _handleTouchEnd: function (e) {
44809         var wasCanvasTouched = e.target === this.canvasEl().dom;
44810         if (wasCanvasTouched) {
44811             e.preventDefault();
44812             // var touch = event.changedTouches[0];
44813             // _this._strokeEnd(touch);
44814             this.strokeEnd(e);
44815         }
44816     },
44817     
44818     reset: function () {
44819         this._lastPoints = [];
44820         this._lastVelocity = 0;
44821         this._lastWidth = (this.min_width + this.max_width) / 2;
44822         this.canvasElCtx().fillStyle = this.dot_color;
44823     },
44824     
44825     strokeMoveUpdate: function(e)
44826     {
44827         this.strokeUpdate(e);
44828         
44829         if (this.throttle) {
44830             this.throttleStroke(this.strokeUpdate, this.throttle);
44831         }
44832         else {
44833             this.strokeUpdate(e);
44834         }
44835     },
44836     
44837     strokeBegin: function(e)
44838     {
44839         var newPointGroup = {
44840             color: this.dot_color,
44841             points: []
44842         };
44843         
44844         if (typeof this.onBegin === 'function') {
44845             this.onBegin(e);
44846         }
44847         
44848         this.curve_data.push(newPointGroup);
44849         this.reset();
44850         this.strokeUpdate(e);
44851     },
44852     
44853     strokeUpdate: function(e)
44854     {
44855         var rect = this.canvasEl().dom.getBoundingClientRect();
44856         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44857         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44858         var lastPoints = lastPointGroup.points;
44859         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44860         var isLastPointTooClose = lastPoint
44861             ? point.distanceTo(lastPoint) <= this.min_distance
44862             : false;
44863         var color = lastPointGroup.color;
44864         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44865             var curve = this.addPoint(point);
44866             if (!lastPoint) {
44867                 this.drawDot({color: color, point: point});
44868             }
44869             else if (curve) {
44870                 this.drawCurve({color: color, curve: curve});
44871             }
44872             lastPoints.push({
44873                 time: point.time,
44874                 x: point.x,
44875                 y: point.y
44876             });
44877         }
44878     },
44879     
44880     strokeEnd: function(e)
44881     {
44882         this.strokeUpdate(e);
44883         if (typeof this.onEnd === 'function') {
44884             this.onEnd(e);
44885         }
44886     },
44887     
44888     addPoint:  function (point) {
44889         var _lastPoints = this._lastPoints;
44890         _lastPoints.push(point);
44891         if (_lastPoints.length > 2) {
44892             if (_lastPoints.length === 3) {
44893                 _lastPoints.unshift(_lastPoints[0]);
44894             }
44895             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44896             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44897             _lastPoints.shift();
44898             return curve;
44899         }
44900         return null;
44901     },
44902     
44903     calculateCurveWidths: function (startPoint, endPoint) {
44904         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44905             (1 - this.velocity_filter_weight) * this._lastVelocity;
44906
44907         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44908         var widths = {
44909             end: newWidth,
44910             start: this._lastWidth
44911         };
44912         
44913         this._lastVelocity = velocity;
44914         this._lastWidth = newWidth;
44915         return widths;
44916     },
44917     
44918     drawDot: function (_a) {
44919         var color = _a.color, point = _a.point;
44920         var ctx = this.canvasElCtx();
44921         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44922         ctx.beginPath();
44923         this.drawCurveSegment(point.x, point.y, width);
44924         ctx.closePath();
44925         ctx.fillStyle = color;
44926         ctx.fill();
44927     },
44928     
44929     drawCurve: function (_a) {
44930         var color = _a.color, curve = _a.curve;
44931         var ctx = this.canvasElCtx();
44932         var widthDelta = curve.endWidth - curve.startWidth;
44933         var drawSteps = Math.floor(curve.length()) * 2;
44934         ctx.beginPath();
44935         ctx.fillStyle = color;
44936         for (var i = 0; i < drawSteps; i += 1) {
44937         var t = i / drawSteps;
44938         var tt = t * t;
44939         var ttt = tt * t;
44940         var u = 1 - t;
44941         var uu = u * u;
44942         var uuu = uu * u;
44943         var x = uuu * curve.startPoint.x;
44944         x += 3 * uu * t * curve.control1.x;
44945         x += 3 * u * tt * curve.control2.x;
44946         x += ttt * curve.endPoint.x;
44947         var y = uuu * curve.startPoint.y;
44948         y += 3 * uu * t * curve.control1.y;
44949         y += 3 * u * tt * curve.control2.y;
44950         y += ttt * curve.endPoint.y;
44951         var width = curve.startWidth + ttt * widthDelta;
44952         this.drawCurveSegment(x, y, width);
44953         }
44954         ctx.closePath();
44955         ctx.fill();
44956     },
44957     
44958     drawCurveSegment: function (x, y, width) {
44959         var ctx = this.canvasElCtx();
44960         ctx.moveTo(x, y);
44961         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44962         this.is_empty = false;
44963     },
44964     
44965     clear: function()
44966     {
44967         var ctx = this.canvasElCtx();
44968         var canvas = this.canvasEl().dom;
44969         ctx.fillStyle = this.bg_color;
44970         ctx.clearRect(0, 0, canvas.width, canvas.height);
44971         ctx.fillRect(0, 0, canvas.width, canvas.height);
44972         this.curve_data = [];
44973         this.reset();
44974         this.is_empty = true;
44975     },
44976     
44977     fileEl: function()
44978     {
44979         return  this.el.select('input',true).first();
44980     },
44981     
44982     canvasEl: function()
44983     {
44984         return this.el.select('canvas',true).first();
44985     },
44986     
44987     canvasElCtx: function()
44988     {
44989         return this.el.select('canvas',true).first().dom.getContext('2d');
44990     },
44991     
44992     getImage: function(type)
44993     {
44994         if(this.is_empty) {
44995             return false;
44996         }
44997         
44998         // encryption ?
44999         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45000     },
45001     
45002     drawFromImage: function(img_src)
45003     {
45004         var img = new Image();
45005         
45006         img.onload = function(){
45007             this.canvasElCtx().drawImage(img, 0, 0);
45008         }.bind(this);
45009         
45010         img.src = img_src;
45011         
45012         this.is_empty = false;
45013     },
45014     
45015     selectImage: function()
45016     {
45017         this.fileEl().dom.click();
45018     },
45019     
45020     uploadImage: function(e)
45021     {
45022         var reader = new FileReader();
45023         
45024         reader.onload = function(e){
45025             var img = new Image();
45026             img.onload = function(){
45027                 this.reset();
45028                 this.canvasElCtx().drawImage(img, 0, 0);
45029             }.bind(this);
45030             img.src = e.target.result;
45031         }.bind(this);
45032         
45033         reader.readAsDataURL(e.target.files[0]);
45034     },
45035     
45036     // Bezier Point Constructor
45037     Point: (function () {
45038         function Point(x, y, time) {
45039             this.x = x;
45040             this.y = y;
45041             this.time = time || Date.now();
45042         }
45043         Point.prototype.distanceTo = function (start) {
45044             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45045         };
45046         Point.prototype.equals = function (other) {
45047             return this.x === other.x && this.y === other.y && this.time === other.time;
45048         };
45049         Point.prototype.velocityFrom = function (start) {
45050             return this.time !== start.time
45051             ? this.distanceTo(start) / (this.time - start.time)
45052             : 0;
45053         };
45054         return Point;
45055     }()),
45056     
45057     
45058     // Bezier Constructor
45059     Bezier: (function () {
45060         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45061             this.startPoint = startPoint;
45062             this.control2 = control2;
45063             this.control1 = control1;
45064             this.endPoint = endPoint;
45065             this.startWidth = startWidth;
45066             this.endWidth = endWidth;
45067         }
45068         Bezier.fromPoints = function (points, widths, scope) {
45069             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45070             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45071             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45072         };
45073         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45074             var dx1 = s1.x - s2.x;
45075             var dy1 = s1.y - s2.y;
45076             var dx2 = s2.x - s3.x;
45077             var dy2 = s2.y - s3.y;
45078             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45079             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45080             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45081             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45082             var dxm = m1.x - m2.x;
45083             var dym = m1.y - m2.y;
45084             var k = l2 / (l1 + l2);
45085             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45086             var tx = s2.x - cm.x;
45087             var ty = s2.y - cm.y;
45088             return {
45089                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45090                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45091             };
45092         };
45093         Bezier.prototype.length = function () {
45094             var steps = 10;
45095             var length = 0;
45096             var px;
45097             var py;
45098             for (var i = 0; i <= steps; i += 1) {
45099                 var t = i / steps;
45100                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45101                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45102                 if (i > 0) {
45103                     var xdiff = cx - px;
45104                     var ydiff = cy - py;
45105                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45106                 }
45107                 px = cx;
45108                 py = cy;
45109             }
45110             return length;
45111         };
45112         Bezier.prototype.point = function (t, start, c1, c2, end) {
45113             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45114             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45115             + (3.0 * c2 * (1.0 - t) * t * t)
45116             + (end * t * t * t);
45117         };
45118         return Bezier;
45119     }()),
45120     
45121     throttleStroke: function(fn, wait) {
45122       if (wait === void 0) { wait = 250; }
45123       var previous = 0;
45124       var timeout = null;
45125       var result;
45126       var storedContext;
45127       var storedArgs;
45128       var later = function () {
45129           previous = Date.now();
45130           timeout = null;
45131           result = fn.apply(storedContext, storedArgs);
45132           if (!timeout) {
45133               storedContext = null;
45134               storedArgs = [];
45135           }
45136       };
45137       return function wrapper() {
45138           var args = [];
45139           for (var _i = 0; _i < arguments.length; _i++) {
45140               args[_i] = arguments[_i];
45141           }
45142           var now = Date.now();
45143           var remaining = wait - (now - previous);
45144           storedContext = this;
45145           storedArgs = args;
45146           if (remaining <= 0 || remaining > wait) {
45147               if (timeout) {
45148                   clearTimeout(timeout);
45149                   timeout = null;
45150               }
45151               previous = now;
45152               result = fn.apply(storedContext, storedArgs);
45153               if (!timeout) {
45154                   storedContext = null;
45155                   storedArgs = [];
45156               }
45157           }
45158           else if (!timeout) {
45159               timeout = window.setTimeout(later, remaining);
45160           }
45161           return result;
45162       };
45163   }
45164   
45165 });
45166
45167  
45168
45169  // old names for form elements
45170 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45171 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45172 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45173 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45174 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45175 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45176 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45177 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45178 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45179 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45180 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45181 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45182 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45183 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45184 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45185 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45186 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45187 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45188 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45189 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45190 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45191 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45192 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45193 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45194 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45195 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45196
45197 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45198 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45199
45200 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45201 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45202
45203 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45204 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45205 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45206 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45207