roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); 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  * @builder-top
21491  * @parent none
21492  * @children Roo.bootstrap.Component
21493  * Bootstrap Popover class
21494  * @cfg {String} html contents of the popover   (or false to use children..)
21495  * @cfg {String} title of popover (or false to hide)
21496  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21497  * @cfg {String} trigger click || hover (or false to trigger manually)
21498  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21499  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21500  *      - if false and it has a 'parent' then it will be automatically added to that element
21501  *      - if string - Roo.get  will be called 
21502  * @cfg {Number} delay - delay before showing
21503  
21504  * @constructor
21505  * Create a new Popover
21506  * @param {Object} config The config object
21507  */
21508
21509 Roo.bootstrap.Popover = function(config){
21510     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21511     
21512     this.addEvents({
21513         // raw events
21514          /**
21515          * @event show
21516          * After the popover show
21517          * 
21518          * @param {Roo.bootstrap.Popover} this
21519          */
21520         "show" : true,
21521         /**
21522          * @event hide
21523          * After the popover hide
21524          * 
21525          * @param {Roo.bootstrap.Popover} this
21526          */
21527         "hide" : true
21528     });
21529 };
21530
21531 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21532     
21533     title: false,
21534     html: false,
21535     
21536     placement : 'right',
21537     trigger : 'hover', // hover
21538     modal : false,
21539     delay : 0,
21540     
21541     over: false,
21542     
21543     can_build_overlaid : false,
21544     
21545     maskEl : false, // the mask element
21546     headerEl : false,
21547     contentEl : false,
21548     alignEl : false, // when show is called with an element - this get's stored.
21549     
21550     getChildContainer : function()
21551     {
21552         return this.contentEl;
21553         
21554     },
21555     getPopoverHeader : function()
21556     {
21557         this.title = true; // flag not to hide it..
21558         this.headerEl.addClass('p-0');
21559         return this.headerEl
21560     },
21561     
21562     
21563     getAutoCreate : function(){
21564          
21565         var cfg = {
21566            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21567            style: 'display:block',
21568            cn : [
21569                 {
21570                     cls : 'arrow'
21571                 },
21572                 {
21573                     cls : 'popover-inner ',
21574                     cn : [
21575                         {
21576                             tag: 'h3',
21577                             cls: 'popover-title popover-header',
21578                             html : this.title === false ? '' : this.title
21579                         },
21580                         {
21581                             cls : 'popover-content popover-body '  + (this.cls || ''),
21582                             html : this.html || ''
21583                         }
21584                     ]
21585                     
21586                 }
21587            ]
21588         };
21589         
21590         return cfg;
21591     },
21592     /**
21593      * @param {string} the title
21594      */
21595     setTitle: function(str)
21596     {
21597         this.title = str;
21598         if (this.el) {
21599             this.headerEl.dom.innerHTML = str;
21600         }
21601         
21602     },
21603     /**
21604      * @param {string} the body content
21605      */
21606     setContent: function(str)
21607     {
21608         this.html = str;
21609         if (this.contentEl) {
21610             this.contentEl.dom.innerHTML = str;
21611         }
21612         
21613     },
21614     // as it get's added to the bottom of the page.
21615     onRender : function(ct, position)
21616     {
21617         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21618         
21619         
21620         
21621         if(!this.el){
21622             var cfg = Roo.apply({},  this.getAutoCreate());
21623             cfg.id = Roo.id();
21624             
21625             if (this.cls) {
21626                 cfg.cls += ' ' + this.cls;
21627             }
21628             if (this.style) {
21629                 cfg.style = this.style;
21630             }
21631             //Roo.log("adding to ");
21632             this.el = Roo.get(document.body).createChild(cfg, position);
21633 //            Roo.log(this.el);
21634         }
21635         
21636         this.contentEl = this.el.select('.popover-content',true).first();
21637         this.headerEl =  this.el.select('.popover-title',true).first();
21638         
21639         var nitems = [];
21640         if(typeof(this.items) != 'undefined'){
21641             var items = this.items;
21642             delete this.items;
21643
21644             for(var i =0;i < items.length;i++) {
21645                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21646             }
21647         }
21648
21649         this.items = nitems;
21650         
21651         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21652         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21653         
21654         
21655         
21656         this.initEvents();
21657     },
21658     
21659     resizeMask : function()
21660     {
21661         this.maskEl.setSize(
21662             Roo.lib.Dom.getViewWidth(true),
21663             Roo.lib.Dom.getViewHeight(true)
21664         );
21665     },
21666     
21667     initEvents : function()
21668     {
21669         
21670         if (!this.modal) { 
21671             Roo.bootstrap.Popover.register(this);
21672         }
21673          
21674         this.arrowEl = this.el.select('.arrow',true).first();
21675         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21676         this.el.enableDisplayMode('block');
21677         this.el.hide();
21678  
21679         
21680         if (this.over === false && !this.parent()) {
21681             return; 
21682         }
21683         if (this.triggers === false) {
21684             return;
21685         }
21686          
21687         // support parent
21688         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21689         var triggers = this.trigger ? this.trigger.split(' ') : [];
21690         Roo.each(triggers, function(trigger) {
21691         
21692             if (trigger == 'click') {
21693                 on_el.on('click', this.toggle, this);
21694             } else if (trigger != 'manual') {
21695                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21696                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21697       
21698                 on_el.on(eventIn  ,this.enter, this);
21699                 on_el.on(eventOut, this.leave, this);
21700             }
21701         }, this);
21702     },
21703     
21704     
21705     // private
21706     timeout : null,
21707     hoverState : null,
21708     
21709     toggle : function () {
21710         this.hoverState == 'in' ? this.leave() : this.enter();
21711     },
21712     
21713     enter : function () {
21714         
21715         clearTimeout(this.timeout);
21716     
21717         this.hoverState = 'in';
21718     
21719         if (!this.delay || !this.delay.show) {
21720             this.show();
21721             return;
21722         }
21723         var _t = this;
21724         this.timeout = setTimeout(function () {
21725             if (_t.hoverState == 'in') {
21726                 _t.show();
21727             }
21728         }, this.delay.show)
21729     },
21730     
21731     leave : function() {
21732         clearTimeout(this.timeout);
21733     
21734         this.hoverState = 'out';
21735     
21736         if (!this.delay || !this.delay.hide) {
21737             this.hide();
21738             return;
21739         }
21740         var _t = this;
21741         this.timeout = setTimeout(function () {
21742             if (_t.hoverState == 'out') {
21743                 _t.hide();
21744             }
21745         }, this.delay.hide)
21746     },
21747     
21748     /**
21749      * update the position of the dialog
21750      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21751      * 
21752      *
21753      */
21754     
21755     doAlign : function()
21756     {
21757         
21758         if (this.alignEl) {
21759             this.updatePosition(this.placement, true);
21760              
21761         } else {
21762             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21763             var es = this.el.getSize();
21764             var x = Roo.lib.Dom.getViewWidth()/2;
21765             var y = Roo.lib.Dom.getViewHeight()/2;
21766             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21767             
21768         }
21769
21770          
21771          
21772         
21773         
21774     },
21775     
21776     /**
21777      * Show the popover
21778      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21779      * @param {string} (left|right|top|bottom) position
21780      */
21781     show : function (on_el, placement)
21782     {
21783         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21784         on_el = on_el || false; // default to false
21785          
21786         if (!on_el) {
21787             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21788                 on_el = this.parent().el;
21789             } else if (this.over) {
21790                 on_el = Roo.get(this.over);
21791             }
21792             
21793         }
21794         
21795         this.alignEl = Roo.get( on_el );
21796
21797         if (!this.el) {
21798             this.render(document.body);
21799         }
21800         
21801         
21802          
21803         
21804         if (this.title === false) {
21805             this.headerEl.hide();
21806         }
21807         
21808        
21809         this.el.show();
21810         this.el.dom.style.display = 'block';
21811          
21812         this.doAlign();
21813         
21814         //var arrow = this.el.select('.arrow',true).first();
21815         //arrow.set(align[2], 
21816         
21817         this.el.addClass('in');
21818         
21819          
21820         
21821         this.hoverState = 'in';
21822         
21823         if (this.modal) {
21824             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21825             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21826             this.maskEl.dom.style.display = 'block';
21827             this.maskEl.addClass('show');
21828         }
21829         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21830  
21831         this.fireEvent('show', this);
21832         
21833     },
21834     /**
21835      * fire this manually after loading a grid in the table for example
21836      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21837      * @param {Boolean} try and move it if we cant get right position.
21838      */
21839     updatePosition : function(placement, try_move)
21840     {
21841         // allow for calling with no parameters
21842         placement = placement   ? placement :  this.placement;
21843         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21844         
21845         this.el.removeClass([
21846             'fade','top','bottom', 'left', 'right','in',
21847             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21848         ]);
21849         this.el.addClass(placement + ' bs-popover-' + placement);
21850         
21851         if (!this.alignEl ) {
21852             return false;
21853         }
21854         
21855         switch (placement) {
21856             case 'right':
21857                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21858                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21859                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21860                     //normal display... or moved up/down.
21861                     this.el.setXY(offset);
21862                     var xy = this.alignEl.getAnchorXY('tr', false);
21863                     xy[0]+=2;xy[1]+=5;
21864                     this.arrowEl.setXY(xy);
21865                     return true;
21866                 }
21867                 // continue through...
21868                 return this.updatePosition('left', false);
21869                 
21870             
21871             case 'left':
21872                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21873                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21874                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21875                     //normal display... or moved up/down.
21876                     this.el.setXY(offset);
21877                     var xy = this.alignEl.getAnchorXY('tl', false);
21878                     xy[0]-=10;xy[1]+=5; // << fix me
21879                     this.arrowEl.setXY(xy);
21880                     return true;
21881                 }
21882                 // call self...
21883                 return this.updatePosition('right', false);
21884             
21885             case 'top':
21886                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21887                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21888                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21889                     //normal display... or moved up/down.
21890                     this.el.setXY(offset);
21891                     var xy = this.alignEl.getAnchorXY('t', false);
21892                     xy[1]-=10; // << fix me
21893                     this.arrowEl.setXY(xy);
21894                     return true;
21895                 }
21896                 // fall through
21897                return this.updatePosition('bottom', false);
21898             
21899             case 'bottom':
21900                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21901                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21902                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21903                     //normal display... or moved up/down.
21904                     this.el.setXY(offset);
21905                     var xy = this.alignEl.getAnchorXY('b', false);
21906                      xy[1]+=2; // << fix me
21907                     this.arrowEl.setXY(xy);
21908                     return true;
21909                 }
21910                 // fall through
21911                 return this.updatePosition('top', false);
21912                 
21913             
21914         }
21915         
21916         
21917         return false;
21918     },
21919     
21920     hide : function()
21921     {
21922         this.el.setXY([0,0]);
21923         this.el.removeClass('in');
21924         this.el.hide();
21925         this.hoverState = null;
21926         this.maskEl.hide(); // always..
21927         this.fireEvent('hide', this);
21928     }
21929     
21930 });
21931
21932
21933 Roo.apply(Roo.bootstrap.Popover, {
21934
21935     alignment : {
21936         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21937         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21938         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21939         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21940     },
21941     
21942     zIndex : 20001,
21943
21944     clickHander : false,
21945     
21946     
21947
21948     onMouseDown : function(e)
21949     {
21950         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21951             /// what is nothing is showing..
21952             this.hideAll();
21953         }
21954          
21955     },
21956     
21957     
21958     popups : [],
21959     
21960     register : function(popup)
21961     {
21962         if (!Roo.bootstrap.Popover.clickHandler) {
21963             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21964         }
21965         // hide other popups.
21966         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21967         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21968         this.hideAll(); //<< why?
21969         //this.popups.push(popup);
21970     },
21971     hideAll : function()
21972     {
21973         this.popups.forEach(function(p) {
21974             p.hide();
21975         });
21976     },
21977     onShow : function() {
21978         Roo.bootstrap.Popover.popups.push(this);
21979     },
21980     onHide : function() {
21981         Roo.bootstrap.Popover.popups.remove(this);
21982     } 
21983
21984 });
21985 /**
21986  * @class Roo.bootstrap.PopoverNav
21987  * @extends Roo.bootstrap.nav.Simplebar
21988  * @parent Roo.bootstrap.Popover
21989  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21990  * @licence LGPL
21991  * Bootstrap Popover header navigation class
21992  * FIXME? should this go under nav?
21993  *
21994  * 
21995  * @constructor
21996  * Create a new Popover Header Navigation 
21997  * @param {Object} config The config object
21998  */
21999
22000 Roo.bootstrap.PopoverNav = function(config){
22001     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22002 };
22003
22004 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22005     
22006     
22007     container_method : 'getPopoverHeader' 
22008     
22009      
22010     
22011     
22012    
22013 });
22014
22015  
22016
22017  /*
22018  * - LGPL
22019  *
22020  * Progress
22021  * 
22022  */
22023
22024 /**
22025  * @class Roo.bootstrap.Progress
22026  * @extends Roo.bootstrap.Component
22027  * @children Roo.bootstrap.ProgressBar
22028  * Bootstrap Progress class
22029  * @cfg {Boolean} striped striped of the progress bar
22030  * @cfg {Boolean} active animated of the progress bar
22031  * 
22032  * 
22033  * @constructor
22034  * Create a new Progress
22035  * @param {Object} config The config object
22036  */
22037
22038 Roo.bootstrap.Progress = function(config){
22039     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22040 };
22041
22042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22043     
22044     striped : false,
22045     active: false,
22046     
22047     getAutoCreate : function(){
22048         var cfg = {
22049             tag: 'div',
22050             cls: 'progress'
22051         };
22052         
22053         
22054         if(this.striped){
22055             cfg.cls += ' progress-striped';
22056         }
22057       
22058         if(this.active){
22059             cfg.cls += ' active';
22060         }
22061         
22062         
22063         return cfg;
22064     }
22065    
22066 });
22067
22068  
22069
22070  /*
22071  * - LGPL
22072  *
22073  * ProgressBar
22074  * 
22075  */
22076
22077 /**
22078  * @class Roo.bootstrap.ProgressBar
22079  * @extends Roo.bootstrap.Component
22080  * Bootstrap ProgressBar class
22081  * @cfg {Number} aria_valuenow aria-value now
22082  * @cfg {Number} aria_valuemin aria-value min
22083  * @cfg {Number} aria_valuemax aria-value max
22084  * @cfg {String} label label for the progress bar
22085  * @cfg {String} panel (success | info | warning | danger )
22086  * @cfg {String} role role of the progress bar
22087  * @cfg {String} sr_only text
22088  * 
22089  * 
22090  * @constructor
22091  * Create a new ProgressBar
22092  * @param {Object} config The config object
22093  */
22094
22095 Roo.bootstrap.ProgressBar = function(config){
22096     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22097 };
22098
22099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22100     
22101     aria_valuenow : 0,
22102     aria_valuemin : 0,
22103     aria_valuemax : 100,
22104     label : false,
22105     panel : false,
22106     role : false,
22107     sr_only: false,
22108     
22109     getAutoCreate : function()
22110     {
22111         
22112         var cfg = {
22113             tag: 'div',
22114             cls: 'progress-bar',
22115             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22116         };
22117         
22118         if(this.sr_only){
22119             cfg.cn = {
22120                 tag: 'span',
22121                 cls: 'sr-only',
22122                 html: this.sr_only
22123             }
22124         }
22125         
22126         if(this.role){
22127             cfg.role = this.role;
22128         }
22129         
22130         if(this.aria_valuenow){
22131             cfg['aria-valuenow'] = this.aria_valuenow;
22132         }
22133         
22134         if(this.aria_valuemin){
22135             cfg['aria-valuemin'] = this.aria_valuemin;
22136         }
22137         
22138         if(this.aria_valuemax){
22139             cfg['aria-valuemax'] = this.aria_valuemax;
22140         }
22141         
22142         if(this.label && !this.sr_only){
22143             cfg.html = this.label;
22144         }
22145         
22146         if(this.panel){
22147             cfg.cls += ' progress-bar-' + this.panel;
22148         }
22149         
22150         return cfg;
22151     },
22152     
22153     update : function(aria_valuenow)
22154     {
22155         this.aria_valuenow = aria_valuenow;
22156         
22157         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22158     }
22159    
22160 });
22161
22162  
22163
22164  /**
22165  * @class Roo.bootstrap.TabGroup
22166  * @extends Roo.bootstrap.Column
22167  * @children Roo.bootstrap.TabPanel
22168  * Bootstrap Column class
22169  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22170  * @cfg {Boolean} carousel true to make the group behave like a carousel
22171  * @cfg {Boolean} bullets show bullets for the panels
22172  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22173  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22174  * @cfg {Boolean} showarrow (true|false) show arrow default true
22175  * 
22176  * @constructor
22177  * Create a new TabGroup
22178  * @param {Object} config The config object
22179  */
22180
22181 Roo.bootstrap.TabGroup = function(config){
22182     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22183     if (!this.navId) {
22184         this.navId = Roo.id();
22185     }
22186     this.tabs = [];
22187     Roo.bootstrap.TabGroup.register(this);
22188     
22189 };
22190
22191 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22192     
22193     carousel : false,
22194     transition : false,
22195     bullets : 0,
22196     timer : 0,
22197     autoslide : false,
22198     slideFn : false,
22199     slideOnTouch : false,
22200     showarrow : true,
22201     
22202     getAutoCreate : function()
22203     {
22204         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22205         
22206         cfg.cls += ' tab-content';
22207         
22208         if (this.carousel) {
22209             cfg.cls += ' carousel slide';
22210             
22211             cfg.cn = [{
22212                cls : 'carousel-inner',
22213                cn : []
22214             }];
22215         
22216             if(this.bullets  && !Roo.isTouch){
22217                 
22218                 var bullets = {
22219                     cls : 'carousel-bullets',
22220                     cn : []
22221                 };
22222                
22223                 if(this.bullets_cls){
22224                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22225                 }
22226                 
22227                 bullets.cn.push({
22228                     cls : 'clear'
22229                 });
22230                 
22231                 cfg.cn[0].cn.push(bullets);
22232             }
22233             
22234             if(this.showarrow){
22235                 cfg.cn[0].cn.push({
22236                     tag : 'div',
22237                     class : 'carousel-arrow',
22238                     cn : [
22239                         {
22240                             tag : 'div',
22241                             class : 'carousel-prev',
22242                             cn : [
22243                                 {
22244                                     tag : 'i',
22245                                     class : 'fa fa-chevron-left'
22246                                 }
22247                             ]
22248                         },
22249                         {
22250                             tag : 'div',
22251                             class : 'carousel-next',
22252                             cn : [
22253                                 {
22254                                     tag : 'i',
22255                                     class : 'fa fa-chevron-right'
22256                                 }
22257                             ]
22258                         }
22259                     ]
22260                 });
22261             }
22262             
22263         }
22264         
22265         return cfg;
22266     },
22267     
22268     initEvents:  function()
22269     {
22270 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22271 //            this.el.on("touchstart", this.onTouchStart, this);
22272 //        }
22273         
22274         if(this.autoslide){
22275             var _this = this;
22276             
22277             this.slideFn = window.setInterval(function() {
22278                 _this.showPanelNext();
22279             }, this.timer);
22280         }
22281         
22282         if(this.showarrow){
22283             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22284             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22285         }
22286         
22287         
22288     },
22289     
22290 //    onTouchStart : function(e, el, o)
22291 //    {
22292 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22293 //            return;
22294 //        }
22295 //        
22296 //        this.showPanelNext();
22297 //    },
22298     
22299     
22300     getChildContainer : function()
22301     {
22302         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22303     },
22304     
22305     /**
22306     * register a Navigation item
22307     * @param {Roo.bootstrap.nav.Item} the navitem to add
22308     */
22309     register : function(item)
22310     {
22311         this.tabs.push( item);
22312         item.navId = this.navId; // not really needed..
22313         this.addBullet();
22314     
22315     },
22316     
22317     getActivePanel : function()
22318     {
22319         var r = false;
22320         Roo.each(this.tabs, function(t) {
22321             if (t.active) {
22322                 r = t;
22323                 return false;
22324             }
22325             return null;
22326         });
22327         return r;
22328         
22329     },
22330     getPanelByName : function(n)
22331     {
22332         var r = false;
22333         Roo.each(this.tabs, function(t) {
22334             if (t.tabId == n) {
22335                 r = t;
22336                 return false;
22337             }
22338             return null;
22339         });
22340         return r;
22341     },
22342     indexOfPanel : function(p)
22343     {
22344         var r = false;
22345         Roo.each(this.tabs, function(t,i) {
22346             if (t.tabId == p.tabId) {
22347                 r = i;
22348                 return false;
22349             }
22350             return null;
22351         });
22352         return r;
22353     },
22354     /**
22355      * show a specific panel
22356      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22357      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22358      */
22359     showPanel : function (pan)
22360     {
22361         if(this.transition || typeof(pan) == 'undefined'){
22362             Roo.log("waiting for the transitionend");
22363             return false;
22364         }
22365         
22366         if (typeof(pan) == 'number') {
22367             pan = this.tabs[pan];
22368         }
22369         
22370         if (typeof(pan) == 'string') {
22371             pan = this.getPanelByName(pan);
22372         }
22373         
22374         var cur = this.getActivePanel();
22375         
22376         if(!pan || !cur){
22377             Roo.log('pan or acitve pan is undefined');
22378             return false;
22379         }
22380         
22381         if (pan.tabId == this.getActivePanel().tabId) {
22382             return true;
22383         }
22384         
22385         if (false === cur.fireEvent('beforedeactivate')) {
22386             return false;
22387         }
22388         
22389         if(this.bullets > 0 && !Roo.isTouch){
22390             this.setActiveBullet(this.indexOfPanel(pan));
22391         }
22392         
22393         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22394             
22395             //class="carousel-item carousel-item-next carousel-item-left"
22396             
22397             this.transition = true;
22398             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22399             var lr = dir == 'next' ? 'left' : 'right';
22400             pan.el.addClass(dir); // or prev
22401             pan.el.addClass('carousel-item-' + dir); // or prev
22402             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22403             cur.el.addClass(lr); // or right
22404             pan.el.addClass(lr);
22405             cur.el.addClass('carousel-item-' +lr); // or right
22406             pan.el.addClass('carousel-item-' +lr);
22407             
22408             
22409             var _this = this;
22410             cur.el.on('transitionend', function() {
22411                 Roo.log("trans end?");
22412                 
22413                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22414                 pan.setActive(true);
22415                 
22416                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22417                 cur.setActive(false);
22418                 
22419                 _this.transition = false;
22420                 
22421             }, this, { single:  true } );
22422             
22423             return true;
22424         }
22425         
22426         cur.setActive(false);
22427         pan.setActive(true);
22428         
22429         return true;
22430         
22431     },
22432     showPanelNext : function()
22433     {
22434         var i = this.indexOfPanel(this.getActivePanel());
22435         
22436         if (i >= this.tabs.length - 1 && !this.autoslide) {
22437             return;
22438         }
22439         
22440         if (i >= this.tabs.length - 1 && this.autoslide) {
22441             i = -1;
22442         }
22443         
22444         this.showPanel(this.tabs[i+1]);
22445     },
22446     
22447     showPanelPrev : function()
22448     {
22449         var i = this.indexOfPanel(this.getActivePanel());
22450         
22451         if (i  < 1 && !this.autoslide) {
22452             return;
22453         }
22454         
22455         if (i < 1 && this.autoslide) {
22456             i = this.tabs.length;
22457         }
22458         
22459         this.showPanel(this.tabs[i-1]);
22460     },
22461     
22462     
22463     addBullet: function()
22464     {
22465         if(!this.bullets || Roo.isTouch){
22466             return;
22467         }
22468         var ctr = this.el.select('.carousel-bullets',true).first();
22469         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22470         var bullet = ctr.createChild({
22471             cls : 'bullet bullet-' + i
22472         },ctr.dom.lastChild);
22473         
22474         
22475         var _this = this;
22476         
22477         bullet.on('click', (function(e, el, o, ii, t){
22478
22479             e.preventDefault();
22480
22481             this.showPanel(ii);
22482
22483             if(this.autoslide && this.slideFn){
22484                 clearInterval(this.slideFn);
22485                 this.slideFn = window.setInterval(function() {
22486                     _this.showPanelNext();
22487                 }, this.timer);
22488             }
22489
22490         }).createDelegate(this, [i, bullet], true));
22491                 
22492         
22493     },
22494      
22495     setActiveBullet : function(i)
22496     {
22497         if(Roo.isTouch){
22498             return;
22499         }
22500         
22501         Roo.each(this.el.select('.bullet', true).elements, function(el){
22502             el.removeClass('selected');
22503         });
22504
22505         var bullet = this.el.select('.bullet-' + i, true).first();
22506         
22507         if(!bullet){
22508             return;
22509         }
22510         
22511         bullet.addClass('selected');
22512     }
22513     
22514     
22515   
22516 });
22517
22518  
22519
22520  
22521  
22522 Roo.apply(Roo.bootstrap.TabGroup, {
22523     
22524     groups: {},
22525      /**
22526     * register a Navigation Group
22527     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22528     */
22529     register : function(navgrp)
22530     {
22531         this.groups[navgrp.navId] = navgrp;
22532         
22533     },
22534     /**
22535     * fetch a Navigation Group based on the navigation ID
22536     * if one does not exist , it will get created.
22537     * @param {string} the navgroup to add
22538     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22539     */
22540     get: function(navId) {
22541         if (typeof(this.groups[navId]) == 'undefined') {
22542             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22543         }
22544         return this.groups[navId] ;
22545     }
22546     
22547     
22548     
22549 });
22550
22551  /*
22552  * - LGPL
22553  *
22554  * TabPanel
22555  * 
22556  */
22557
22558 /**
22559  * @class Roo.bootstrap.TabPanel
22560  * @extends Roo.bootstrap.Component
22561  * @children Roo.bootstrap.Component
22562  * Bootstrap TabPanel class
22563  * @cfg {Boolean} active panel active
22564  * @cfg {String} html panel content
22565  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22566  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22567  * @cfg {String} href click to link..
22568  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22569  * 
22570  * 
22571  * @constructor
22572  * Create a new TabPanel
22573  * @param {Object} config The config object
22574  */
22575
22576 Roo.bootstrap.TabPanel = function(config){
22577     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22578     this.addEvents({
22579         /**
22580              * @event changed
22581              * Fires when the active status changes
22582              * @param {Roo.bootstrap.TabPanel} this
22583              * @param {Boolean} state the new state
22584             
22585          */
22586         'changed': true,
22587         /**
22588              * @event beforedeactivate
22589              * Fires before a tab is de-activated - can be used to do validation on a form.
22590              * @param {Roo.bootstrap.TabPanel} this
22591              * @return {Boolean} false if there is an error
22592             
22593          */
22594         'beforedeactivate': true
22595      });
22596     
22597     this.tabId = this.tabId || Roo.id();
22598   
22599 };
22600
22601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22602     
22603     active: false,
22604     html: false,
22605     tabId: false,
22606     navId : false,
22607     href : '',
22608     touchSlide : false,
22609     getAutoCreate : function(){
22610         
22611         
22612         var cfg = {
22613             tag: 'div',
22614             // item is needed for carousel - not sure if it has any effect otherwise
22615             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22616             html: this.html || ''
22617         };
22618         
22619         if(this.active){
22620             cfg.cls += ' active';
22621         }
22622         
22623         if(this.tabId){
22624             cfg.tabId = this.tabId;
22625         }
22626         
22627         
22628         
22629         return cfg;
22630     },
22631     
22632     initEvents:  function()
22633     {
22634         var p = this.parent();
22635         
22636         this.navId = this.navId || p.navId;
22637         
22638         if (typeof(this.navId) != 'undefined') {
22639             // not really needed.. but just in case.. parent should be a NavGroup.
22640             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22641             
22642             tg.register(this);
22643             
22644             var i = tg.tabs.length - 1;
22645             
22646             if(this.active && tg.bullets > 0 && i < tg.bullets){
22647                 tg.setActiveBullet(i);
22648             }
22649         }
22650         
22651         this.el.on('click', this.onClick, this);
22652         
22653         if(Roo.isTouch && this.touchSlide){
22654             this.el.on("touchstart", this.onTouchStart, this);
22655             this.el.on("touchmove", this.onTouchMove, this);
22656             this.el.on("touchend", this.onTouchEnd, this);
22657         }
22658         
22659     },
22660     
22661     onRender : function(ct, position)
22662     {
22663         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22664     },
22665     
22666     setActive : function(state)
22667     {
22668         Roo.log("panel - set active " + this.tabId + "=" + state);
22669         
22670         this.active = state;
22671         if (!state) {
22672             this.el.removeClass('active');
22673             
22674         } else  if (!this.el.hasClass('active')) {
22675             this.el.addClass('active');
22676         }
22677         
22678         this.fireEvent('changed', this, state);
22679     },
22680     
22681     onClick : function(e)
22682     {
22683         e.preventDefault();
22684         
22685         if(!this.href.length){
22686             return;
22687         }
22688         
22689         window.location.href = this.href;
22690     },
22691     
22692     startX : 0,
22693     startY : 0,
22694     endX : 0,
22695     endY : 0,
22696     swiping : false,
22697     
22698     onTouchStart : function(e)
22699     {
22700         this.swiping = false;
22701         
22702         this.startX = e.browserEvent.touches[0].clientX;
22703         this.startY = e.browserEvent.touches[0].clientY;
22704     },
22705     
22706     onTouchMove : function(e)
22707     {
22708         this.swiping = true;
22709         
22710         this.endX = e.browserEvent.touches[0].clientX;
22711         this.endY = e.browserEvent.touches[0].clientY;
22712     },
22713     
22714     onTouchEnd : function(e)
22715     {
22716         if(!this.swiping){
22717             this.onClick(e);
22718             return;
22719         }
22720         
22721         var tabGroup = this.parent();
22722         
22723         if(this.endX > this.startX){ // swiping right
22724             tabGroup.showPanelPrev();
22725             return;
22726         }
22727         
22728         if(this.startX > this.endX){ // swiping left
22729             tabGroup.showPanelNext();
22730             return;
22731         }
22732     }
22733     
22734     
22735 });
22736  
22737
22738  
22739
22740  /*
22741  * - LGPL
22742  *
22743  * DateField
22744  * 
22745  */
22746
22747 /**
22748  * @class Roo.bootstrap.form.DateField
22749  * @extends Roo.bootstrap.form.Input
22750  * Bootstrap DateField class
22751  * @cfg {Number} weekStart default 0
22752  * @cfg {String} viewMode default empty, (months|years)
22753  * @cfg {String} minViewMode default empty, (months|years)
22754  * @cfg {Number} startDate default -Infinity
22755  * @cfg {Number} endDate default Infinity
22756  * @cfg {Boolean} todayHighlight default false
22757  * @cfg {Boolean} todayBtn default false
22758  * @cfg {Boolean} calendarWeeks default false
22759  * @cfg {Object} daysOfWeekDisabled default empty
22760  * @cfg {Boolean} singleMode default false (true | false)
22761  * 
22762  * @cfg {Boolean} keyboardNavigation default true
22763  * @cfg {String} language default en
22764  * 
22765  * @constructor
22766  * Create a new DateField
22767  * @param {Object} config The config object
22768  */
22769
22770 Roo.bootstrap.form.DateField = function(config){
22771     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22772      this.addEvents({
22773             /**
22774              * @event show
22775              * Fires when this field show.
22776              * @param {Roo.bootstrap.form.DateField} this
22777              * @param {Mixed} date The date value
22778              */
22779             show : true,
22780             /**
22781              * @event show
22782              * Fires when this field hide.
22783              * @param {Roo.bootstrap.form.DateField} this
22784              * @param {Mixed} date The date value
22785              */
22786             hide : true,
22787             /**
22788              * @event select
22789              * Fires when select a date.
22790              * @param {Roo.bootstrap.form.DateField} this
22791              * @param {Mixed} date The date value
22792              */
22793             select : true,
22794             /**
22795              * @event beforeselect
22796              * Fires when before select a date.
22797              * @param {Roo.bootstrap.form.DateField} this
22798              * @param {Mixed} date The date value
22799              */
22800             beforeselect : true
22801         });
22802 };
22803
22804 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22805     
22806     /**
22807      * @cfg {String} format
22808      * The default date format string which can be overriden for localization support.  The format must be
22809      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22810      */
22811     format : "m/d/y",
22812     /**
22813      * @cfg {String} altFormats
22814      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22815      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22816      */
22817     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22818     
22819     weekStart : 0,
22820     
22821     viewMode : '',
22822     
22823     minViewMode : '',
22824     
22825     todayHighlight : false,
22826     
22827     todayBtn: false,
22828     
22829     language: 'en',
22830     
22831     keyboardNavigation: true,
22832     
22833     calendarWeeks: false,
22834     
22835     startDate: -Infinity,
22836     
22837     endDate: Infinity,
22838     
22839     daysOfWeekDisabled: [],
22840     
22841     _events: [],
22842     
22843     singleMode : false,
22844     
22845     UTCDate: function()
22846     {
22847         return new Date(Date.UTC.apply(Date, arguments));
22848     },
22849     
22850     UTCToday: function()
22851     {
22852         var today = new Date();
22853         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22854     },
22855     
22856     getDate: function() {
22857             var d = this.getUTCDate();
22858             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22859     },
22860     
22861     getUTCDate: function() {
22862             return this.date;
22863     },
22864     
22865     setDate: function(d) {
22866             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22867     },
22868     
22869     setUTCDate: function(d) {
22870             this.date = d;
22871             this.setValue(this.formatDate(this.date));
22872     },
22873         
22874     onRender: function(ct, position)
22875     {
22876         
22877         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22878         
22879         this.language = this.language || 'en';
22880         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22881         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22882         
22883         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22884         this.format = this.format || 'm/d/y';
22885         this.isInline = false;
22886         this.isInput = true;
22887         this.component = this.el.select('.add-on', true).first() || false;
22888         this.component = (this.component && this.component.length === 0) ? false : this.component;
22889         this.hasInput = this.component && this.inputEl().length;
22890         
22891         if (typeof(this.minViewMode === 'string')) {
22892             switch (this.minViewMode) {
22893                 case 'months':
22894                     this.minViewMode = 1;
22895                     break;
22896                 case 'years':
22897                     this.minViewMode = 2;
22898                     break;
22899                 default:
22900                     this.minViewMode = 0;
22901                     break;
22902             }
22903         }
22904         
22905         if (typeof(this.viewMode === 'string')) {
22906             switch (this.viewMode) {
22907                 case 'months':
22908                     this.viewMode = 1;
22909                     break;
22910                 case 'years':
22911                     this.viewMode = 2;
22912                     break;
22913                 default:
22914                     this.viewMode = 0;
22915                     break;
22916             }
22917         }
22918                 
22919         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22920         
22921 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22922         
22923         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22924         
22925         this.picker().on('mousedown', this.onMousedown, this);
22926         this.picker().on('click', this.onClick, this);
22927         
22928         this.picker().addClass('datepicker-dropdown');
22929         
22930         this.startViewMode = this.viewMode;
22931         
22932         if(this.singleMode){
22933             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22934                 v.setVisibilityMode(Roo.Element.DISPLAY);
22935                 v.hide();
22936             });
22937             
22938             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22939                 v.setStyle('width', '189px');
22940             });
22941         }
22942         
22943         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22944             if(!this.calendarWeeks){
22945                 v.remove();
22946                 return;
22947             }
22948             
22949             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22950             v.attr('colspan', function(i, val){
22951                 return parseInt(val) + 1;
22952             });
22953         });
22954                         
22955         
22956         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22957         
22958         this.setStartDate(this.startDate);
22959         this.setEndDate(this.endDate);
22960         
22961         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22962         
22963         this.fillDow();
22964         this.fillMonths();
22965         this.update();
22966         this.showMode();
22967         
22968         if(this.isInline) {
22969             this.showPopup();
22970         }
22971     },
22972     
22973     picker : function()
22974     {
22975         return this.pickerEl;
22976 //        return this.el.select('.datepicker', true).first();
22977     },
22978     
22979     fillDow: function()
22980     {
22981         var dowCnt = this.weekStart;
22982         
22983         var dow = {
22984             tag: 'tr',
22985             cn: [
22986                 
22987             ]
22988         };
22989         
22990         if(this.calendarWeeks){
22991             dow.cn.push({
22992                 tag: 'th',
22993                 cls: 'cw',
22994                 html: '&nbsp;'
22995             })
22996         }
22997         
22998         while (dowCnt < this.weekStart + 7) {
22999             dow.cn.push({
23000                 tag: 'th',
23001                 cls: 'dow',
23002                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23003             });
23004         }
23005         
23006         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23007     },
23008     
23009     fillMonths: function()
23010     {    
23011         var i = 0;
23012         var months = this.picker().select('>.datepicker-months td', true).first();
23013         
23014         months.dom.innerHTML = '';
23015         
23016         while (i < 12) {
23017             var month = {
23018                 tag: 'span',
23019                 cls: 'month',
23020                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23021             };
23022             
23023             months.createChild(month);
23024         }
23025         
23026     },
23027     
23028     update: function()
23029     {
23030         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;
23031         
23032         if (this.date < this.startDate) {
23033             this.viewDate = new Date(this.startDate);
23034         } else if (this.date > this.endDate) {
23035             this.viewDate = new Date(this.endDate);
23036         } else {
23037             this.viewDate = new Date(this.date);
23038         }
23039         
23040         this.fill();
23041     },
23042     
23043     fill: function() 
23044     {
23045         var d = new Date(this.viewDate),
23046                 year = d.getUTCFullYear(),
23047                 month = d.getUTCMonth(),
23048                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23049                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23050                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23051                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23052                 currentDate = this.date && this.date.valueOf(),
23053                 today = this.UTCToday();
23054         
23055         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23056         
23057 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23058         
23059 //        this.picker.select('>tfoot th.today').
23060 //                                              .text(dates[this.language].today)
23061 //                                              .toggle(this.todayBtn !== false);
23062     
23063         this.updateNavArrows();
23064         this.fillMonths();
23065                                                 
23066         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23067         
23068         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23069          
23070         prevMonth.setUTCDate(day);
23071         
23072         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23073         
23074         var nextMonth = new Date(prevMonth);
23075         
23076         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23077         
23078         nextMonth = nextMonth.valueOf();
23079         
23080         var fillMonths = false;
23081         
23082         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23083         
23084         while(prevMonth.valueOf() <= nextMonth) {
23085             var clsName = '';
23086             
23087             if (prevMonth.getUTCDay() === this.weekStart) {
23088                 if(fillMonths){
23089                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23090                 }
23091                     
23092                 fillMonths = {
23093                     tag: 'tr',
23094                     cn: []
23095                 };
23096                 
23097                 if(this.calendarWeeks){
23098                     // ISO 8601: First week contains first thursday.
23099                     // ISO also states week starts on Monday, but we can be more abstract here.
23100                     var
23101                     // Start of current week: based on weekstart/current date
23102                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23103                     // Thursday of this week
23104                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23105                     // First Thursday of year, year from thursday
23106                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23107                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23108                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23109                     
23110                     fillMonths.cn.push({
23111                         tag: 'td',
23112                         cls: 'cw',
23113                         html: calWeek
23114                     });
23115                 }
23116             }
23117             
23118             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23119                 clsName += ' old';
23120             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23121                 clsName += ' new';
23122             }
23123             if (this.todayHighlight &&
23124                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23125                 prevMonth.getUTCMonth() == today.getMonth() &&
23126                 prevMonth.getUTCDate() == today.getDate()) {
23127                 clsName += ' today';
23128             }
23129             
23130             if (currentDate && prevMonth.valueOf() === currentDate) {
23131                 clsName += ' active';
23132             }
23133             
23134             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23135                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23136                     clsName += ' disabled';
23137             }
23138             
23139             fillMonths.cn.push({
23140                 tag: 'td',
23141                 cls: 'day ' + clsName,
23142                 html: prevMonth.getDate()
23143             });
23144             
23145             prevMonth.setDate(prevMonth.getDate()+1);
23146         }
23147           
23148         var currentYear = this.date && this.date.getUTCFullYear();
23149         var currentMonth = this.date && this.date.getUTCMonth();
23150         
23151         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23152         
23153         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23154             v.removeClass('active');
23155             
23156             if(currentYear === year && k === currentMonth){
23157                 v.addClass('active');
23158             }
23159             
23160             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23161                 v.addClass('disabled');
23162             }
23163             
23164         });
23165         
23166         
23167         year = parseInt(year/10, 10) * 10;
23168         
23169         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23170         
23171         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23172         
23173         year -= 1;
23174         for (var i = -1; i < 11; i++) {
23175             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23176                 tag: 'span',
23177                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23178                 html: year
23179             });
23180             
23181             year += 1;
23182         }
23183     },
23184     
23185     showMode: function(dir) 
23186     {
23187         if (dir) {
23188             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23189         }
23190         
23191         Roo.each(this.picker().select('>div',true).elements, function(v){
23192             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23193             v.hide();
23194         });
23195         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23196     },
23197     
23198     place: function()
23199     {
23200         if(this.isInline) {
23201             return;
23202         }
23203         
23204         this.picker().removeClass(['bottom', 'top']);
23205         
23206         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23207             /*
23208              * place to the top of element!
23209              *
23210              */
23211             
23212             this.picker().addClass('top');
23213             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23214             
23215             return;
23216         }
23217         
23218         this.picker().addClass('bottom');
23219         
23220         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23221     },
23222     
23223     parseDate : function(value)
23224     {
23225         if(!value || value instanceof Date){
23226             return value;
23227         }
23228         var v = Date.parseDate(value, this.format);
23229         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23230             v = Date.parseDate(value, 'Y-m-d');
23231         }
23232         if(!v && this.altFormats){
23233             if(!this.altFormatsArray){
23234                 this.altFormatsArray = this.altFormats.split("|");
23235             }
23236             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23237                 v = Date.parseDate(value, this.altFormatsArray[i]);
23238             }
23239         }
23240         return v;
23241     },
23242     
23243     formatDate : function(date, fmt)
23244     {   
23245         return (!date || !(date instanceof Date)) ?
23246         date : date.dateFormat(fmt || this.format);
23247     },
23248     
23249     onFocus : function()
23250     {
23251         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23252         this.showPopup();
23253     },
23254     
23255     onBlur : function()
23256     {
23257         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23258         
23259         var d = this.inputEl().getValue();
23260         
23261         this.setValue(d);
23262                 
23263         this.hidePopup();
23264     },
23265     
23266     showPopup : function()
23267     {
23268         this.picker().show();
23269         this.update();
23270         this.place();
23271         
23272         this.fireEvent('showpopup', this, this.date);
23273     },
23274     
23275     hidePopup : function()
23276     {
23277         if(this.isInline) {
23278             return;
23279         }
23280         this.picker().hide();
23281         this.viewMode = this.startViewMode;
23282         this.showMode();
23283         
23284         this.fireEvent('hidepopup', this, this.date);
23285         
23286     },
23287     
23288     onMousedown: function(e)
23289     {
23290         e.stopPropagation();
23291         e.preventDefault();
23292     },
23293     
23294     keyup: function(e)
23295     {
23296         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23297         this.update();
23298     },
23299
23300     setValue: function(v)
23301     {
23302         if(this.fireEvent('beforeselect', this, v) !== false){
23303             var d = new Date(this.parseDate(v) ).clearTime();
23304         
23305             if(isNaN(d.getTime())){
23306                 this.date = this.viewDate = '';
23307                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23308                 return;
23309             }
23310
23311             v = this.formatDate(d);
23312
23313             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23314
23315             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23316
23317             this.update();
23318
23319             this.fireEvent('select', this, this.date);
23320         }
23321     },
23322     
23323     getValue: function()
23324     {
23325         return this.formatDate(this.date);
23326     },
23327     
23328     fireKey: function(e)
23329     {
23330         if (!this.picker().isVisible()){
23331             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23332                 this.showPopup();
23333             }
23334             return;
23335         }
23336         
23337         var dateChanged = false,
23338         dir, day, month,
23339         newDate, newViewDate;
23340         
23341         switch(e.keyCode){
23342             case 27: // escape
23343                 this.hidePopup();
23344                 e.preventDefault();
23345                 break;
23346             case 37: // left
23347             case 39: // right
23348                 if (!this.keyboardNavigation) {
23349                     break;
23350                 }
23351                 dir = e.keyCode == 37 ? -1 : 1;
23352                 
23353                 if (e.ctrlKey){
23354                     newDate = this.moveYear(this.date, dir);
23355                     newViewDate = this.moveYear(this.viewDate, dir);
23356                 } else if (e.shiftKey){
23357                     newDate = this.moveMonth(this.date, dir);
23358                     newViewDate = this.moveMonth(this.viewDate, dir);
23359                 } else {
23360                     newDate = new Date(this.date);
23361                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23362                     newViewDate = new Date(this.viewDate);
23363                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23364                 }
23365                 if (this.dateWithinRange(newDate)){
23366                     this.date = newDate;
23367                     this.viewDate = newViewDate;
23368                     this.setValue(this.formatDate(this.date));
23369 //                    this.update();
23370                     e.preventDefault();
23371                     dateChanged = true;
23372                 }
23373                 break;
23374             case 38: // up
23375             case 40: // down
23376                 if (!this.keyboardNavigation) {
23377                     break;
23378                 }
23379                 dir = e.keyCode == 38 ? -1 : 1;
23380                 if (e.ctrlKey){
23381                     newDate = this.moveYear(this.date, dir);
23382                     newViewDate = this.moveYear(this.viewDate, dir);
23383                 } else if (e.shiftKey){
23384                     newDate = this.moveMonth(this.date, dir);
23385                     newViewDate = this.moveMonth(this.viewDate, dir);
23386                 } else {
23387                     newDate = new Date(this.date);
23388                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23389                     newViewDate = new Date(this.viewDate);
23390                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23391                 }
23392                 if (this.dateWithinRange(newDate)){
23393                     this.date = newDate;
23394                     this.viewDate = newViewDate;
23395                     this.setValue(this.formatDate(this.date));
23396 //                    this.update();
23397                     e.preventDefault();
23398                     dateChanged = true;
23399                 }
23400                 break;
23401             case 13: // enter
23402                 this.setValue(this.formatDate(this.date));
23403                 this.hidePopup();
23404                 e.preventDefault();
23405                 break;
23406             case 9: // tab
23407                 this.setValue(this.formatDate(this.date));
23408                 this.hidePopup();
23409                 break;
23410             case 16: // shift
23411             case 17: // ctrl
23412             case 18: // alt
23413                 break;
23414             default :
23415                 this.hidePopup();
23416                 
23417         }
23418     },
23419     
23420     
23421     onClick: function(e) 
23422     {
23423         e.stopPropagation();
23424         e.preventDefault();
23425         
23426         var target = e.getTarget();
23427         
23428         if(target.nodeName.toLowerCase() === 'i'){
23429             target = Roo.get(target).dom.parentNode;
23430         }
23431         
23432         var nodeName = target.nodeName;
23433         var className = target.className;
23434         var html = target.innerHTML;
23435         //Roo.log(nodeName);
23436         
23437         switch(nodeName.toLowerCase()) {
23438             case 'th':
23439                 switch(className) {
23440                     case 'switch':
23441                         this.showMode(1);
23442                         break;
23443                     case 'prev':
23444                     case 'next':
23445                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23446                         switch(this.viewMode){
23447                                 case 0:
23448                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23449                                         break;
23450                                 case 1:
23451                                 case 2:
23452                                         this.viewDate = this.moveYear(this.viewDate, dir);
23453                                         break;
23454                         }
23455                         this.fill();
23456                         break;
23457                     case 'today':
23458                         var date = new Date();
23459                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23460 //                        this.fill()
23461                         this.setValue(this.formatDate(this.date));
23462                         
23463                         this.hidePopup();
23464                         break;
23465                 }
23466                 break;
23467             case 'span':
23468                 if (className.indexOf('disabled') < 0) {
23469                 if (!this.viewDate) {
23470                     this.viewDate = new Date();
23471                 }
23472                 this.viewDate.setUTCDate(1);
23473                     if (className.indexOf('month') > -1) {
23474                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23475                     } else {
23476                         var year = parseInt(html, 10) || 0;
23477                         this.viewDate.setUTCFullYear(year);
23478                         
23479                     }
23480                     
23481                     if(this.singleMode){
23482                         this.setValue(this.formatDate(this.viewDate));
23483                         this.hidePopup();
23484                         return;
23485                     }
23486                     
23487                     this.showMode(-1);
23488                     this.fill();
23489                 }
23490                 break;
23491                 
23492             case 'td':
23493                 //Roo.log(className);
23494                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23495                     var day = parseInt(html, 10) || 1;
23496                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23497                         month = (this.viewDate || new Date()).getUTCMonth();
23498
23499                     if (className.indexOf('old') > -1) {
23500                         if(month === 0 ){
23501                             month = 11;
23502                             year -= 1;
23503                         }else{
23504                             month -= 1;
23505                         }
23506                     } else if (className.indexOf('new') > -1) {
23507                         if (month == 11) {
23508                             month = 0;
23509                             year += 1;
23510                         } else {
23511                             month += 1;
23512                         }
23513                     }
23514                     //Roo.log([year,month,day]);
23515                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23516                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23517 //                    this.fill();
23518                     //Roo.log(this.formatDate(this.date));
23519                     this.setValue(this.formatDate(this.date));
23520                     this.hidePopup();
23521                 }
23522                 break;
23523         }
23524     },
23525     
23526     setStartDate: function(startDate)
23527     {
23528         this.startDate = startDate || -Infinity;
23529         if (this.startDate !== -Infinity) {
23530             this.startDate = this.parseDate(this.startDate);
23531         }
23532         this.update();
23533         this.updateNavArrows();
23534     },
23535
23536     setEndDate: function(endDate)
23537     {
23538         this.endDate = endDate || Infinity;
23539         if (this.endDate !== Infinity) {
23540             this.endDate = this.parseDate(this.endDate);
23541         }
23542         this.update();
23543         this.updateNavArrows();
23544     },
23545     
23546     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23547     {
23548         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23549         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23550             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23551         }
23552         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23553             return parseInt(d, 10);
23554         });
23555         this.update();
23556         this.updateNavArrows();
23557     },
23558     
23559     updateNavArrows: function() 
23560     {
23561         if(this.singleMode){
23562             return;
23563         }
23564         
23565         var d = new Date(this.viewDate),
23566         year = d.getUTCFullYear(),
23567         month = d.getUTCMonth();
23568         
23569         Roo.each(this.picker().select('.prev', true).elements, function(v){
23570             v.show();
23571             switch (this.viewMode) {
23572                 case 0:
23573
23574                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23575                         v.hide();
23576                     }
23577                     break;
23578                 case 1:
23579                 case 2:
23580                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23581                         v.hide();
23582                     }
23583                     break;
23584             }
23585         });
23586         
23587         Roo.each(this.picker().select('.next', true).elements, function(v){
23588             v.show();
23589             switch (this.viewMode) {
23590                 case 0:
23591
23592                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23593                         v.hide();
23594                     }
23595                     break;
23596                 case 1:
23597                 case 2:
23598                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23599                         v.hide();
23600                     }
23601                     break;
23602             }
23603         })
23604     },
23605     
23606     moveMonth: function(date, dir)
23607     {
23608         if (!dir) {
23609             return date;
23610         }
23611         var new_date = new Date(date.valueOf()),
23612         day = new_date.getUTCDate(),
23613         month = new_date.getUTCMonth(),
23614         mag = Math.abs(dir),
23615         new_month, test;
23616         dir = dir > 0 ? 1 : -1;
23617         if (mag == 1){
23618             test = dir == -1
23619             // If going back one month, make sure month is not current month
23620             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23621             ? function(){
23622                 return new_date.getUTCMonth() == month;
23623             }
23624             // If going forward one month, make sure month is as expected
23625             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23626             : function(){
23627                 return new_date.getUTCMonth() != new_month;
23628             };
23629             new_month = month + dir;
23630             new_date.setUTCMonth(new_month);
23631             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23632             if (new_month < 0 || new_month > 11) {
23633                 new_month = (new_month + 12) % 12;
23634             }
23635         } else {
23636             // For magnitudes >1, move one month at a time...
23637             for (var i=0; i<mag; i++) {
23638                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23639                 new_date = this.moveMonth(new_date, dir);
23640             }
23641             // ...then reset the day, keeping it in the new month
23642             new_month = new_date.getUTCMonth();
23643             new_date.setUTCDate(day);
23644             test = function(){
23645                 return new_month != new_date.getUTCMonth();
23646             };
23647         }
23648         // Common date-resetting loop -- if date is beyond end of month, make it
23649         // end of month
23650         while (test()){
23651             new_date.setUTCDate(--day);
23652             new_date.setUTCMonth(new_month);
23653         }
23654         return new_date;
23655     },
23656
23657     moveYear: function(date, dir)
23658     {
23659         return this.moveMonth(date, dir*12);
23660     },
23661
23662     dateWithinRange: function(date)
23663     {
23664         return date >= this.startDate && date <= this.endDate;
23665     },
23666
23667     
23668     remove: function() 
23669     {
23670         this.picker().remove();
23671     },
23672     
23673     validateValue : function(value)
23674     {
23675         if(this.getVisibilityEl().hasClass('hidden')){
23676             return true;
23677         }
23678         
23679         if(value.length < 1)  {
23680             if(this.allowBlank){
23681                 return true;
23682             }
23683             return false;
23684         }
23685         
23686         if(value.length < this.minLength){
23687             return false;
23688         }
23689         if(value.length > this.maxLength){
23690             return false;
23691         }
23692         if(this.vtype){
23693             var vt = Roo.form.VTypes;
23694             if(!vt[this.vtype](value, this)){
23695                 return false;
23696             }
23697         }
23698         if(typeof this.validator == "function"){
23699             var msg = this.validator(value);
23700             if(msg !== true){
23701                 return false;
23702             }
23703         }
23704         
23705         if(this.regex && !this.regex.test(value)){
23706             return false;
23707         }
23708         
23709         if(typeof(this.parseDate(value)) == 'undefined'){
23710             return false;
23711         }
23712         
23713         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23714             return false;
23715         }      
23716         
23717         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23718             return false;
23719         } 
23720         
23721         
23722         return true;
23723     },
23724     
23725     reset : function()
23726     {
23727         this.date = this.viewDate = '';
23728         
23729         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23730     }
23731    
23732 });
23733
23734 Roo.apply(Roo.bootstrap.form.DateField,  {
23735     
23736     head : {
23737         tag: 'thead',
23738         cn: [
23739         {
23740             tag: 'tr',
23741             cn: [
23742             {
23743                 tag: 'th',
23744                 cls: 'prev',
23745                 html: '<i class="fa fa-arrow-left"/>'
23746             },
23747             {
23748                 tag: 'th',
23749                 cls: 'switch',
23750                 colspan: '5'
23751             },
23752             {
23753                 tag: 'th',
23754                 cls: 'next',
23755                 html: '<i class="fa fa-arrow-right"/>'
23756             }
23757
23758             ]
23759         }
23760         ]
23761     },
23762     
23763     content : {
23764         tag: 'tbody',
23765         cn: [
23766         {
23767             tag: 'tr',
23768             cn: [
23769             {
23770                 tag: 'td',
23771                 colspan: '7'
23772             }
23773             ]
23774         }
23775         ]
23776     },
23777     
23778     footer : {
23779         tag: 'tfoot',
23780         cn: [
23781         {
23782             tag: 'tr',
23783             cn: [
23784             {
23785                 tag: 'th',
23786                 colspan: '7',
23787                 cls: 'today'
23788             }
23789                     
23790             ]
23791         }
23792         ]
23793     },
23794     
23795     dates:{
23796         en: {
23797             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23798             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23799             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23800             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23801             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23802             today: "Today"
23803         }
23804     },
23805     
23806     modes: [
23807     {
23808         clsName: 'days',
23809         navFnc: 'Month',
23810         navStep: 1
23811     },
23812     {
23813         clsName: 'months',
23814         navFnc: 'FullYear',
23815         navStep: 1
23816     },
23817     {
23818         clsName: 'years',
23819         navFnc: 'FullYear',
23820         navStep: 10
23821     }]
23822 });
23823
23824 Roo.apply(Roo.bootstrap.form.DateField,  {
23825   
23826     template : {
23827         tag: 'div',
23828         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23829         cn: [
23830         {
23831             tag: 'div',
23832             cls: 'datepicker-days',
23833             cn: [
23834             {
23835                 tag: 'table',
23836                 cls: 'table-condensed',
23837                 cn:[
23838                 Roo.bootstrap.form.DateField.head,
23839                 {
23840                     tag: 'tbody'
23841                 },
23842                 Roo.bootstrap.form.DateField.footer
23843                 ]
23844             }
23845             ]
23846         },
23847         {
23848             tag: 'div',
23849             cls: 'datepicker-months',
23850             cn: [
23851             {
23852                 tag: 'table',
23853                 cls: 'table-condensed',
23854                 cn:[
23855                 Roo.bootstrap.form.DateField.head,
23856                 Roo.bootstrap.form.DateField.content,
23857                 Roo.bootstrap.form.DateField.footer
23858                 ]
23859             }
23860             ]
23861         },
23862         {
23863             tag: 'div',
23864             cls: 'datepicker-years',
23865             cn: [
23866             {
23867                 tag: 'table',
23868                 cls: 'table-condensed',
23869                 cn:[
23870                 Roo.bootstrap.form.DateField.head,
23871                 Roo.bootstrap.form.DateField.content,
23872                 Roo.bootstrap.form.DateField.footer
23873                 ]
23874             }
23875             ]
23876         }
23877         ]
23878     }
23879 });
23880
23881  
23882
23883  /*
23884  * - LGPL
23885  *
23886  * TimeField
23887  * 
23888  */
23889
23890 /**
23891  * @class Roo.bootstrap.form.TimeField
23892  * @extends Roo.bootstrap.form.Input
23893  * Bootstrap DateField class
23894  * 
23895  * 
23896  * @constructor
23897  * Create a new TimeField
23898  * @param {Object} config The config object
23899  */
23900
23901 Roo.bootstrap.form.TimeField = function(config){
23902     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23903     this.addEvents({
23904             /**
23905              * @event show
23906              * Fires when this field show.
23907              * @param {Roo.bootstrap.form.DateField} thisthis
23908              * @param {Mixed} date The date value
23909              */
23910             show : true,
23911             /**
23912              * @event show
23913              * Fires when this field hide.
23914              * @param {Roo.bootstrap.form.DateField} this
23915              * @param {Mixed} date The date value
23916              */
23917             hide : true,
23918             /**
23919              * @event select
23920              * Fires when select a date.
23921              * @param {Roo.bootstrap.form.DateField} this
23922              * @param {Mixed} date The date value
23923              */
23924             select : true
23925         });
23926 };
23927
23928 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23929     
23930     /**
23931      * @cfg {String} format
23932      * The default time format string which can be overriden for localization support.  The format must be
23933      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23934      */
23935     format : "H:i",
23936
23937     getAutoCreate : function()
23938     {
23939         this.after = '<i class="fa far fa-clock"></i>';
23940         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23941         
23942          
23943     },
23944     onRender: function(ct, position)
23945     {
23946         
23947         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23948                 
23949         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23950         
23951         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23952         
23953         this.pop = this.picker().select('>.datepicker-time',true).first();
23954         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23955         
23956         this.picker().on('mousedown', this.onMousedown, this);
23957         this.picker().on('click', this.onClick, this);
23958         
23959         this.picker().addClass('datepicker-dropdown');
23960     
23961         this.fillTime();
23962         this.update();
23963             
23964         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23965         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23966         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23967         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23968         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23969         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23970
23971     },
23972     
23973     fireKey: function(e){
23974         if (!this.picker().isVisible()){
23975             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23976                 this.show();
23977             }
23978             return;
23979         }
23980
23981         e.preventDefault();
23982         
23983         switch(e.keyCode){
23984             case 27: // escape
23985                 this.hide();
23986                 break;
23987             case 37: // left
23988             case 39: // right
23989                 this.onTogglePeriod();
23990                 break;
23991             case 38: // up
23992                 this.onIncrementMinutes();
23993                 break;
23994             case 40: // down
23995                 this.onDecrementMinutes();
23996                 break;
23997             case 13: // enter
23998             case 9: // tab
23999                 this.setTime();
24000                 break;
24001         }
24002     },
24003     
24004     onClick: function(e) {
24005         e.stopPropagation();
24006         e.preventDefault();
24007     },
24008     
24009     picker : function()
24010     {
24011         return this.pickerEl;
24012     },
24013     
24014     fillTime: function()
24015     {    
24016         var time = this.pop.select('tbody', true).first();
24017         
24018         time.dom.innerHTML = '';
24019         
24020         time.createChild({
24021             tag: 'tr',
24022             cn: [
24023                 {
24024                     tag: 'td',
24025                     cn: [
24026                         {
24027                             tag: 'a',
24028                             href: '#',
24029                             cls: 'btn',
24030                             cn: [
24031                                 {
24032                                     tag: 'i',
24033                                     cls: 'hours-up fa fas fa-chevron-up'
24034                                 }
24035                             ]
24036                         } 
24037                     ]
24038                 },
24039                 {
24040                     tag: 'td',
24041                     cls: 'separator'
24042                 },
24043                 {
24044                     tag: 'td',
24045                     cn: [
24046                         {
24047                             tag: 'a',
24048                             href: '#',
24049                             cls: 'btn',
24050                             cn: [
24051                                 {
24052                                     tag: 'i',
24053                                     cls: 'minutes-up fa fas fa-chevron-up'
24054                                 }
24055                             ]
24056                         }
24057                     ]
24058                 },
24059                 {
24060                     tag: 'td',
24061                     cls: 'separator'
24062                 }
24063             ]
24064         });
24065         
24066         time.createChild({
24067             tag: 'tr',
24068             cn: [
24069                 {
24070                     tag: 'td',
24071                     cn: [
24072                         {
24073                             tag: 'span',
24074                             cls: 'timepicker-hour',
24075                             html: '00'
24076                         }  
24077                     ]
24078                 },
24079                 {
24080                     tag: 'td',
24081                     cls: 'separator',
24082                     html: ':'
24083                 },
24084                 {
24085                     tag: 'td',
24086                     cn: [
24087                         {
24088                             tag: 'span',
24089                             cls: 'timepicker-minute',
24090                             html: '00'
24091                         }  
24092                     ]
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cls: 'separator'
24097                 },
24098                 {
24099                     tag: 'td',
24100                     cn: [
24101                         {
24102                             tag: 'button',
24103                             type: 'button',
24104                             cls: 'btn btn-primary period',
24105                             html: 'AM'
24106                             
24107                         }
24108                     ]
24109                 }
24110             ]
24111         });
24112         
24113         time.createChild({
24114             tag: 'tr',
24115             cn: [
24116                 {
24117                     tag: 'td',
24118                     cn: [
24119                         {
24120                             tag: 'a',
24121                             href: '#',
24122                             cls: 'btn',
24123                             cn: [
24124                                 {
24125                                     tag: 'span',
24126                                     cls: 'hours-down fa fas fa-chevron-down'
24127                                 }
24128                             ]
24129                         }
24130                     ]
24131                 },
24132                 {
24133                     tag: 'td',
24134                     cls: 'separator'
24135                 },
24136                 {
24137                     tag: 'td',
24138                     cn: [
24139                         {
24140                             tag: 'a',
24141                             href: '#',
24142                             cls: 'btn',
24143                             cn: [
24144                                 {
24145                                     tag: 'span',
24146                                     cls: 'minutes-down fa fas fa-chevron-down'
24147                                 }
24148                             ]
24149                         }
24150                     ]
24151                 },
24152                 {
24153                     tag: 'td',
24154                     cls: 'separator'
24155                 }
24156             ]
24157         });
24158         
24159     },
24160     
24161     update: function()
24162     {
24163         
24164         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24165         
24166         this.fill();
24167     },
24168     
24169     fill: function() 
24170     {
24171         var hours = this.time.getHours();
24172         var minutes = this.time.getMinutes();
24173         var period = 'AM';
24174         
24175         if(hours > 11){
24176             period = 'PM';
24177         }
24178         
24179         if(hours == 0){
24180             hours = 12;
24181         }
24182         
24183         
24184         if(hours > 12){
24185             hours = hours - 12;
24186         }
24187         
24188         if(hours < 10){
24189             hours = '0' + hours;
24190         }
24191         
24192         if(minutes < 10){
24193             minutes = '0' + minutes;
24194         }
24195         
24196         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24197         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24198         this.pop.select('button', true).first().dom.innerHTML = period;
24199         
24200     },
24201     
24202     place: function()
24203     {   
24204         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24205         
24206         var cls = ['bottom'];
24207         
24208         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24209             cls.pop();
24210             cls.push('top');
24211         }
24212         
24213         cls.push('right');
24214         
24215         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24216             cls.pop();
24217             cls.push('left');
24218         }
24219         //this.picker().setXY(20000,20000);
24220         this.picker().addClass(cls.join('-'));
24221         
24222         var _this = this;
24223         
24224         Roo.each(cls, function(c){
24225             if(c == 'bottom'){
24226                 (function() {
24227                  //  
24228                 }).defer(200);
24229                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24230                 //_this.picker().setTop(_this.inputEl().getHeight());
24231                 return;
24232             }
24233             if(c == 'top'){
24234                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24235                 
24236                 //_this.picker().setTop(0 - _this.picker().getHeight());
24237                 return;
24238             }
24239             /*
24240             if(c == 'left'){
24241                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24242                 return;
24243             }
24244             if(c == 'right'){
24245                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24246                 return;
24247             }
24248             */
24249         });
24250         
24251     },
24252   
24253     onFocus : function()
24254     {
24255         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24256         this.show();
24257     },
24258     
24259     onBlur : function()
24260     {
24261         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24262         this.hide();
24263     },
24264     
24265     show : function()
24266     {
24267         this.picker().show();
24268         this.pop.show();
24269         this.update();
24270         this.place();
24271         
24272         this.fireEvent('show', this, this.date);
24273     },
24274     
24275     hide : function()
24276     {
24277         this.picker().hide();
24278         this.pop.hide();
24279         
24280         this.fireEvent('hide', this, this.date);
24281     },
24282     
24283     setTime : function()
24284     {
24285         this.hide();
24286         this.setValue(this.time.format(this.format));
24287         
24288         this.fireEvent('select', this, this.date);
24289         
24290         
24291     },
24292     
24293     onMousedown: function(e){
24294         e.stopPropagation();
24295         e.preventDefault();
24296     },
24297     
24298     onIncrementHours: function()
24299     {
24300         Roo.log('onIncrementHours');
24301         this.time = this.time.add(Date.HOUR, 1);
24302         this.update();
24303         
24304     },
24305     
24306     onDecrementHours: function()
24307     {
24308         Roo.log('onDecrementHours');
24309         this.time = this.time.add(Date.HOUR, -1);
24310         this.update();
24311     },
24312     
24313     onIncrementMinutes: function()
24314     {
24315         Roo.log('onIncrementMinutes');
24316         this.time = this.time.add(Date.MINUTE, 1);
24317         this.update();
24318     },
24319     
24320     onDecrementMinutes: function()
24321     {
24322         Roo.log('onDecrementMinutes');
24323         this.time = this.time.add(Date.MINUTE, -1);
24324         this.update();
24325     },
24326     
24327     onTogglePeriod: function()
24328     {
24329         Roo.log('onTogglePeriod');
24330         this.time = this.time.add(Date.HOUR, 12);
24331         this.update();
24332     }
24333     
24334    
24335 });
24336  
24337
24338 Roo.apply(Roo.bootstrap.form.TimeField,  {
24339   
24340     template : {
24341         tag: 'div',
24342         cls: 'datepicker dropdown-menu',
24343         cn: [
24344             {
24345                 tag: 'div',
24346                 cls: 'datepicker-time',
24347                 cn: [
24348                 {
24349                     tag: 'table',
24350                     cls: 'table-condensed',
24351                     cn:[
24352                         {
24353                             tag: 'tbody',
24354                             cn: [
24355                                 {
24356                                     tag: 'tr',
24357                                     cn: [
24358                                     {
24359                                         tag: 'td',
24360                                         colspan: '7'
24361                                     }
24362                                     ]
24363                                 }
24364                             ]
24365                         },
24366                         {
24367                             tag: 'tfoot',
24368                             cn: [
24369                                 {
24370                                     tag: 'tr',
24371                                     cn: [
24372                                     {
24373                                         tag: 'th',
24374                                         colspan: '7',
24375                                         cls: '',
24376                                         cn: [
24377                                             {
24378                                                 tag: 'button',
24379                                                 cls: 'btn btn-info ok',
24380                                                 html: 'OK'
24381                                             }
24382                                         ]
24383                                     }
24384                     
24385                                     ]
24386                                 }
24387                             ]
24388                         }
24389                     ]
24390                 }
24391                 ]
24392             }
24393         ]
24394     }
24395 });
24396
24397  
24398
24399  /*
24400  * - LGPL
24401  *
24402  * MonthField
24403  * 
24404  */
24405
24406 /**
24407  * @class Roo.bootstrap.form.MonthField
24408  * @extends Roo.bootstrap.form.Input
24409  * Bootstrap MonthField class
24410  * 
24411  * @cfg {String} language default en
24412  * 
24413  * @constructor
24414  * Create a new MonthField
24415  * @param {Object} config The config object
24416  */
24417
24418 Roo.bootstrap.form.MonthField = function(config){
24419     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24420     
24421     this.addEvents({
24422         /**
24423          * @event show
24424          * Fires when this field show.
24425          * @param {Roo.bootstrap.form.MonthField} this
24426          * @param {Mixed} date The date value
24427          */
24428         show : true,
24429         /**
24430          * @event show
24431          * Fires when this field hide.
24432          * @param {Roo.bootstrap.form.MonthField} this
24433          * @param {Mixed} date The date value
24434          */
24435         hide : true,
24436         /**
24437          * @event select
24438          * Fires when select a date.
24439          * @param {Roo.bootstrap.form.MonthField} this
24440          * @param {String} oldvalue The old value
24441          * @param {String} newvalue The new value
24442          */
24443         select : true
24444     });
24445 };
24446
24447 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24448     
24449     onRender: function(ct, position)
24450     {
24451         
24452         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24453         
24454         this.language = this.language || 'en';
24455         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24456         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24457         
24458         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24459         this.isInline = false;
24460         this.isInput = true;
24461         this.component = this.el.select('.add-on', true).first() || false;
24462         this.component = (this.component && this.component.length === 0) ? false : this.component;
24463         this.hasInput = this.component && this.inputEL().length;
24464         
24465         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24466         
24467         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24468         
24469         this.picker().on('mousedown', this.onMousedown, this);
24470         this.picker().on('click', this.onClick, this);
24471         
24472         this.picker().addClass('datepicker-dropdown');
24473         
24474         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24475             v.setStyle('width', '189px');
24476         });
24477         
24478         this.fillMonths();
24479         
24480         this.update();
24481         
24482         if(this.isInline) {
24483             this.show();
24484         }
24485         
24486     },
24487     
24488     setValue: function(v, suppressEvent)
24489     {   
24490         var o = this.getValue();
24491         
24492         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24493         
24494         this.update();
24495
24496         if(suppressEvent !== true){
24497             this.fireEvent('select', this, o, v);
24498         }
24499         
24500     },
24501     
24502     getValue: function()
24503     {
24504         return this.value;
24505     },
24506     
24507     onClick: function(e) 
24508     {
24509         e.stopPropagation();
24510         e.preventDefault();
24511         
24512         var target = e.getTarget();
24513         
24514         if(target.nodeName.toLowerCase() === 'i'){
24515             target = Roo.get(target).dom.parentNode;
24516         }
24517         
24518         var nodeName = target.nodeName;
24519         var className = target.className;
24520         var html = target.innerHTML;
24521         
24522         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24523             return;
24524         }
24525         
24526         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24527         
24528         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24529         
24530         this.hide();
24531                         
24532     },
24533     
24534     picker : function()
24535     {
24536         return this.pickerEl;
24537     },
24538     
24539     fillMonths: function()
24540     {    
24541         var i = 0;
24542         var months = this.picker().select('>.datepicker-months td', true).first();
24543         
24544         months.dom.innerHTML = '';
24545         
24546         while (i < 12) {
24547             var month = {
24548                 tag: 'span',
24549                 cls: 'month',
24550                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24551             };
24552             
24553             months.createChild(month);
24554         }
24555         
24556     },
24557     
24558     update: function()
24559     {
24560         var _this = this;
24561         
24562         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24563             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24564         }
24565         
24566         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24567             e.removeClass('active');
24568             
24569             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24570                 e.addClass('active');
24571             }
24572         })
24573     },
24574     
24575     place: function()
24576     {
24577         if(this.isInline) {
24578             return;
24579         }
24580         
24581         this.picker().removeClass(['bottom', 'top']);
24582         
24583         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24584             /*
24585              * place to the top of element!
24586              *
24587              */
24588             
24589             this.picker().addClass('top');
24590             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24591             
24592             return;
24593         }
24594         
24595         this.picker().addClass('bottom');
24596         
24597         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24598     },
24599     
24600     onFocus : function()
24601     {
24602         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24603         this.show();
24604     },
24605     
24606     onBlur : function()
24607     {
24608         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24609         
24610         var d = this.inputEl().getValue();
24611         
24612         this.setValue(d);
24613                 
24614         this.hide();
24615     },
24616     
24617     show : function()
24618     {
24619         this.picker().show();
24620         this.picker().select('>.datepicker-months', true).first().show();
24621         this.update();
24622         this.place();
24623         
24624         this.fireEvent('show', this, this.date);
24625     },
24626     
24627     hide : function()
24628     {
24629         if(this.isInline) {
24630             return;
24631         }
24632         this.picker().hide();
24633         this.fireEvent('hide', this, this.date);
24634         
24635     },
24636     
24637     onMousedown: function(e)
24638     {
24639         e.stopPropagation();
24640         e.preventDefault();
24641     },
24642     
24643     keyup: function(e)
24644     {
24645         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24646         this.update();
24647     },
24648
24649     fireKey: function(e)
24650     {
24651         if (!this.picker().isVisible()){
24652             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24653                 this.show();
24654             }
24655             return;
24656         }
24657         
24658         var dir;
24659         
24660         switch(e.keyCode){
24661             case 27: // escape
24662                 this.hide();
24663                 e.preventDefault();
24664                 break;
24665             case 37: // left
24666             case 39: // right
24667                 dir = e.keyCode == 37 ? -1 : 1;
24668                 
24669                 this.vIndex = this.vIndex + dir;
24670                 
24671                 if(this.vIndex < 0){
24672                     this.vIndex = 0;
24673                 }
24674                 
24675                 if(this.vIndex > 11){
24676                     this.vIndex = 11;
24677                 }
24678                 
24679                 if(isNaN(this.vIndex)){
24680                     this.vIndex = 0;
24681                 }
24682                 
24683                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24684                 
24685                 break;
24686             case 38: // up
24687             case 40: // down
24688                 
24689                 dir = e.keyCode == 38 ? -1 : 1;
24690                 
24691                 this.vIndex = this.vIndex + dir * 4;
24692                 
24693                 if(this.vIndex < 0){
24694                     this.vIndex = 0;
24695                 }
24696                 
24697                 if(this.vIndex > 11){
24698                     this.vIndex = 11;
24699                 }
24700                 
24701                 if(isNaN(this.vIndex)){
24702                     this.vIndex = 0;
24703                 }
24704                 
24705                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24706                 break;
24707                 
24708             case 13: // enter
24709                 
24710                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24711                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24712                 }
24713                 
24714                 this.hide();
24715                 e.preventDefault();
24716                 break;
24717             case 9: // tab
24718                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24719                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720                 }
24721                 this.hide();
24722                 break;
24723             case 16: // shift
24724             case 17: // ctrl
24725             case 18: // alt
24726                 break;
24727             default :
24728                 this.hide();
24729                 
24730         }
24731     },
24732     
24733     remove: function() 
24734     {
24735         this.picker().remove();
24736     }
24737    
24738 });
24739
24740 Roo.apply(Roo.bootstrap.form.MonthField,  {
24741     
24742     content : {
24743         tag: 'tbody',
24744         cn: [
24745         {
24746             tag: 'tr',
24747             cn: [
24748             {
24749                 tag: 'td',
24750                 colspan: '7'
24751             }
24752             ]
24753         }
24754         ]
24755     },
24756     
24757     dates:{
24758         en: {
24759             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24760             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24761         }
24762     }
24763 });
24764
24765 Roo.apply(Roo.bootstrap.form.MonthField,  {
24766   
24767     template : {
24768         tag: 'div',
24769         cls: 'datepicker dropdown-menu roo-dynamic',
24770         cn: [
24771             {
24772                 tag: 'div',
24773                 cls: 'datepicker-months',
24774                 cn: [
24775                 {
24776                     tag: 'table',
24777                     cls: 'table-condensed',
24778                     cn:[
24779                         Roo.bootstrap.form.DateField.content
24780                     ]
24781                 }
24782                 ]
24783             }
24784         ]
24785     }
24786 });
24787
24788  
24789
24790  
24791  /*
24792  * - LGPL
24793  *
24794  * CheckBox
24795  * 
24796  */
24797
24798 /**
24799  * @class Roo.bootstrap.form.CheckBox
24800  * @extends Roo.bootstrap.form.Input
24801  * Bootstrap CheckBox class
24802  * 
24803  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24804  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24805  * @cfg {String} boxLabel The text that appears beside the checkbox
24806  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24807  * @cfg {Boolean} checked initnal the element
24808  * @cfg {Boolean} inline inline the element (default false)
24809  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24810  * @cfg {String} tooltip label tooltip
24811  * 
24812  * @constructor
24813  * Create a new CheckBox
24814  * @param {Object} config The config object
24815  */
24816
24817 Roo.bootstrap.form.CheckBox = function(config){
24818     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24819    
24820     this.addEvents({
24821         /**
24822         * @event check
24823         * Fires when the element is checked or unchecked.
24824         * @param {Roo.bootstrap.form.CheckBox} this This input
24825         * @param {Boolean} checked The new checked value
24826         */
24827        check : true,
24828        /**
24829         * @event click
24830         * Fires when the element is click.
24831         * @param {Roo.bootstrap.form.CheckBox} this This input
24832         */
24833        click : true
24834     });
24835     
24836 };
24837
24838 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24839   
24840     inputType: 'checkbox',
24841     inputValue: 1,
24842     valueOff: 0,
24843     boxLabel: false,
24844     checked: false,
24845     weight : false,
24846     inline: false,
24847     tooltip : '',
24848     
24849     // checkbox success does not make any sense really.. 
24850     invalidClass : "",
24851     validClass : "",
24852     
24853     
24854     getAutoCreate : function()
24855     {
24856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24857         
24858         var id = Roo.id();
24859         
24860         var cfg = {};
24861         
24862         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24863         
24864         if(this.inline){
24865             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24866         }
24867         
24868         var input =  {
24869             tag: 'input',
24870             id : id,
24871             type : this.inputType,
24872             value : this.inputValue,
24873             cls : 'roo-' + this.inputType, //'form-box',
24874             placeholder : this.placeholder || ''
24875             
24876         };
24877         
24878         if(this.inputType != 'radio'){
24879             var hidden =  {
24880                 tag: 'input',
24881                 type : 'hidden',
24882                 cls : 'roo-hidden-value',
24883                 value : this.checked ? this.inputValue : this.valueOff
24884             };
24885         }
24886         
24887             
24888         if (this.weight) { // Validity check?
24889             cfg.cls += " " + this.inputType + "-" + this.weight;
24890         }
24891         
24892         if (this.disabled) {
24893             input.disabled=true;
24894         }
24895         
24896         if(this.checked){
24897             input.checked = this.checked;
24898         }
24899         
24900         if (this.name) {
24901             
24902             input.name = this.name;
24903             
24904             if(this.inputType != 'radio'){
24905                 hidden.name = this.name;
24906                 input.name = '_hidden_' + this.name;
24907             }
24908         }
24909         
24910         if (this.size) {
24911             input.cls += ' input-' + this.size;
24912         }
24913         
24914         var settings=this;
24915         
24916         ['xs','sm','md','lg'].map(function(size){
24917             if (settings[size]) {
24918                 cfg.cls += ' col-' + size + '-' + settings[size];
24919             }
24920         });
24921         
24922         var inputblock = input;
24923          
24924         if (this.before || this.after) {
24925             
24926             inputblock = {
24927                 cls : 'input-group',
24928                 cn :  [] 
24929             };
24930             
24931             if (this.before) {
24932                 inputblock.cn.push({
24933                     tag :'span',
24934                     cls : 'input-group-addon',
24935                     html : this.before
24936                 });
24937             }
24938             
24939             inputblock.cn.push(input);
24940             
24941             if(this.inputType != 'radio'){
24942                 inputblock.cn.push(hidden);
24943             }
24944             
24945             if (this.after) {
24946                 inputblock.cn.push({
24947                     tag :'span',
24948                     cls : 'input-group-addon',
24949                     html : this.after
24950                 });
24951             }
24952             
24953         }
24954         var boxLabelCfg = false;
24955         
24956         if(this.boxLabel){
24957            
24958             boxLabelCfg = {
24959                 tag: 'label',
24960                 //'for': id, // box label is handled by onclick - so no for...
24961                 cls: 'box-label',
24962                 html: this.boxLabel
24963             };
24964             if(this.tooltip){
24965                 boxLabelCfg.tooltip = this.tooltip;
24966             }
24967              
24968         }
24969         
24970         
24971         if (align ==='left' && this.fieldLabel.length) {
24972 //                Roo.log("left and has label");
24973             cfg.cn = [
24974                 {
24975                     tag: 'label',
24976                     'for' :  id,
24977                     cls : 'control-label',
24978                     html : this.fieldLabel
24979                 },
24980                 {
24981                     cls : "", 
24982                     cn: [
24983                         inputblock
24984                     ]
24985                 }
24986             ];
24987             
24988             if (boxLabelCfg) {
24989                 cfg.cn[1].cn.push(boxLabelCfg);
24990             }
24991             
24992             if(this.labelWidth > 12){
24993                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24994             }
24995             
24996             if(this.labelWidth < 13 && this.labelmd == 0){
24997                 this.labelmd = this.labelWidth;
24998             }
24999             
25000             if(this.labellg > 0){
25001                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25002                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25003             }
25004             
25005             if(this.labelmd > 0){
25006                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25007                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25008             }
25009             
25010             if(this.labelsm > 0){
25011                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25012                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25013             }
25014             
25015             if(this.labelxs > 0){
25016                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25017                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25018             }
25019             
25020         } else if ( this.fieldLabel.length) {
25021 //                Roo.log(" label");
25022                 cfg.cn = [
25023                    
25024                     {
25025                         tag: this.boxLabel ? 'span' : 'label',
25026                         'for': id,
25027                         cls: 'control-label box-input-label',
25028                         //cls : 'input-group-addon',
25029                         html : this.fieldLabel
25030                     },
25031                     
25032                     inputblock
25033                     
25034                 ];
25035                 if (boxLabelCfg) {
25036                     cfg.cn.push(boxLabelCfg);
25037                 }
25038
25039         } else {
25040             
25041 //                Roo.log(" no label && no align");
25042                 cfg.cn = [  inputblock ] ;
25043                 if (boxLabelCfg) {
25044                     cfg.cn.push(boxLabelCfg);
25045                 }
25046
25047                 
25048         }
25049         
25050        
25051         
25052         if(this.inputType != 'radio'){
25053             cfg.cn.push(hidden);
25054         }
25055         
25056         return cfg;
25057         
25058     },
25059     
25060     /**
25061      * return the real input element.
25062      */
25063     inputEl: function ()
25064     {
25065         return this.el.select('input.roo-' + this.inputType,true).first();
25066     },
25067     hiddenEl: function ()
25068     {
25069         return this.el.select('input.roo-hidden-value',true).first();
25070     },
25071     
25072     labelEl: function()
25073     {
25074         return this.el.select('label.control-label',true).first();
25075     },
25076     /* depricated... */
25077     
25078     label: function()
25079     {
25080         return this.labelEl();
25081     },
25082     
25083     boxLabelEl: function()
25084     {
25085         return this.el.select('label.box-label',true).first();
25086     },
25087     
25088     initEvents : function()
25089     {
25090 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25091         
25092         this.inputEl().on('click', this.onClick,  this);
25093         
25094         if (this.boxLabel) { 
25095             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25096         }
25097         
25098         this.startValue = this.getValue();
25099         
25100         if(this.groupId){
25101             Roo.bootstrap.form.CheckBox.register(this);
25102         }
25103     },
25104     
25105     onClick : function(e)
25106     {   
25107         if(this.fireEvent('click', this, e) !== false){
25108             this.setChecked(!this.checked);
25109         }
25110         
25111     },
25112     
25113     setChecked : function(state,suppressEvent)
25114     {
25115         this.startValue = this.getValue();
25116
25117         if(this.inputType == 'radio'){
25118             
25119             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25120                 e.dom.checked = false;
25121             });
25122             
25123             this.inputEl().dom.checked = true;
25124             
25125             this.inputEl().dom.value = this.inputValue;
25126             
25127             if(suppressEvent !== true){
25128                 this.fireEvent('check', this, true);
25129             }
25130             
25131             this.validate();
25132             
25133             return;
25134         }
25135         
25136         this.checked = state;
25137         
25138         this.inputEl().dom.checked = state;
25139         
25140         
25141         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25142         
25143         if(suppressEvent !== true){
25144             this.fireEvent('check', this, state);
25145         }
25146         
25147         this.validate();
25148     },
25149     
25150     getValue : function()
25151     {
25152         if(this.inputType == 'radio'){
25153             return this.getGroupValue();
25154         }
25155         
25156         return this.hiddenEl().dom.value;
25157         
25158     },
25159     
25160     getGroupValue : function()
25161     {
25162         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25163             return '';
25164         }
25165         
25166         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25167     },
25168     
25169     setValue : function(v,suppressEvent)
25170     {
25171         if(this.inputType == 'radio'){
25172             this.setGroupValue(v, suppressEvent);
25173             return;
25174         }
25175         
25176         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25177         
25178         this.validate();
25179     },
25180     
25181     setGroupValue : function(v, suppressEvent)
25182     {
25183         this.startValue = this.getValue();
25184         
25185         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25186             e.dom.checked = false;
25187             
25188             if(e.dom.value == v){
25189                 e.dom.checked = true;
25190             }
25191         });
25192         
25193         if(suppressEvent !== true){
25194             this.fireEvent('check', this, true);
25195         }
25196
25197         this.validate();
25198         
25199         return;
25200     },
25201     
25202     validate : function()
25203     {
25204         if(this.getVisibilityEl().hasClass('hidden')){
25205             return true;
25206         }
25207         
25208         if(
25209                 this.disabled || 
25210                 (this.inputType == 'radio' && this.validateRadio()) ||
25211                 (this.inputType == 'checkbox' && this.validateCheckbox())
25212         ){
25213             this.markValid();
25214             return true;
25215         }
25216         
25217         this.markInvalid();
25218         return false;
25219     },
25220     
25221     validateRadio : function()
25222     {
25223         if(this.getVisibilityEl().hasClass('hidden')){
25224             return true;
25225         }
25226         
25227         if(this.allowBlank){
25228             return true;
25229         }
25230         
25231         var valid = false;
25232         
25233         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25234             if(!e.dom.checked){
25235                 return;
25236             }
25237             
25238             valid = true;
25239             
25240             return false;
25241         });
25242         
25243         return valid;
25244     },
25245     
25246     validateCheckbox : function()
25247     {
25248         if(!this.groupId){
25249             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25250             //return (this.getValue() == this.inputValue) ? true : false;
25251         }
25252         
25253         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25254         
25255         if(!group){
25256             return false;
25257         }
25258         
25259         var r = false;
25260         
25261         for(var i in group){
25262             if(group[i].el.isVisible(true)){
25263                 r = false;
25264                 break;
25265             }
25266             
25267             r = true;
25268         }
25269         
25270         for(var i in group){
25271             if(r){
25272                 break;
25273             }
25274             
25275             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25276         }
25277         
25278         return r;
25279     },
25280     
25281     /**
25282      * Mark this field as valid
25283      */
25284     markValid : function()
25285     {
25286         var _this = this;
25287         
25288         this.fireEvent('valid', this);
25289         
25290         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25291         
25292         if(this.groupId){
25293             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25294         }
25295         
25296         if(label){
25297             label.markValid();
25298         }
25299
25300         if(this.inputType == 'radio'){
25301             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25302                 var fg = e.findParent('.form-group', false, true);
25303                 if (Roo.bootstrap.version == 3) {
25304                     fg.removeClass([_this.invalidClass, _this.validClass]);
25305                     fg.addClass(_this.validClass);
25306                 } else {
25307                     fg.removeClass(['is-valid', 'is-invalid']);
25308                     fg.addClass('is-valid');
25309                 }
25310             });
25311             
25312             return;
25313         }
25314
25315         if(!this.groupId){
25316             var fg = this.el.findParent('.form-group', false, true);
25317             if (Roo.bootstrap.version == 3) {
25318                 fg.removeClass([this.invalidClass, this.validClass]);
25319                 fg.addClass(this.validClass);
25320             } else {
25321                 fg.removeClass(['is-valid', 'is-invalid']);
25322                 fg.addClass('is-valid');
25323             }
25324             return;
25325         }
25326         
25327         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25328         
25329         if(!group){
25330             return;
25331         }
25332         
25333         for(var i in group){
25334             var fg = group[i].el.findParent('.form-group', false, true);
25335             if (Roo.bootstrap.version == 3) {
25336                 fg.removeClass([this.invalidClass, this.validClass]);
25337                 fg.addClass(this.validClass);
25338             } else {
25339                 fg.removeClass(['is-valid', 'is-invalid']);
25340                 fg.addClass('is-valid');
25341             }
25342         }
25343     },
25344     
25345      /**
25346      * Mark this field as invalid
25347      * @param {String} msg The validation message
25348      */
25349     markInvalid : function(msg)
25350     {
25351         if(this.allowBlank){
25352             return;
25353         }
25354         
25355         var _this = this;
25356         
25357         this.fireEvent('invalid', this, msg);
25358         
25359         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25360         
25361         if(this.groupId){
25362             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25363         }
25364         
25365         if(label){
25366             label.markInvalid();
25367         }
25368             
25369         if(this.inputType == 'radio'){
25370             
25371             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25372                 var fg = e.findParent('.form-group', false, true);
25373                 if (Roo.bootstrap.version == 3) {
25374                     fg.removeClass([_this.invalidClass, _this.validClass]);
25375                     fg.addClass(_this.invalidClass);
25376                 } else {
25377                     fg.removeClass(['is-invalid', 'is-valid']);
25378                     fg.addClass('is-invalid');
25379                 }
25380             });
25381             
25382             return;
25383         }
25384         
25385         if(!this.groupId){
25386             var fg = this.el.findParent('.form-group', false, true);
25387             if (Roo.bootstrap.version == 3) {
25388                 fg.removeClass([_this.invalidClass, _this.validClass]);
25389                 fg.addClass(_this.invalidClass);
25390             } else {
25391                 fg.removeClass(['is-invalid', 'is-valid']);
25392                 fg.addClass('is-invalid');
25393             }
25394             return;
25395         }
25396         
25397         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25398         
25399         if(!group){
25400             return;
25401         }
25402         
25403         for(var i in group){
25404             var fg = group[i].el.findParent('.form-group', false, true);
25405             if (Roo.bootstrap.version == 3) {
25406                 fg.removeClass([_this.invalidClass, _this.validClass]);
25407                 fg.addClass(_this.invalidClass);
25408             } else {
25409                 fg.removeClass(['is-invalid', 'is-valid']);
25410                 fg.addClass('is-invalid');
25411             }
25412         }
25413         
25414     },
25415     
25416     clearInvalid : function()
25417     {
25418         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25419         
25420         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25421         
25422         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25423         
25424         if (label && label.iconEl) {
25425             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25426             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25427         }
25428     },
25429     
25430     disable : function()
25431     {
25432         if(this.inputType != 'radio'){
25433             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25434             return;
25435         }
25436         
25437         var _this = this;
25438         
25439         if(this.rendered){
25440             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25441                 _this.getActionEl().addClass(this.disabledClass);
25442                 e.dom.disabled = true;
25443             });
25444         }
25445         
25446         this.disabled = true;
25447         this.fireEvent("disable", this);
25448         return this;
25449     },
25450
25451     enable : function()
25452     {
25453         if(this.inputType != 'radio'){
25454             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25455             return;
25456         }
25457         
25458         var _this = this;
25459         
25460         if(this.rendered){
25461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25462                 _this.getActionEl().removeClass(this.disabledClass);
25463                 e.dom.disabled = false;
25464             });
25465         }
25466         
25467         this.disabled = false;
25468         this.fireEvent("enable", this);
25469         return this;
25470     },
25471     
25472     setBoxLabel : function(v)
25473     {
25474         this.boxLabel = v;
25475         
25476         if(this.rendered){
25477             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25478         }
25479     }
25480
25481 });
25482
25483 Roo.apply(Roo.bootstrap.form.CheckBox, {
25484     
25485     groups: {},
25486     
25487      /**
25488     * register a CheckBox Group
25489     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25490     */
25491     register : function(checkbox)
25492     {
25493         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25494             this.groups[checkbox.groupId] = {};
25495         }
25496         
25497         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25498             return;
25499         }
25500         
25501         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25502         
25503     },
25504     /**
25505     * fetch a CheckBox Group based on the group ID
25506     * @param {string} the group ID
25507     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25508     */
25509     get: function(groupId) {
25510         if (typeof(this.groups[groupId]) == 'undefined') {
25511             return false;
25512         }
25513         
25514         return this.groups[groupId] ;
25515     }
25516     
25517     
25518 });
25519 /*
25520  * - LGPL
25521  *
25522  * RadioItem
25523  * 
25524  */
25525
25526 /**
25527  * @class Roo.bootstrap.form.Radio
25528  * @extends Roo.bootstrap.Component
25529  * Bootstrap Radio class
25530  * @cfg {String} boxLabel - the label associated
25531  * @cfg {String} value - the value of radio
25532  * 
25533  * @constructor
25534  * Create a new Radio
25535  * @param {Object} config The config object
25536  */
25537 Roo.bootstrap.form.Radio = function(config){
25538     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25539     
25540 };
25541
25542 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25543     
25544     boxLabel : '',
25545     
25546     value : '',
25547     
25548     getAutoCreate : function()
25549     {
25550         var cfg = {
25551             tag : 'div',
25552             cls : 'form-group radio',
25553             cn : [
25554                 {
25555                     tag : 'label',
25556                     cls : 'box-label',
25557                     html : this.boxLabel
25558                 }
25559             ]
25560         };
25561         
25562         return cfg;
25563     },
25564     
25565     initEvents : function() 
25566     {
25567         this.parent().register(this);
25568         
25569         this.el.on('click', this.onClick, this);
25570         
25571     },
25572     
25573     onClick : function(e)
25574     {
25575         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25576             this.setChecked(true);
25577         }
25578     },
25579     
25580     setChecked : function(state, suppressEvent)
25581     {
25582         this.parent().setValue(this.value, suppressEvent);
25583         
25584     },
25585     
25586     setBoxLabel : function(v)
25587     {
25588         this.boxLabel = v;
25589         
25590         if(this.rendered){
25591             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25592         }
25593     }
25594     
25595 });
25596  
25597
25598  /*
25599  * - LGPL
25600  *
25601  * Input
25602  * 
25603  */
25604
25605 /**
25606  * @class Roo.bootstrap.form.SecurePass
25607  * @extends Roo.bootstrap.form.Input
25608  * Bootstrap SecurePass class
25609  *
25610  * 
25611  * @constructor
25612  * Create a new SecurePass
25613  * @param {Object} config The config object
25614  */
25615  
25616 Roo.bootstrap.form.SecurePass = function (config) {
25617     // these go here, so the translation tool can replace them..
25618     this.errors = {
25619         PwdEmpty: "Please type a password, and then retype it to confirm.",
25620         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25621         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25622         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25623         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25624         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25625         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25626         TooWeak: "Your password is Too Weak."
25627     },
25628     this.meterLabel = "Password strength:";
25629     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25630     this.meterClass = [
25631         "roo-password-meter-tooweak", 
25632         "roo-password-meter-weak", 
25633         "roo-password-meter-medium", 
25634         "roo-password-meter-strong", 
25635         "roo-password-meter-grey"
25636     ];
25637     
25638     this.errors = {};
25639     
25640     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25641 }
25642
25643 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25644     /**
25645      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25646      * {
25647      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25648      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25649      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25650      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25651      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25652      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25653      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25654      * })
25655      */
25656     // private
25657     
25658     meterWidth: 300,
25659     errorMsg :'',    
25660     errors: false,
25661     imageRoot: '/',
25662     /**
25663      * @cfg {String/Object} Label for the strength meter (defaults to
25664      * 'Password strength:')
25665      */
25666     // private
25667     meterLabel: '',
25668     /**
25669      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25670      * ['Weak', 'Medium', 'Strong'])
25671      */
25672     // private    
25673     pwdStrengths: false,    
25674     // private
25675     strength: 0,
25676     // private
25677     _lastPwd: null,
25678     // private
25679     kCapitalLetter: 0,
25680     kSmallLetter: 1,
25681     kDigit: 2,
25682     kPunctuation: 3,
25683     
25684     insecure: false,
25685     // private
25686     initEvents: function ()
25687     {
25688         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25689
25690         if (this.el.is('input[type=password]') && Roo.isSafari) {
25691             this.el.on('keydown', this.SafariOnKeyDown, this);
25692         }
25693
25694         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25695     },
25696     // private
25697     onRender: function (ct, position)
25698     {
25699         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25700         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25701         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25702
25703         this.trigger.createChild({
25704                    cn: [
25705                     {
25706                     //id: 'PwdMeter',
25707                     tag: 'div',
25708                     cls: 'roo-password-meter-grey col-xs-12',
25709                     style: {
25710                         //width: 0,
25711                         //width: this.meterWidth + 'px'                                                
25712                         }
25713                     },
25714                     {                            
25715                          cls: 'roo-password-meter-text'                          
25716                     }
25717                 ]            
25718         });
25719
25720          
25721         if (this.hideTrigger) {
25722             this.trigger.setDisplayed(false);
25723         }
25724         this.setSize(this.width || '', this.height || '');
25725     },
25726     // private
25727     onDestroy: function ()
25728     {
25729         if (this.trigger) {
25730             this.trigger.removeAllListeners();
25731             this.trigger.remove();
25732         }
25733         if (this.wrap) {
25734             this.wrap.remove();
25735         }
25736         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25737     },
25738     // private
25739     checkStrength: function ()
25740     {
25741         var pwd = this.inputEl().getValue();
25742         if (pwd == this._lastPwd) {
25743             return;
25744         }
25745
25746         var strength;
25747         if (this.ClientSideStrongPassword(pwd)) {
25748             strength = 3;
25749         } else if (this.ClientSideMediumPassword(pwd)) {
25750             strength = 2;
25751         } else if (this.ClientSideWeakPassword(pwd)) {
25752             strength = 1;
25753         } else {
25754             strength = 0;
25755         }
25756         
25757         Roo.log('strength1: ' + strength);
25758         
25759         //var pm = this.trigger.child('div/div/div').dom;
25760         var pm = this.trigger.child('div/div');
25761         pm.removeClass(this.meterClass);
25762         pm.addClass(this.meterClass[strength]);
25763                 
25764         
25765         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25766                 
25767         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25768         
25769         this._lastPwd = pwd;
25770     },
25771     reset: function ()
25772     {
25773         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25774         
25775         this._lastPwd = '';
25776         
25777         var pm = this.trigger.child('div/div');
25778         pm.removeClass(this.meterClass);
25779         pm.addClass('roo-password-meter-grey');        
25780         
25781         
25782         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25783         
25784         pt.innerHTML = '';
25785         this.inputEl().dom.type='password';
25786     },
25787     // private
25788     validateValue: function (value)
25789     {
25790         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25791             return false;
25792         }
25793         if (value.length == 0) {
25794             if (this.allowBlank) {
25795                 this.clearInvalid();
25796                 return true;
25797             }
25798
25799             this.markInvalid(this.errors.PwdEmpty);
25800             this.errorMsg = this.errors.PwdEmpty;
25801             return false;
25802         }
25803         
25804         if(this.insecure){
25805             return true;
25806         }
25807         
25808         if (!value.match(/[\x21-\x7e]+/)) {
25809             this.markInvalid(this.errors.PwdBadChar);
25810             this.errorMsg = this.errors.PwdBadChar;
25811             return false;
25812         }
25813         if (value.length < 6) {
25814             this.markInvalid(this.errors.PwdShort);
25815             this.errorMsg = this.errors.PwdShort;
25816             return false;
25817         }
25818         if (value.length > 16) {
25819             this.markInvalid(this.errors.PwdLong);
25820             this.errorMsg = this.errors.PwdLong;
25821             return false;
25822         }
25823         var strength;
25824         if (this.ClientSideStrongPassword(value)) {
25825             strength = 3;
25826         } else if (this.ClientSideMediumPassword(value)) {
25827             strength = 2;
25828         } else if (this.ClientSideWeakPassword(value)) {
25829             strength = 1;
25830         } else {
25831             strength = 0;
25832         }
25833
25834         
25835         if (strength < 2) {
25836             //this.markInvalid(this.errors.TooWeak);
25837             this.errorMsg = this.errors.TooWeak;
25838             //return false;
25839         }
25840         
25841         
25842         console.log('strength2: ' + strength);
25843         
25844         //var pm = this.trigger.child('div/div/div').dom;
25845         
25846         var pm = this.trigger.child('div/div');
25847         pm.removeClass(this.meterClass);
25848         pm.addClass(this.meterClass[strength]);
25849                 
25850         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25851                 
25852         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25853         
25854         this.errorMsg = ''; 
25855         return true;
25856     },
25857     // private
25858     CharacterSetChecks: function (type)
25859     {
25860         this.type = type;
25861         this.fResult = false;
25862     },
25863     // private
25864     isctype: function (character, type)
25865     {
25866         switch (type) {  
25867             case this.kCapitalLetter:
25868                 if (character >= 'A' && character <= 'Z') {
25869                     return true;
25870                 }
25871                 break;
25872             
25873             case this.kSmallLetter:
25874                 if (character >= 'a' && character <= 'z') {
25875                     return true;
25876                 }
25877                 break;
25878             
25879             case this.kDigit:
25880                 if (character >= '0' && character <= '9') {
25881                     return true;
25882                 }
25883                 break;
25884             
25885             case this.kPunctuation:
25886                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25887                     return true;
25888                 }
25889                 break;
25890             
25891             default:
25892                 return false;
25893         }
25894
25895     },
25896     // private
25897     IsLongEnough: function (pwd, size)
25898     {
25899         return !(pwd == null || isNaN(size) || pwd.length < size);
25900     },
25901     // private
25902     SpansEnoughCharacterSets: function (word, nb)
25903     {
25904         if (!this.IsLongEnough(word, nb))
25905         {
25906             return false;
25907         }
25908
25909         var characterSetChecks = new Array(
25910             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25911             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25912         );
25913         
25914         for (var index = 0; index < word.length; ++index) {
25915             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25916                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25917                     characterSetChecks[nCharSet].fResult = true;
25918                     break;
25919                 }
25920             }
25921         }
25922
25923         var nCharSets = 0;
25924         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25925             if (characterSetChecks[nCharSet].fResult) {
25926                 ++nCharSets;
25927             }
25928         }
25929
25930         if (nCharSets < nb) {
25931             return false;
25932         }
25933         return true;
25934     },
25935     // private
25936     ClientSideStrongPassword: function (pwd)
25937     {
25938         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25939     },
25940     // private
25941     ClientSideMediumPassword: function (pwd)
25942     {
25943         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25944     },
25945     // private
25946     ClientSideWeakPassword: function (pwd)
25947     {
25948         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25949     }
25950           
25951 })//<script type="text/javascript">
25952
25953 /*
25954  * Based  Ext JS Library 1.1.1
25955  * Copyright(c) 2006-2007, Ext JS, LLC.
25956  * LGPL
25957  *
25958  */
25959  
25960 /**
25961  * @class Roo.HtmlEditorCore
25962  * @extends Roo.Component
25963  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25964  *
25965  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25966  */
25967
25968 Roo.HtmlEditorCore = function(config){
25969     
25970     
25971     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25972     
25973     
25974     this.addEvents({
25975         /**
25976          * @event initialize
25977          * Fires when the editor is fully initialized (including the iframe)
25978          * @param {Roo.HtmlEditorCore} this
25979          */
25980         initialize: true,
25981         /**
25982          * @event activate
25983          * Fires when the editor is first receives the focus. Any insertion must wait
25984          * until after this event.
25985          * @param {Roo.HtmlEditorCore} this
25986          */
25987         activate: true,
25988          /**
25989          * @event beforesync
25990          * Fires before the textarea is updated with content from the editor iframe. Return false
25991          * to cancel the sync.
25992          * @param {Roo.HtmlEditorCore} this
25993          * @param {String} html
25994          */
25995         beforesync: true,
25996          /**
25997          * @event beforepush
25998          * Fires before the iframe editor is updated with content from the textarea. Return false
25999          * to cancel the push.
26000          * @param {Roo.HtmlEditorCore} this
26001          * @param {String} html
26002          */
26003         beforepush: true,
26004          /**
26005          * @event sync
26006          * Fires when the textarea is updated with content from the editor iframe.
26007          * @param {Roo.HtmlEditorCore} this
26008          * @param {String} html
26009          */
26010         sync: true,
26011          /**
26012          * @event push
26013          * Fires when the iframe editor is updated with content from the textarea.
26014          * @param {Roo.HtmlEditorCore} this
26015          * @param {String} html
26016          */
26017         push: true,
26018         
26019         /**
26020          * @event editorevent
26021          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26022          * @param {Roo.HtmlEditorCore} this
26023          */
26024         editorevent: true
26025         
26026     });
26027     
26028     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26029     
26030     // defaults : white / black...
26031     this.applyBlacklists();
26032     
26033     
26034     
26035 };
26036
26037
26038 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26039
26040
26041      /**
26042      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26043      */
26044     
26045     owner : false,
26046     
26047      /**
26048      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26049      *                        Roo.resizable.
26050      */
26051     resizable : false,
26052      /**
26053      * @cfg {Number} height (in pixels)
26054      */   
26055     height: 300,
26056    /**
26057      * @cfg {Number} width (in pixels)
26058      */   
26059     width: 500,
26060     
26061     /**
26062      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26063      * 
26064      */
26065     stylesheets: false,
26066     
26067     /**
26068      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26069      */
26070     allowComments: false,
26071     // id of frame..
26072     frameId: false,
26073     
26074     // private properties
26075     validationEvent : false,
26076     deferHeight: true,
26077     initialized : false,
26078     activated : false,
26079     sourceEditMode : false,
26080     onFocus : Roo.emptyFn,
26081     iframePad:3,
26082     hideMode:'offsets',
26083     
26084     clearUp: true,
26085     
26086     // blacklist + whitelisted elements..
26087     black: false,
26088     white: false,
26089      
26090     bodyCls : '',
26091
26092     /**
26093      * Protected method that will not generally be called directly. It
26094      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26095      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26096      */
26097     getDocMarkup : function(){
26098         // body styles..
26099         var st = '';
26100         
26101         // inherit styels from page...?? 
26102         if (this.stylesheets === false) {
26103             
26104             Roo.get(document.head).select('style').each(function(node) {
26105                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26106             });
26107             
26108             Roo.get(document.head).select('link').each(function(node) { 
26109                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26110             });
26111             
26112         } else if (!this.stylesheets.length) {
26113                 // simple..
26114                 st = '<style type="text/css">' +
26115                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26116                    '</style>';
26117         } else {
26118             for (var i in this.stylesheets) { 
26119                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26120             }
26121             
26122         }
26123         
26124         st +=  '<style type="text/css">' +
26125             'IMG { cursor: pointer } ' +
26126         '</style>';
26127
26128         var cls = 'roo-htmleditor-body';
26129         
26130         if(this.bodyCls.length){
26131             cls += ' ' + this.bodyCls;
26132         }
26133         
26134         return '<html><head>' + st  +
26135             //<style type="text/css">' +
26136             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26137             //'</style>' +
26138             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26139     },
26140
26141     // private
26142     onRender : function(ct, position)
26143     {
26144         var _t = this;
26145         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26146         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26147         
26148         
26149         this.el.dom.style.border = '0 none';
26150         this.el.dom.setAttribute('tabIndex', -1);
26151         this.el.addClass('x-hidden hide');
26152         
26153         
26154         
26155         if(Roo.isIE){ // fix IE 1px bogus margin
26156             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26157         }
26158        
26159         
26160         this.frameId = Roo.id();
26161         
26162          
26163         
26164         var iframe = this.owner.wrap.createChild({
26165             tag: 'iframe',
26166             cls: 'form-control', // bootstrap..
26167             id: this.frameId,
26168             name: this.frameId,
26169             frameBorder : 'no',
26170             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26171         }, this.el
26172         );
26173         
26174         
26175         this.iframe = iframe.dom;
26176
26177          this.assignDocWin();
26178         
26179         this.doc.designMode = 'on';
26180        
26181         this.doc.open();
26182         this.doc.write(this.getDocMarkup());
26183         this.doc.close();
26184
26185         
26186         var task = { // must defer to wait for browser to be ready
26187             run : function(){
26188                 //console.log("run task?" + this.doc.readyState);
26189                 this.assignDocWin();
26190                 if(this.doc.body || this.doc.readyState == 'complete'){
26191                     try {
26192                         this.doc.designMode="on";
26193                     } catch (e) {
26194                         return;
26195                     }
26196                     Roo.TaskMgr.stop(task);
26197                     this.initEditor.defer(10, this);
26198                 }
26199             },
26200             interval : 10,
26201             duration: 10000,
26202             scope: this
26203         };
26204         Roo.TaskMgr.start(task);
26205
26206     },
26207
26208     // private
26209     onResize : function(w, h)
26210     {
26211          Roo.log('resize: ' +w + ',' + h );
26212         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26213         if(!this.iframe){
26214             return;
26215         }
26216         if(typeof w == 'number'){
26217             
26218             this.iframe.style.width = w + 'px';
26219         }
26220         if(typeof h == 'number'){
26221             
26222             this.iframe.style.height = h + 'px';
26223             if(this.doc){
26224                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26225             }
26226         }
26227         
26228     },
26229
26230     /**
26231      * Toggles the editor between standard and source edit mode.
26232      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26233      */
26234     toggleSourceEdit : function(sourceEditMode){
26235         
26236         this.sourceEditMode = sourceEditMode === true;
26237         
26238         if(this.sourceEditMode){
26239  
26240             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26241             
26242         }else{
26243             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26244             //this.iframe.className = '';
26245             this.deferFocus();
26246         }
26247         //this.setSize(this.owner.wrap.getSize());
26248         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26249     },
26250
26251     
26252   
26253
26254     /**
26255      * Protected method that will not generally be called directly. If you need/want
26256      * custom HTML cleanup, this is the method you should override.
26257      * @param {String} html The HTML to be cleaned
26258      * return {String} The cleaned HTML
26259      */
26260     cleanHtml : function(html){
26261         html = String(html);
26262         if(html.length > 5){
26263             if(Roo.isSafari){ // strip safari nonsense
26264                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26265             }
26266         }
26267         if(html == '&nbsp;'){
26268             html = '';
26269         }
26270         return html;
26271     },
26272
26273     /**
26274      * HTML Editor -> Textarea
26275      * Protected method that will not generally be called directly. Syncs the contents
26276      * of the editor iframe with the textarea.
26277      */
26278     syncValue : function(){
26279         if(this.initialized){
26280             var bd = (this.doc.body || this.doc.documentElement);
26281             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26282             var html = bd.innerHTML;
26283             if(Roo.isSafari){
26284                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26285                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26286                 if(m && m[1]){
26287                     html = '<div style="'+m[0]+'">' + html + '</div>';
26288                 }
26289             }
26290             html = this.cleanHtml(html);
26291             // fix up the special chars.. normaly like back quotes in word...
26292             // however we do not want to do this with chinese..
26293             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26294                 
26295                 var cc = match.charCodeAt();
26296
26297                 // Get the character value, handling surrogate pairs
26298                 if (match.length == 2) {
26299                     // It's a surrogate pair, calculate the Unicode code point
26300                     var high = match.charCodeAt(0) - 0xD800;
26301                     var low  = match.charCodeAt(1) - 0xDC00;
26302                     cc = (high * 0x400) + low + 0x10000;
26303                 }  else if (
26304                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26305                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26306                     (cc >= 0xf900 && cc < 0xfb00 )
26307                 ) {
26308                         return match;
26309                 }  
26310          
26311                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26312                 return "&#" + cc + ";";
26313                 
26314                 
26315             });
26316             
26317             
26318              
26319             if(this.owner.fireEvent('beforesync', this, html) !== false){
26320                 this.el.dom.value = html;
26321                 this.owner.fireEvent('sync', this, html);
26322             }
26323         }
26324     },
26325
26326     /**
26327      * Protected method that will not generally be called directly. Pushes the value of the textarea
26328      * into the iframe editor.
26329      */
26330     pushValue : function(){
26331         if(this.initialized){
26332             var v = this.el.dom.value.trim();
26333             
26334 //            if(v.length < 1){
26335 //                v = '&#160;';
26336 //            }
26337             
26338             if(this.owner.fireEvent('beforepush', this, v) !== false){
26339                 var d = (this.doc.body || this.doc.documentElement);
26340                 d.innerHTML = v;
26341                 this.cleanUpPaste();
26342                 this.el.dom.value = d.innerHTML;
26343                 this.owner.fireEvent('push', this, v);
26344             }
26345         }
26346     },
26347
26348     // private
26349     deferFocus : function(){
26350         this.focus.defer(10, this);
26351     },
26352
26353     // doc'ed in Field
26354     focus : function(){
26355         if(this.win && !this.sourceEditMode){
26356             this.win.focus();
26357         }else{
26358             this.el.focus();
26359         }
26360     },
26361     
26362     assignDocWin: function()
26363     {
26364         var iframe = this.iframe;
26365         
26366          if(Roo.isIE){
26367             this.doc = iframe.contentWindow.document;
26368             this.win = iframe.contentWindow;
26369         } else {
26370 //            if (!Roo.get(this.frameId)) {
26371 //                return;
26372 //            }
26373 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26374 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26375             
26376             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26377                 return;
26378             }
26379             
26380             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26381             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26382         }
26383     },
26384     
26385     // private
26386     initEditor : function(){
26387         //console.log("INIT EDITOR");
26388         this.assignDocWin();
26389         
26390         
26391         
26392         this.doc.designMode="on";
26393         this.doc.open();
26394         this.doc.write(this.getDocMarkup());
26395         this.doc.close();
26396         
26397         var dbody = (this.doc.body || this.doc.documentElement);
26398         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26399         // this copies styles from the containing element into thsi one..
26400         // not sure why we need all of this..
26401         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26402         
26403         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26404         //ss['background-attachment'] = 'fixed'; // w3c
26405         dbody.bgProperties = 'fixed'; // ie
26406         //Roo.DomHelper.applyStyles(dbody, ss);
26407         Roo.EventManager.on(this.doc, {
26408             //'mousedown': this.onEditorEvent,
26409             'mouseup': this.onEditorEvent,
26410             'dblclick': this.onEditorEvent,
26411             'click': this.onEditorEvent,
26412             'keyup': this.onEditorEvent,
26413             buffer:100,
26414             scope: this
26415         });
26416         if(Roo.isGecko){
26417             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26418         }
26419         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26420             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26421         }
26422         this.initialized = true;
26423
26424         this.owner.fireEvent('initialize', this);
26425         this.pushValue();
26426     },
26427
26428     // private
26429     onDestroy : function(){
26430         
26431         
26432         
26433         if(this.rendered){
26434             
26435             //for (var i =0; i < this.toolbars.length;i++) {
26436             //    // fixme - ask toolbars for heights?
26437             //    this.toolbars[i].onDestroy();
26438            // }
26439             
26440             //this.wrap.dom.innerHTML = '';
26441             //this.wrap.remove();
26442         }
26443     },
26444
26445     // private
26446     onFirstFocus : function(){
26447         
26448         this.assignDocWin();
26449         
26450         
26451         this.activated = true;
26452          
26453     
26454         if(Roo.isGecko){ // prevent silly gecko errors
26455             this.win.focus();
26456             var s = this.win.getSelection();
26457             if(!s.focusNode || s.focusNode.nodeType != 3){
26458                 var r = s.getRangeAt(0);
26459                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26460                 r.collapse(true);
26461                 this.deferFocus();
26462             }
26463             try{
26464                 this.execCmd('useCSS', true);
26465                 this.execCmd('styleWithCSS', false);
26466             }catch(e){}
26467         }
26468         this.owner.fireEvent('activate', this);
26469     },
26470
26471     // private
26472     adjustFont: function(btn){
26473         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26474         //if(Roo.isSafari){ // safari
26475         //    adjust *= 2;
26476        // }
26477         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26478         if(Roo.isSafari){ // safari
26479             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26480             v =  (v < 10) ? 10 : v;
26481             v =  (v > 48) ? 48 : v;
26482             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26483             
26484         }
26485         
26486         
26487         v = Math.max(1, v+adjust);
26488         
26489         this.execCmd('FontSize', v  );
26490     },
26491
26492     onEditorEvent : function(e)
26493     {
26494         this.owner.fireEvent('editorevent', this, e);
26495       //  this.updateToolbar();
26496         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26497     },
26498
26499     insertTag : function(tg)
26500     {
26501         // could be a bit smarter... -> wrap the current selected tRoo..
26502         if (tg.toLowerCase() == 'span' ||
26503             tg.toLowerCase() == 'code' ||
26504             tg.toLowerCase() == 'sup' ||
26505             tg.toLowerCase() == 'sub' 
26506             ) {
26507             
26508             range = this.createRange(this.getSelection());
26509             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26510             wrappingNode.appendChild(range.extractContents());
26511             range.insertNode(wrappingNode);
26512
26513             return;
26514             
26515             
26516             
26517         }
26518         this.execCmd("formatblock",   tg);
26519         
26520     },
26521     
26522     insertText : function(txt)
26523     {
26524         
26525         
26526         var range = this.createRange();
26527         range.deleteContents();
26528                //alert(Sender.getAttribute('label'));
26529                
26530         range.insertNode(this.doc.createTextNode(txt));
26531     } ,
26532     
26533      
26534
26535     /**
26536      * Executes a Midas editor command on the editor document and performs necessary focus and
26537      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26538      * @param {String} cmd The Midas command
26539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26540      */
26541     relayCmd : function(cmd, value){
26542         this.win.focus();
26543         this.execCmd(cmd, value);
26544         this.owner.fireEvent('editorevent', this);
26545         //this.updateToolbar();
26546         this.owner.deferFocus();
26547     },
26548
26549     /**
26550      * Executes a Midas editor command directly on the editor document.
26551      * For visual commands, you should use {@link #relayCmd} instead.
26552      * <b>This should only be called after the editor is initialized.</b>
26553      * @param {String} cmd The Midas command
26554      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26555      */
26556     execCmd : function(cmd, value){
26557         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26558         this.syncValue();
26559     },
26560  
26561  
26562    
26563     /**
26564      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26565      * to insert tRoo.
26566      * @param {String} text | dom node.. 
26567      */
26568     insertAtCursor : function(text)
26569     {
26570         
26571         if(!this.activated){
26572             return;
26573         }
26574         /*
26575         if(Roo.isIE){
26576             this.win.focus();
26577             var r = this.doc.selection.createRange();
26578             if(r){
26579                 r.collapse(true);
26580                 r.pasteHTML(text);
26581                 this.syncValue();
26582                 this.deferFocus();
26583             
26584             }
26585             return;
26586         }
26587         */
26588         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26589             this.win.focus();
26590             
26591             
26592             // from jquery ui (MIT licenced)
26593             var range, node;
26594             var win = this.win;
26595             
26596             if (win.getSelection && win.getSelection().getRangeAt) {
26597                 range = win.getSelection().getRangeAt(0);
26598                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26599                 range.insertNode(node);
26600             } else if (win.document.selection && win.document.selection.createRange) {
26601                 // no firefox support
26602                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26603                 win.document.selection.createRange().pasteHTML(txt);
26604             } else {
26605                 // no firefox support
26606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26607                 this.execCmd('InsertHTML', txt);
26608             } 
26609             
26610             this.syncValue();
26611             
26612             this.deferFocus();
26613         }
26614     },
26615  // private
26616     mozKeyPress : function(e){
26617         if(e.ctrlKey){
26618             var c = e.getCharCode(), cmd;
26619           
26620             if(c > 0){
26621                 c = String.fromCharCode(c).toLowerCase();
26622                 switch(c){
26623                     case 'b':
26624                         cmd = 'bold';
26625                         break;
26626                     case 'i':
26627                         cmd = 'italic';
26628                         break;
26629                     
26630                     case 'u':
26631                         cmd = 'underline';
26632                         break;
26633                     
26634                     case 'v':
26635                         this.cleanUpPaste.defer(100, this);
26636                         return;
26637                         
26638                 }
26639                 if(cmd){
26640                     this.win.focus();
26641                     this.execCmd(cmd);
26642                     this.deferFocus();
26643                     e.preventDefault();
26644                 }
26645                 
26646             }
26647         }
26648     },
26649
26650     // private
26651     fixKeys : function(){ // load time branching for fastest keydown performance
26652         if(Roo.isIE){
26653             return function(e){
26654                 var k = e.getKey(), r;
26655                 if(k == e.TAB){
26656                     e.stopEvent();
26657                     r = this.doc.selection.createRange();
26658                     if(r){
26659                         r.collapse(true);
26660                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26661                         this.deferFocus();
26662                     }
26663                     return;
26664                 }
26665                 
26666                 if(k == e.ENTER){
26667                     r = this.doc.selection.createRange();
26668                     if(r){
26669                         var target = r.parentElement();
26670                         if(!target || target.tagName.toLowerCase() != 'li'){
26671                             e.stopEvent();
26672                             r.pasteHTML('<br />');
26673                             r.collapse(false);
26674                             r.select();
26675                         }
26676                     }
26677                 }
26678                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679                     this.cleanUpPaste.defer(100, this);
26680                     return;
26681                 }
26682                 
26683                 
26684             };
26685         }else if(Roo.isOpera){
26686             return function(e){
26687                 var k = e.getKey();
26688                 if(k == e.TAB){
26689                     e.stopEvent();
26690                     this.win.focus();
26691                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26692                     this.deferFocus();
26693                 }
26694                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695                     this.cleanUpPaste.defer(100, this);
26696                     return;
26697                 }
26698                 
26699             };
26700         }else if(Roo.isSafari){
26701             return function(e){
26702                 var k = e.getKey();
26703                 
26704                 if(k == e.TAB){
26705                     e.stopEvent();
26706                     this.execCmd('InsertText','\t');
26707                     this.deferFocus();
26708                     return;
26709                 }
26710                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26711                     this.cleanUpPaste.defer(100, this);
26712                     return;
26713                 }
26714                 
26715              };
26716         }
26717     }(),
26718     
26719     getAllAncestors: function()
26720     {
26721         var p = this.getSelectedNode();
26722         var a = [];
26723         if (!p) {
26724             a.push(p); // push blank onto stack..
26725             p = this.getParentElement();
26726         }
26727         
26728         
26729         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26730             a.push(p);
26731             p = p.parentNode;
26732         }
26733         a.push(this.doc.body);
26734         return a;
26735     },
26736     lastSel : false,
26737     lastSelNode : false,
26738     
26739     
26740     getSelection : function() 
26741     {
26742         this.assignDocWin();
26743         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26744     },
26745     
26746     getSelectedNode: function() 
26747     {
26748         // this may only work on Gecko!!!
26749         
26750         // should we cache this!!!!
26751         
26752         
26753         
26754          
26755         var range = this.createRange(this.getSelection()).cloneRange();
26756         
26757         if (Roo.isIE) {
26758             var parent = range.parentElement();
26759             while (true) {
26760                 var testRange = range.duplicate();
26761                 testRange.moveToElementText(parent);
26762                 if (testRange.inRange(range)) {
26763                     break;
26764                 }
26765                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26766                     break;
26767                 }
26768                 parent = parent.parentElement;
26769             }
26770             return parent;
26771         }
26772         
26773         // is ancestor a text element.
26774         var ac =  range.commonAncestorContainer;
26775         if (ac.nodeType == 3) {
26776             ac = ac.parentNode;
26777         }
26778         
26779         var ar = ac.childNodes;
26780          
26781         var nodes = [];
26782         var other_nodes = [];
26783         var has_other_nodes = false;
26784         for (var i=0;i<ar.length;i++) {
26785             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26786                 continue;
26787             }
26788             // fullly contained node.
26789             
26790             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26791                 nodes.push(ar[i]);
26792                 continue;
26793             }
26794             
26795             // probably selected..
26796             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26797                 other_nodes.push(ar[i]);
26798                 continue;
26799             }
26800             // outer..
26801             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26802                 continue;
26803             }
26804             
26805             
26806             has_other_nodes = true;
26807         }
26808         if (!nodes.length && other_nodes.length) {
26809             nodes= other_nodes;
26810         }
26811         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26812             return false;
26813         }
26814         
26815         return nodes[0];
26816     },
26817     createRange: function(sel)
26818     {
26819         // this has strange effects when using with 
26820         // top toolbar - not sure if it's a great idea.
26821         //this.editor.contentWindow.focus();
26822         if (typeof sel != "undefined") {
26823             try {
26824                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26825             } catch(e) {
26826                 return this.doc.createRange();
26827             }
26828         } else {
26829             return this.doc.createRange();
26830         }
26831     },
26832     getParentElement: function()
26833     {
26834         
26835         this.assignDocWin();
26836         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26837         
26838         var range = this.createRange(sel);
26839          
26840         try {
26841             var p = range.commonAncestorContainer;
26842             while (p.nodeType == 3) { // text node
26843                 p = p.parentNode;
26844             }
26845             return p;
26846         } catch (e) {
26847             return null;
26848         }
26849     
26850     },
26851     /***
26852      *
26853      * Range intersection.. the hard stuff...
26854      *  '-1' = before
26855      *  '0' = hits..
26856      *  '1' = after.
26857      *         [ -- selected range --- ]
26858      *   [fail]                        [fail]
26859      *
26860      *    basically..
26861      *      if end is before start or  hits it. fail.
26862      *      if start is after end or hits it fail.
26863      *
26864      *   if either hits (but other is outside. - then it's not 
26865      *   
26866      *    
26867      **/
26868     
26869     
26870     // @see http://www.thismuchiknow.co.uk/?p=64.
26871     rangeIntersectsNode : function(range, node)
26872     {
26873         var nodeRange = node.ownerDocument.createRange();
26874         try {
26875             nodeRange.selectNode(node);
26876         } catch (e) {
26877             nodeRange.selectNodeContents(node);
26878         }
26879     
26880         var rangeStartRange = range.cloneRange();
26881         rangeStartRange.collapse(true);
26882     
26883         var rangeEndRange = range.cloneRange();
26884         rangeEndRange.collapse(false);
26885     
26886         var nodeStartRange = nodeRange.cloneRange();
26887         nodeStartRange.collapse(true);
26888     
26889         var nodeEndRange = nodeRange.cloneRange();
26890         nodeEndRange.collapse(false);
26891     
26892         return rangeStartRange.compareBoundaryPoints(
26893                  Range.START_TO_START, nodeEndRange) == -1 &&
26894                rangeEndRange.compareBoundaryPoints(
26895                  Range.START_TO_START, nodeStartRange) == 1;
26896         
26897          
26898     },
26899     rangeCompareNode : function(range, node)
26900     {
26901         var nodeRange = node.ownerDocument.createRange();
26902         try {
26903             nodeRange.selectNode(node);
26904         } catch (e) {
26905             nodeRange.selectNodeContents(node);
26906         }
26907         
26908         
26909         range.collapse(true);
26910     
26911         nodeRange.collapse(true);
26912      
26913         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26914         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26915          
26916         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26917         
26918         var nodeIsBefore   =  ss == 1;
26919         var nodeIsAfter    = ee == -1;
26920         
26921         if (nodeIsBefore && nodeIsAfter) {
26922             return 0; // outer
26923         }
26924         if (!nodeIsBefore && nodeIsAfter) {
26925             return 1; //right trailed.
26926         }
26927         
26928         if (nodeIsBefore && !nodeIsAfter) {
26929             return 2;  // left trailed.
26930         }
26931         // fully contined.
26932         return 3;
26933     },
26934
26935     // private? - in a new class?
26936     cleanUpPaste :  function()
26937     {
26938         // cleans up the whole document..
26939         Roo.log('cleanuppaste');
26940         
26941         this.cleanUpChildren(this.doc.body);
26942         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26943         if (clean != this.doc.body.innerHTML) {
26944             this.doc.body.innerHTML = clean;
26945         }
26946         
26947     },
26948     
26949     cleanWordChars : function(input) {// change the chars to hex code
26950         var he = Roo.HtmlEditorCore;
26951         
26952         var output = input;
26953         Roo.each(he.swapCodes, function(sw) { 
26954             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26955             
26956             output = output.replace(swapper, sw[1]);
26957         });
26958         
26959         return output;
26960     },
26961     
26962     
26963     cleanUpChildren : function (n)
26964     {
26965         if (!n.childNodes.length) {
26966             return;
26967         }
26968         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26969            this.cleanUpChild(n.childNodes[i]);
26970         }
26971     },
26972     
26973     
26974         
26975     
26976     cleanUpChild : function (node)
26977     {
26978         var ed = this;
26979         //console.log(node);
26980         if (node.nodeName == "#text") {
26981             // clean up silly Windows -- stuff?
26982             return; 
26983         }
26984         if (node.nodeName == "#comment") {
26985             if (!this.allowComments) {
26986                 node.parentNode.removeChild(node);
26987             }
26988             // clean up silly Windows -- stuff?
26989             return; 
26990         }
26991         var lcname = node.tagName.toLowerCase();
26992         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26993         // whitelist of tags..
26994         
26995         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26996             // remove node.
26997             node.parentNode.removeChild(node);
26998             return;
26999             
27000         }
27001         
27002         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27003         
27004         // spans with no attributes - just remove them..
27005         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27006             remove_keep_children = true;
27007         }
27008         
27009         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27010         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27011         
27012         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27013         //    remove_keep_children = true;
27014         //}
27015         
27016         if (remove_keep_children) {
27017             this.cleanUpChildren(node);
27018             // inserts everything just before this node...
27019             while (node.childNodes.length) {
27020                 var cn = node.childNodes[0];
27021                 node.removeChild(cn);
27022                 node.parentNode.insertBefore(cn, node);
27023             }
27024             node.parentNode.removeChild(node);
27025             return;
27026         }
27027         
27028         if (!node.attributes || !node.attributes.length) {
27029             
27030           
27031             
27032             
27033             this.cleanUpChildren(node);
27034             return;
27035         }
27036         
27037         function cleanAttr(n,v)
27038         {
27039             
27040             if (v.match(/^\./) || v.match(/^\//)) {
27041                 return;
27042             }
27043             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27044                 return;
27045             }
27046             if (v.match(/^#/)) {
27047                 return;
27048             }
27049             if (v.match(/^\{/)) { // allow template editing.
27050                 return;
27051             }
27052 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27053             node.removeAttribute(n);
27054             
27055         }
27056         
27057         var cwhite = this.cwhite;
27058         var cblack = this.cblack;
27059             
27060         function cleanStyle(n,v)
27061         {
27062             if (v.match(/expression/)) { //XSS?? should we even bother..
27063                 node.removeAttribute(n);
27064                 return;
27065             }
27066             
27067             var parts = v.split(/;/);
27068             var clean = [];
27069             
27070             Roo.each(parts, function(p) {
27071                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27072                 if (!p.length) {
27073                     return true;
27074                 }
27075                 var l = p.split(':').shift().replace(/\s+/g,'');
27076                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27077                 
27078                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27079 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27080                     //node.removeAttribute(n);
27081                     return true;
27082                 }
27083                 //Roo.log()
27084                 // only allow 'c whitelisted system attributes'
27085                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27086 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27087                     //node.removeAttribute(n);
27088                     return true;
27089                 }
27090                 
27091                 
27092                  
27093                 
27094                 clean.push(p);
27095                 return true;
27096             });
27097             if (clean.length) { 
27098                 node.setAttribute(n, clean.join(';'));
27099             } else {
27100                 node.removeAttribute(n);
27101             }
27102             
27103         }
27104         
27105         
27106         for (var i = node.attributes.length-1; i > -1 ; i--) {
27107             var a = node.attributes[i];
27108             //console.log(a);
27109             
27110             if (a.name.toLowerCase().substr(0,2)=='on')  {
27111                 node.removeAttribute(a.name);
27112                 continue;
27113             }
27114             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27115                 node.removeAttribute(a.name);
27116                 continue;
27117             }
27118             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27119                 cleanAttr(a.name,a.value); // fixme..
27120                 continue;
27121             }
27122             if (a.name == 'style') {
27123                 cleanStyle(a.name,a.value);
27124                 continue;
27125             }
27126             /// clean up MS crap..
27127             // tecnically this should be a list of valid class'es..
27128             
27129             
27130             if (a.name == 'class') {
27131                 if (a.value.match(/^Mso/)) {
27132                     node.removeAttribute('class');
27133                 }
27134                 
27135                 if (a.value.match(/^body$/)) {
27136                     node.removeAttribute('class');
27137                 }
27138                 continue;
27139             }
27140             
27141             // style cleanup!?
27142             // class cleanup?
27143             
27144         }
27145         
27146         
27147         this.cleanUpChildren(node);
27148         
27149         
27150     },
27151     
27152     /**
27153      * Clean up MS wordisms...
27154      */
27155     cleanWord : function(node)
27156     {
27157         if (!node) {
27158             this.cleanWord(this.doc.body);
27159             return;
27160         }
27161         
27162         if(
27163                 node.nodeName == 'SPAN' &&
27164                 !node.hasAttributes() &&
27165                 node.childNodes.length == 1 &&
27166                 node.firstChild.nodeName == "#text"  
27167         ) {
27168             var textNode = node.firstChild;
27169             node.removeChild(textNode);
27170             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27171                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27172             }
27173             node.parentNode.insertBefore(textNode, node);
27174             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27175                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27176             }
27177             node.parentNode.removeChild(node);
27178         }
27179         
27180         if (node.nodeName == "#text") {
27181             // clean up silly Windows -- stuff?
27182             return; 
27183         }
27184         if (node.nodeName == "#comment") {
27185             node.parentNode.removeChild(node);
27186             // clean up silly Windows -- stuff?
27187             return; 
27188         }
27189         
27190         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27191             node.parentNode.removeChild(node);
27192             return;
27193         }
27194         //Roo.log(node.tagName);
27195         // remove - but keep children..
27196         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27197             //Roo.log('-- removed');
27198             while (node.childNodes.length) {
27199                 var cn = node.childNodes[0];
27200                 node.removeChild(cn);
27201                 node.parentNode.insertBefore(cn, node);
27202                 // move node to parent - and clean it..
27203                 this.cleanWord(cn);
27204             }
27205             node.parentNode.removeChild(node);
27206             /// no need to iterate chidlren = it's got none..
27207             //this.iterateChildren(node, this.cleanWord);
27208             return;
27209         }
27210         // clean styles
27211         if (node.className.length) {
27212             
27213             var cn = node.className.split(/\W+/);
27214             var cna = [];
27215             Roo.each(cn, function(cls) {
27216                 if (cls.match(/Mso[a-zA-Z]+/)) {
27217                     return;
27218                 }
27219                 cna.push(cls);
27220             });
27221             node.className = cna.length ? cna.join(' ') : '';
27222             if (!cna.length) {
27223                 node.removeAttribute("class");
27224             }
27225         }
27226         
27227         if (node.hasAttribute("lang")) {
27228             node.removeAttribute("lang");
27229         }
27230         
27231         if (node.hasAttribute("style")) {
27232             
27233             var styles = node.getAttribute("style").split(";");
27234             var nstyle = [];
27235             Roo.each(styles, function(s) {
27236                 if (!s.match(/:/)) {
27237                     return;
27238                 }
27239                 var kv = s.split(":");
27240                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27241                     return;
27242                 }
27243                 // what ever is left... we allow.
27244                 nstyle.push(s);
27245             });
27246             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27247             if (!nstyle.length) {
27248                 node.removeAttribute('style');
27249             }
27250         }
27251         this.iterateChildren(node, this.cleanWord);
27252         
27253         
27254         
27255     },
27256     /**
27257      * iterateChildren of a Node, calling fn each time, using this as the scole..
27258      * @param {DomNode} node node to iterate children of.
27259      * @param {Function} fn method of this class to call on each item.
27260      */
27261     iterateChildren : function(node, fn)
27262     {
27263         if (!node.childNodes.length) {
27264                 return;
27265         }
27266         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27267            fn.call(this, node.childNodes[i])
27268         }
27269     },
27270     
27271     
27272     /**
27273      * cleanTableWidths.
27274      *
27275      * Quite often pasting from word etc.. results in tables with column and widths.
27276      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27277      *
27278      */
27279     cleanTableWidths : function(node)
27280     {
27281          
27282          
27283         if (!node) {
27284             this.cleanTableWidths(this.doc.body);
27285             return;
27286         }
27287         
27288         // ignore list...
27289         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27290             return; 
27291         }
27292         Roo.log(node.tagName);
27293         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27294             this.iterateChildren(node, this.cleanTableWidths);
27295             return;
27296         }
27297         if (node.hasAttribute('width')) {
27298             node.removeAttribute('width');
27299         }
27300         
27301          
27302         if (node.hasAttribute("style")) {
27303             // pretty basic...
27304             
27305             var styles = node.getAttribute("style").split(";");
27306             var nstyle = [];
27307             Roo.each(styles, function(s) {
27308                 if (!s.match(/:/)) {
27309                     return;
27310                 }
27311                 var kv = s.split(":");
27312                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27313                     return;
27314                 }
27315                 // what ever is left... we allow.
27316                 nstyle.push(s);
27317             });
27318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27319             if (!nstyle.length) {
27320                 node.removeAttribute('style');
27321             }
27322         }
27323         
27324         this.iterateChildren(node, this.cleanTableWidths);
27325         
27326         
27327     },
27328     
27329     
27330     
27331     
27332     domToHTML : function(currentElement, depth, nopadtext) {
27333         
27334         depth = depth || 0;
27335         nopadtext = nopadtext || false;
27336     
27337         if (!currentElement) {
27338             return this.domToHTML(this.doc.body);
27339         }
27340         
27341         //Roo.log(currentElement);
27342         var j;
27343         var allText = false;
27344         var nodeName = currentElement.nodeName;
27345         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27346         
27347         if  (nodeName == '#text') {
27348             
27349             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27350         }
27351         
27352         
27353         var ret = '';
27354         if (nodeName != 'BODY') {
27355              
27356             var i = 0;
27357             // Prints the node tagName, such as <A>, <IMG>, etc
27358             if (tagName) {
27359                 var attr = [];
27360                 for(i = 0; i < currentElement.attributes.length;i++) {
27361                     // quoting?
27362                     var aname = currentElement.attributes.item(i).name;
27363                     if (!currentElement.attributes.item(i).value.length) {
27364                         continue;
27365                     }
27366                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27367                 }
27368                 
27369                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27370             } 
27371             else {
27372                 
27373                 // eack
27374             }
27375         } else {
27376             tagName = false;
27377         }
27378         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27379             return ret;
27380         }
27381         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27382             nopadtext = true;
27383         }
27384         
27385         
27386         // Traverse the tree
27387         i = 0;
27388         var currentElementChild = currentElement.childNodes.item(i);
27389         var allText = true;
27390         var innerHTML  = '';
27391         lastnode = '';
27392         while (currentElementChild) {
27393             // Formatting code (indent the tree so it looks nice on the screen)
27394             var nopad = nopadtext;
27395             if (lastnode == 'SPAN') {
27396                 nopad  = true;
27397             }
27398             // text
27399             if  (currentElementChild.nodeName == '#text') {
27400                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27401                 toadd = nopadtext ? toadd : toadd.trim();
27402                 if (!nopad && toadd.length > 80) {
27403                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27404                 }
27405                 innerHTML  += toadd;
27406                 
27407                 i++;
27408                 currentElementChild = currentElement.childNodes.item(i);
27409                 lastNode = '';
27410                 continue;
27411             }
27412             allText = false;
27413             
27414             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27415                 
27416             // Recursively traverse the tree structure of the child node
27417             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27418             lastnode = currentElementChild.nodeName;
27419             i++;
27420             currentElementChild=currentElement.childNodes.item(i);
27421         }
27422         
27423         ret += innerHTML;
27424         
27425         if (!allText) {
27426                 // The remaining code is mostly for formatting the tree
27427             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27428         }
27429         
27430         
27431         if (tagName) {
27432             ret+= "</"+tagName+">";
27433         }
27434         return ret;
27435         
27436     },
27437         
27438     applyBlacklists : function()
27439     {
27440         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27441         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27442         
27443         this.white = [];
27444         this.black = [];
27445         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27446             if (b.indexOf(tag) > -1) {
27447                 return;
27448             }
27449             this.white.push(tag);
27450             
27451         }, this);
27452         
27453         Roo.each(w, function(tag) {
27454             if (b.indexOf(tag) > -1) {
27455                 return;
27456             }
27457             if (this.white.indexOf(tag) > -1) {
27458                 return;
27459             }
27460             this.white.push(tag);
27461             
27462         }, this);
27463         
27464         
27465         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27466             if (w.indexOf(tag) > -1) {
27467                 return;
27468             }
27469             this.black.push(tag);
27470             
27471         }, this);
27472         
27473         Roo.each(b, function(tag) {
27474             if (w.indexOf(tag) > -1) {
27475                 return;
27476             }
27477             if (this.black.indexOf(tag) > -1) {
27478                 return;
27479             }
27480             this.black.push(tag);
27481             
27482         }, this);
27483         
27484         
27485         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27486         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27487         
27488         this.cwhite = [];
27489         this.cblack = [];
27490         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27491             if (b.indexOf(tag) > -1) {
27492                 return;
27493             }
27494             this.cwhite.push(tag);
27495             
27496         }, this);
27497         
27498         Roo.each(w, function(tag) {
27499             if (b.indexOf(tag) > -1) {
27500                 return;
27501             }
27502             if (this.cwhite.indexOf(tag) > -1) {
27503                 return;
27504             }
27505             this.cwhite.push(tag);
27506             
27507         }, this);
27508         
27509         
27510         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27511             if (w.indexOf(tag) > -1) {
27512                 return;
27513             }
27514             this.cblack.push(tag);
27515             
27516         }, this);
27517         
27518         Roo.each(b, function(tag) {
27519             if (w.indexOf(tag) > -1) {
27520                 return;
27521             }
27522             if (this.cblack.indexOf(tag) > -1) {
27523                 return;
27524             }
27525             this.cblack.push(tag);
27526             
27527         }, this);
27528     },
27529     
27530     setStylesheets : function(stylesheets)
27531     {
27532         if(typeof(stylesheets) == 'string'){
27533             Roo.get(this.iframe.contentDocument.head).createChild({
27534                 tag : 'link',
27535                 rel : 'stylesheet',
27536                 type : 'text/css',
27537                 href : stylesheets
27538             });
27539             
27540             return;
27541         }
27542         var _this = this;
27543      
27544         Roo.each(stylesheets, function(s) {
27545             if(!s.length){
27546                 return;
27547             }
27548             
27549             Roo.get(_this.iframe.contentDocument.head).createChild({
27550                 tag : 'link',
27551                 rel : 'stylesheet',
27552                 type : 'text/css',
27553                 href : s
27554             });
27555         });
27556
27557         
27558     },
27559     
27560     removeStylesheets : function()
27561     {
27562         var _this = this;
27563         
27564         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27565             s.remove();
27566         });
27567     },
27568     
27569     setStyle : function(style)
27570     {
27571         Roo.get(this.iframe.contentDocument.head).createChild({
27572             tag : 'style',
27573             type : 'text/css',
27574             html : style
27575         });
27576
27577         return;
27578     }
27579     
27580     // hide stuff that is not compatible
27581     /**
27582      * @event blur
27583      * @hide
27584      */
27585     /**
27586      * @event change
27587      * @hide
27588      */
27589     /**
27590      * @event focus
27591      * @hide
27592      */
27593     /**
27594      * @event specialkey
27595      * @hide
27596      */
27597     /**
27598      * @cfg {String} fieldClass @hide
27599      */
27600     /**
27601      * @cfg {String} focusClass @hide
27602      */
27603     /**
27604      * @cfg {String} autoCreate @hide
27605      */
27606     /**
27607      * @cfg {String} inputType @hide
27608      */
27609     /**
27610      * @cfg {String} invalidClass @hide
27611      */
27612     /**
27613      * @cfg {String} invalidText @hide
27614      */
27615     /**
27616      * @cfg {String} msgFx @hide
27617      */
27618     /**
27619      * @cfg {String} validateOnBlur @hide
27620      */
27621 });
27622
27623 Roo.HtmlEditorCore.white = [
27624         'area', 'br', 'img', 'input', 'hr', 'wbr',
27625         
27626        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27627        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27628        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27629        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27630        'table',   'ul',         'xmp', 
27631        
27632        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27633       'thead',   'tr', 
27634      
27635       'dir', 'menu', 'ol', 'ul', 'dl',
27636        
27637       'embed',  'object'
27638 ];
27639
27640
27641 Roo.HtmlEditorCore.black = [
27642     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27643         'applet', // 
27644         'base',   'basefont', 'bgsound', 'blink',  'body', 
27645         'frame',  'frameset', 'head',    'html',   'ilayer', 
27646         'iframe', 'layer',  'link',     'meta',    'object',   
27647         'script', 'style' ,'title',  'xml' // clean later..
27648 ];
27649 Roo.HtmlEditorCore.clean = [
27650     'script', 'style', 'title', 'xml'
27651 ];
27652 Roo.HtmlEditorCore.remove = [
27653     'font'
27654 ];
27655 // attributes..
27656
27657 Roo.HtmlEditorCore.ablack = [
27658     'on'
27659 ];
27660     
27661 Roo.HtmlEditorCore.aclean = [ 
27662     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27663 ];
27664
27665 // protocols..
27666 Roo.HtmlEditorCore.pwhite= [
27667         'http',  'https',  'mailto'
27668 ];
27669
27670 // white listed style attributes.
27671 Roo.HtmlEditorCore.cwhite= [
27672       //  'text-align', /// default is to allow most things..
27673       
27674          
27675 //        'font-size'//??
27676 ];
27677
27678 // black listed style attributes.
27679 Roo.HtmlEditorCore.cblack= [
27680       //  'font-size' -- this can be set by the project 
27681 ];
27682
27683
27684 Roo.HtmlEditorCore.swapCodes   =[ 
27685     [    8211, "&#8211;" ], 
27686     [    8212, "&#8212;" ], 
27687     [    8216,  "'" ],  
27688     [    8217, "'" ],  
27689     [    8220, '"' ],  
27690     [    8221, '"' ],  
27691     [    8226, "*" ],  
27692     [    8230, "..." ]
27693 ]; 
27694
27695     /*
27696  * - LGPL
27697  *
27698  * HtmlEditor
27699  * 
27700  */
27701
27702 /**
27703  * @class Roo.bootstrap.form.HtmlEditor
27704  * @extends Roo.bootstrap.form.TextArea
27705  * Bootstrap HtmlEditor class
27706
27707  * @constructor
27708  * Create a new HtmlEditor
27709  * @param {Object} config The config object
27710  */
27711
27712 Roo.bootstrap.form.HtmlEditor = function(config){
27713     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27714     if (!this.toolbars) {
27715         this.toolbars = [];
27716     }
27717     
27718     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27719     this.addEvents({
27720             /**
27721              * @event initialize
27722              * Fires when the editor is fully initialized (including the iframe)
27723              * @param {HtmlEditor} this
27724              */
27725             initialize: true,
27726             /**
27727              * @event activate
27728              * Fires when the editor is first receives the focus. Any insertion must wait
27729              * until after this event.
27730              * @param {HtmlEditor} this
27731              */
27732             activate: true,
27733              /**
27734              * @event beforesync
27735              * Fires before the textarea is updated with content from the editor iframe. Return false
27736              * to cancel the sync.
27737              * @param {HtmlEditor} this
27738              * @param {String} html
27739              */
27740             beforesync: true,
27741              /**
27742              * @event beforepush
27743              * Fires before the iframe editor is updated with content from the textarea. Return false
27744              * to cancel the push.
27745              * @param {HtmlEditor} this
27746              * @param {String} html
27747              */
27748             beforepush: true,
27749              /**
27750              * @event sync
27751              * Fires when the textarea is updated with content from the editor iframe.
27752              * @param {HtmlEditor} this
27753              * @param {String} html
27754              */
27755             sync: true,
27756              /**
27757              * @event push
27758              * Fires when the iframe editor is updated with content from the textarea.
27759              * @param {HtmlEditor} this
27760              * @param {String} html
27761              */
27762             push: true,
27763              /**
27764              * @event editmodechange
27765              * Fires when the editor switches edit modes
27766              * @param {HtmlEditor} this
27767              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27768              */
27769             editmodechange: true,
27770             /**
27771              * @event editorevent
27772              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27773              * @param {HtmlEditor} this
27774              */
27775             editorevent: true,
27776             /**
27777              * @event firstfocus
27778              * Fires when on first focus - needed by toolbars..
27779              * @param {HtmlEditor} this
27780              */
27781             firstfocus: true,
27782             /**
27783              * @event autosave
27784              * Auto save the htmlEditor value as a file into Events
27785              * @param {HtmlEditor} this
27786              */
27787             autosave: true,
27788             /**
27789              * @event savedpreview
27790              * preview the saved version of htmlEditor
27791              * @param {HtmlEditor} this
27792              */
27793             savedpreview: true
27794         });
27795 };
27796
27797
27798 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27799     
27800     
27801       /**
27802      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27803      */
27804     toolbars : false,
27805     
27806      /**
27807     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27808     */
27809     btns : [],
27810    
27811      /**
27812      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27813      *                        Roo.resizable.
27814      */
27815     resizable : false,
27816      /**
27817      * @cfg {Number} height (in pixels)
27818      */   
27819     height: 300,
27820    /**
27821      * @cfg {Number} width (in pixels)
27822      */   
27823     width: false,
27824     
27825     /**
27826      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27827      * 
27828      */
27829     stylesheets: false,
27830     
27831     // id of frame..
27832     frameId: false,
27833     
27834     // private properties
27835     validationEvent : false,
27836     deferHeight: true,
27837     initialized : false,
27838     activated : false,
27839     
27840     onFocus : Roo.emptyFn,
27841     iframePad:3,
27842     hideMode:'offsets',
27843     
27844     tbContainer : false,
27845     
27846     bodyCls : '',
27847     
27848     toolbarContainer :function() {
27849         return this.wrap.select('.x-html-editor-tb',true).first();
27850     },
27851
27852     /**
27853      * Protected method that will not generally be called directly. It
27854      * is called when the editor creates its toolbar. Override this method if you need to
27855      * add custom toolbar buttons.
27856      * @param {HtmlEditor} editor
27857      */
27858     createToolbar : function(){
27859         Roo.log('renewing');
27860         Roo.log("create toolbars");
27861         
27862         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27863         this.toolbars[0].render(this.toolbarContainer());
27864         
27865         return;
27866         
27867 //        if (!editor.toolbars || !editor.toolbars.length) {
27868 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27869 //        }
27870 //        
27871 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27872 //            editor.toolbars[i] = Roo.factory(
27873 //                    typeof(editor.toolbars[i]) == 'string' ?
27874 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27875 //                Roo.bootstrap.form.HtmlEditor);
27876 //            editor.toolbars[i].init(editor);
27877 //        }
27878     },
27879
27880      
27881     // private
27882     onRender : function(ct, position)
27883     {
27884        // Roo.log("Call onRender: " + this.xtype);
27885         var _t = this;
27886         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27887       
27888         this.wrap = this.inputEl().wrap({
27889             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27890         });
27891         
27892         this.editorcore.onRender(ct, position);
27893          
27894         if (this.resizable) {
27895             this.resizeEl = new Roo.Resizable(this.wrap, {
27896                 pinned : true,
27897                 wrap: true,
27898                 dynamic : true,
27899                 minHeight : this.height,
27900                 height: this.height,
27901                 handles : this.resizable,
27902                 width: this.width,
27903                 listeners : {
27904                     resize : function(r, w, h) {
27905                         _t.onResize(w,h); // -something
27906                     }
27907                 }
27908             });
27909             
27910         }
27911         this.createToolbar(this);
27912        
27913         
27914         if(!this.width && this.resizable){
27915             this.setSize(this.wrap.getSize());
27916         }
27917         if (this.resizeEl) {
27918             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27919             // should trigger onReize..
27920         }
27921         
27922     },
27923
27924     // private
27925     onResize : function(w, h)
27926     {
27927         Roo.log('resize: ' +w + ',' + h );
27928         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27929         var ew = false;
27930         var eh = false;
27931         
27932         if(this.inputEl() ){
27933             if(typeof w == 'number'){
27934                 var aw = w - this.wrap.getFrameWidth('lr');
27935                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27936                 ew = aw;
27937             }
27938             if(typeof h == 'number'){
27939                  var tbh = -11;  // fixme it needs to tool bar size!
27940                 for (var i =0; i < this.toolbars.length;i++) {
27941                     // fixme - ask toolbars for heights?
27942                     tbh += this.toolbars[i].el.getHeight();
27943                     //if (this.toolbars[i].footer) {
27944                     //    tbh += this.toolbars[i].footer.el.getHeight();
27945                     //}
27946                 }
27947               
27948                 
27949                 
27950                 
27951                 
27952                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27953                 ah -= 5; // knock a few pixes off for look..
27954                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27955                 var eh = ah;
27956             }
27957         }
27958         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27959         this.editorcore.onResize(ew,eh);
27960         
27961     },
27962
27963     /**
27964      * Toggles the editor between standard and source edit mode.
27965      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27966      */
27967     toggleSourceEdit : function(sourceEditMode)
27968     {
27969         this.editorcore.toggleSourceEdit(sourceEditMode);
27970         
27971         if(this.editorcore.sourceEditMode){
27972             Roo.log('editor - showing textarea');
27973             
27974 //            Roo.log('in');
27975 //            Roo.log(this.syncValue());
27976             this.syncValue();
27977             this.inputEl().removeClass(['hide', 'x-hidden']);
27978             this.inputEl().dom.removeAttribute('tabIndex');
27979             this.inputEl().focus();
27980         }else{
27981             Roo.log('editor - hiding textarea');
27982 //            Roo.log('out')
27983 //            Roo.log(this.pushValue()); 
27984             this.pushValue();
27985             
27986             this.inputEl().addClass(['hide', 'x-hidden']);
27987             this.inputEl().dom.setAttribute('tabIndex', -1);
27988             //this.deferFocus();
27989         }
27990          
27991         if(this.resizable){
27992             this.setSize(this.wrap.getSize());
27993         }
27994         
27995         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27996     },
27997  
27998     // private (for BoxComponent)
27999     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28000
28001     // private (for BoxComponent)
28002     getResizeEl : function(){
28003         return this.wrap;
28004     },
28005
28006     // private (for BoxComponent)
28007     getPositionEl : function(){
28008         return this.wrap;
28009     },
28010
28011     // private
28012     initEvents : function(){
28013         this.originalValue = this.getValue();
28014     },
28015
28016 //    /**
28017 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28018 //     * @method
28019 //     */
28020 //    markInvalid : Roo.emptyFn,
28021 //    /**
28022 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28023 //     * @method
28024 //     */
28025 //    clearInvalid : Roo.emptyFn,
28026
28027     setValue : function(v){
28028         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28029         this.editorcore.pushValue();
28030     },
28031
28032      
28033     // private
28034     deferFocus : function(){
28035         this.focus.defer(10, this);
28036     },
28037
28038     // doc'ed in Field
28039     focus : function(){
28040         this.editorcore.focus();
28041         
28042     },
28043       
28044
28045     // private
28046     onDestroy : function(){
28047         
28048         
28049         
28050         if(this.rendered){
28051             
28052             for (var i =0; i < this.toolbars.length;i++) {
28053                 // fixme - ask toolbars for heights?
28054                 this.toolbars[i].onDestroy();
28055             }
28056             
28057             this.wrap.dom.innerHTML = '';
28058             this.wrap.remove();
28059         }
28060     },
28061
28062     // private
28063     onFirstFocus : function(){
28064         //Roo.log("onFirstFocus");
28065         this.editorcore.onFirstFocus();
28066          for (var i =0; i < this.toolbars.length;i++) {
28067             this.toolbars[i].onFirstFocus();
28068         }
28069         
28070     },
28071     
28072     // private
28073     syncValue : function()
28074     {   
28075         this.editorcore.syncValue();
28076     },
28077     
28078     pushValue : function()
28079     {   
28080         this.editorcore.pushValue();
28081     }
28082      
28083     
28084     // hide stuff that is not compatible
28085     /**
28086      * @event blur
28087      * @hide
28088      */
28089     /**
28090      * @event change
28091      * @hide
28092      */
28093     /**
28094      * @event focus
28095      * @hide
28096      */
28097     /**
28098      * @event specialkey
28099      * @hide
28100      */
28101     /**
28102      * @cfg {String} fieldClass @hide
28103      */
28104     /**
28105      * @cfg {String} focusClass @hide
28106      */
28107     /**
28108      * @cfg {String} autoCreate @hide
28109      */
28110     /**
28111      * @cfg {String} inputType @hide
28112      */
28113      
28114     /**
28115      * @cfg {String} invalidText @hide
28116      */
28117     /**
28118      * @cfg {String} msgFx @hide
28119      */
28120     /**
28121      * @cfg {String} validateOnBlur @hide
28122      */
28123 });
28124  
28125     
28126    
28127    
28128    
28129       
28130 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28131 /**
28132  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28133  * @parent Roo.bootstrap.form.HtmlEditor
28134  * @extends Roo.bootstrap.nav.Simplebar
28135  * Basic Toolbar
28136  * 
28137  * @example
28138  * Usage:
28139  *
28140  new Roo.bootstrap.form.HtmlEditor({
28141     ....
28142     toolbars : [
28143         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28144             disable : { fonts: 1 , format: 1, ..., ... , ...],
28145             btns : [ .... ]
28146         })
28147     }
28148      
28149  * 
28150  * @cfg {Object} disable List of elements to disable..
28151  * @cfg {Array} btns List of additional buttons.
28152  * 
28153  * 
28154  * NEEDS Extra CSS? 
28155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28156  */
28157  
28158 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28159 {
28160     
28161     Roo.apply(this, config);
28162     
28163     // default disabled, based on 'good practice'..
28164     this.disable = this.disable || {};
28165     Roo.applyIf(this.disable, {
28166         fontSize : true,
28167         colors : true,
28168         specialElements : true
28169     });
28170     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28171     
28172     this.editor = config.editor;
28173     this.editorcore = config.editor.editorcore;
28174     
28175     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28176     
28177     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28178     // dont call parent... till later.
28179 }
28180 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28181      
28182     bar : true,
28183     
28184     editor : false,
28185     editorcore : false,
28186     
28187     
28188     formats : [
28189         "p" ,  
28190         "h1","h2","h3","h4","h5","h6", 
28191         "pre", "code", 
28192         "abbr", "acronym", "address", "cite", "samp", "var",
28193         'div','span'
28194     ],
28195     
28196     onRender : function(ct, position)
28197     {
28198        // Roo.log("Call onRender: " + this.xtype);
28199         
28200        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28201        Roo.log(this.el);
28202        this.el.dom.style.marginBottom = '0';
28203        var _this = this;
28204        var editorcore = this.editorcore;
28205        var editor= this.editor;
28206        
28207        var children = [];
28208        var btn = function(id,cmd , toggle, handler, html){
28209        
28210             var  event = toggle ? 'toggle' : 'click';
28211        
28212             var a = {
28213                 size : 'sm',
28214                 xtype: 'Button',
28215                 xns: Roo.bootstrap,
28216                 //glyphicon : id,
28217                 fa: id,
28218                 cmd : id || cmd,
28219                 enableToggle:toggle !== false,
28220                 html : html || '',
28221                 pressed : toggle ? false : null,
28222                 listeners : {}
28223             };
28224             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28225                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28226             };
28227             children.push(a);
28228             return a;
28229        }
28230        
28231     //    var cb_box = function...
28232         
28233         var style = {
28234                 xtype: 'Button',
28235                 size : 'sm',
28236                 xns: Roo.bootstrap,
28237                 fa : 'font',
28238                 //html : 'submit'
28239                 menu : {
28240                     xtype: 'Menu',
28241                     xns: Roo.bootstrap,
28242                     items:  []
28243                 }
28244         };
28245         Roo.each(this.formats, function(f) {
28246             style.menu.items.push({
28247                 xtype :'MenuItem',
28248                 xns: Roo.bootstrap,
28249                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28250                 tagname : f,
28251                 listeners : {
28252                     click : function()
28253                     {
28254                         editorcore.insertTag(this.tagname);
28255                         editor.focus();
28256                     }
28257                 }
28258                 
28259             });
28260         });
28261         children.push(style);   
28262         
28263         btn('bold',false,true);
28264         btn('italic',false,true);
28265         btn('align-left', 'justifyleft',true);
28266         btn('align-center', 'justifycenter',true);
28267         btn('align-right' , 'justifyright',true);
28268         btn('link', false, false, function(btn) {
28269             //Roo.log("create link?");
28270             var url = prompt(this.createLinkText, this.defaultLinkValue);
28271             if(url && url != 'http:/'+'/'){
28272                 this.editorcore.relayCmd('createlink', url);
28273             }
28274         }),
28275         btn('list','insertunorderedlist',true);
28276         btn('pencil', false,true, function(btn){
28277                 Roo.log(this);
28278                 this.toggleSourceEdit(btn.pressed);
28279         });
28280         
28281         if (this.editor.btns.length > 0) {
28282             for (var i = 0; i<this.editor.btns.length; i++) {
28283                 children.push(this.editor.btns[i]);
28284             }
28285         }
28286         
28287         /*
28288         var cog = {
28289                 xtype: 'Button',
28290                 size : 'sm',
28291                 xns: Roo.bootstrap,
28292                 glyphicon : 'cog',
28293                 //html : 'submit'
28294                 menu : {
28295                     xtype: 'Menu',
28296                     xns: Roo.bootstrap,
28297                     items:  []
28298                 }
28299         };
28300         
28301         cog.menu.items.push({
28302             xtype :'MenuItem',
28303             xns: Roo.bootstrap,
28304             html : Clean styles,
28305             tagname : f,
28306             listeners : {
28307                 click : function()
28308                 {
28309                     editorcore.insertTag(this.tagname);
28310                     editor.focus();
28311                 }
28312             }
28313             
28314         });
28315        */
28316         
28317          
28318        this.xtype = 'NavSimplebar';
28319         
28320         for(var i=0;i< children.length;i++) {
28321             
28322             this.buttons.add(this.addxtypeChild(children[i]));
28323             
28324         }
28325         
28326         editor.on('editorevent', this.updateToolbar, this);
28327     },
28328     onBtnClick : function(id)
28329     {
28330        this.editorcore.relayCmd(id);
28331        this.editorcore.focus();
28332     },
28333     
28334     /**
28335      * Protected method that will not generally be called directly. It triggers
28336      * a toolbar update by reading the markup state of the current selection in the editor.
28337      */
28338     updateToolbar: function(){
28339
28340         if(!this.editorcore.activated){
28341             this.editor.onFirstFocus(); // is this neeed?
28342             return;
28343         }
28344
28345         var btns = this.buttons; 
28346         var doc = this.editorcore.doc;
28347         btns.get('bold').setActive(doc.queryCommandState('bold'));
28348         btns.get('italic').setActive(doc.queryCommandState('italic'));
28349         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28350         
28351         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28352         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28353         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28354         
28355         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28356         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28357          /*
28358         
28359         var ans = this.editorcore.getAllAncestors();
28360         if (this.formatCombo) {
28361             
28362             
28363             var store = this.formatCombo.store;
28364             this.formatCombo.setValue("");
28365             for (var i =0; i < ans.length;i++) {
28366                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28367                     // select it..
28368                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28369                     break;
28370                 }
28371             }
28372         }
28373         
28374         
28375         
28376         // hides menus... - so this cant be on a menu...
28377         Roo.bootstrap.MenuMgr.hideAll();
28378         */
28379         Roo.bootstrap.menu.Manager.hideAll();
28380         //this.editorsyncValue();
28381     },
28382     onFirstFocus: function() {
28383         this.buttons.each(function(item){
28384            item.enable();
28385         });
28386     },
28387     toggleSourceEdit : function(sourceEditMode){
28388         
28389           
28390         if(sourceEditMode){
28391             Roo.log("disabling buttons");
28392            this.buttons.each( function(item){
28393                 if(item.cmd != 'pencil'){
28394                     item.disable();
28395                 }
28396             });
28397           
28398         }else{
28399             Roo.log("enabling buttons");
28400             if(this.editorcore.initialized){
28401                 this.buttons.each( function(item){
28402                     item.enable();
28403                 });
28404             }
28405             
28406         }
28407         Roo.log("calling toggole on editor");
28408         // tell the editor that it's been pressed..
28409         this.editor.toggleSourceEdit(sourceEditMode);
28410        
28411     }
28412 });
28413
28414
28415
28416
28417  
28418 /*
28419  * - LGPL
28420  */
28421
28422 /**
28423  * @class Roo.bootstrap.form.Markdown
28424  * @extends Roo.bootstrap.form.TextArea
28425  * Bootstrap Showdown editable area
28426  * @cfg {string} content
28427  * 
28428  * @constructor
28429  * Create a new Showdown
28430  */
28431
28432 Roo.bootstrap.form.Markdown = function(config){
28433     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28434    
28435 };
28436
28437 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28438     
28439     editing :false,
28440     
28441     initEvents : function()
28442     {
28443         
28444         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28445         this.markdownEl = this.el.createChild({
28446             cls : 'roo-markdown-area'
28447         });
28448         this.inputEl().addClass('d-none');
28449         if (this.getValue() == '') {
28450             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28451             
28452         } else {
28453             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28454         }
28455         this.markdownEl.on('click', this.toggleTextEdit, this);
28456         this.on('blur', this.toggleTextEdit, this);
28457         this.on('specialkey', this.resizeTextArea, this);
28458     },
28459     
28460     toggleTextEdit : function()
28461     {
28462         var sh = this.markdownEl.getHeight();
28463         this.inputEl().addClass('d-none');
28464         this.markdownEl.addClass('d-none');
28465         if (!this.editing) {
28466             // show editor?
28467             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28468             this.inputEl().removeClass('d-none');
28469             this.inputEl().focus();
28470             this.editing = true;
28471             return;
28472         }
28473         // show showdown...
28474         this.updateMarkdown();
28475         this.markdownEl.removeClass('d-none');
28476         this.editing = false;
28477         return;
28478     },
28479     updateMarkdown : function()
28480     {
28481         if (this.getValue() == '') {
28482             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28483             return;
28484         }
28485  
28486         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28487     },
28488     
28489     resizeTextArea: function () {
28490         
28491         var sh = 100;
28492         Roo.log([sh, this.getValue().split("\n").length * 30]);
28493         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28494     },
28495     setValue : function(val)
28496     {
28497         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28498         if (!this.editing) {
28499             this.updateMarkdown();
28500         }
28501         
28502     },
28503     focus : function()
28504     {
28505         if (!this.editing) {
28506             this.toggleTextEdit();
28507         }
28508         
28509     }
28510
28511
28512 });/*
28513  * Based on:
28514  * Ext JS Library 1.1.1
28515  * Copyright(c) 2006-2007, Ext JS, LLC.
28516  *
28517  * Originally Released Under LGPL - original licence link has changed is not relivant.
28518  *
28519  * Fork - LGPL
28520  * <script type="text/javascript">
28521  */
28522  
28523 /**
28524  * @class Roo.bootstrap.PagingToolbar
28525  * @extends Roo.bootstrap.nav.Simplebar
28526  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28527  * @constructor
28528  * Create a new PagingToolbar
28529  * @param {Object} config The config object
28530  * @param {Roo.data.Store} store
28531  */
28532 Roo.bootstrap.PagingToolbar = function(config)
28533 {
28534     // old args format still supported... - xtype is prefered..
28535         // created from xtype...
28536     
28537     this.ds = config.dataSource;
28538     
28539     if (config.store && !this.ds) {
28540         this.store= Roo.factory(config.store, Roo.data);
28541         this.ds = this.store;
28542         this.ds.xmodule = this.xmodule || false;
28543     }
28544     
28545     this.toolbarItems = [];
28546     if (config.items) {
28547         this.toolbarItems = config.items;
28548     }
28549     
28550     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28551     
28552     this.cursor = 0;
28553     
28554     if (this.ds) { 
28555         this.bind(this.ds);
28556     }
28557     
28558     if (Roo.bootstrap.version == 4) {
28559         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28560     } else {
28561         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28562     }
28563     
28564 };
28565
28566 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28567     /**
28568      * @cfg {Roo.bootstrap.Button} buttons[]
28569      * Buttons for the toolbar
28570      */
28571      /**
28572      * @cfg {Roo.data.Store} store
28573      * The underlying data store providing the paged data
28574      */
28575     /**
28576      * @cfg {String/HTMLElement/Element} container
28577      * container The id or element that will contain the toolbar
28578      */
28579     /**
28580      * @cfg {Boolean} displayInfo
28581      * True to display the displayMsg (defaults to false)
28582      */
28583     /**
28584      * @cfg {Number} pageSize
28585      * The number of records to display per page (defaults to 20)
28586      */
28587     pageSize: 20,
28588     /**
28589      * @cfg {String} displayMsg
28590      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28591      */
28592     displayMsg : 'Displaying {0} - {1} of {2}',
28593     /**
28594      * @cfg {String} emptyMsg
28595      * The message to display when no records are found (defaults to "No data to display")
28596      */
28597     emptyMsg : 'No data to display',
28598     /**
28599      * Customizable piece of the default paging text (defaults to "Page")
28600      * @type String
28601      */
28602     beforePageText : "Page",
28603     /**
28604      * Customizable piece of the default paging text (defaults to "of %0")
28605      * @type String
28606      */
28607     afterPageText : "of {0}",
28608     /**
28609      * Customizable piece of the default paging text (defaults to "First Page")
28610      * @type String
28611      */
28612     firstText : "First Page",
28613     /**
28614      * Customizable piece of the default paging text (defaults to "Previous Page")
28615      * @type String
28616      */
28617     prevText : "Previous Page",
28618     /**
28619      * Customizable piece of the default paging text (defaults to "Next Page")
28620      * @type String
28621      */
28622     nextText : "Next Page",
28623     /**
28624      * Customizable piece of the default paging text (defaults to "Last Page")
28625      * @type String
28626      */
28627     lastText : "Last Page",
28628     /**
28629      * Customizable piece of the default paging text (defaults to "Refresh")
28630      * @type String
28631      */
28632     refreshText : "Refresh",
28633
28634     buttons : false,
28635     // private
28636     onRender : function(ct, position) 
28637     {
28638         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28639         this.navgroup.parentId = this.id;
28640         this.navgroup.onRender(this.el, null);
28641         // add the buttons to the navgroup
28642         
28643         if(this.displayInfo){
28644             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28645             this.displayEl = this.el.select('.x-paging-info', true).first();
28646 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28647 //            this.displayEl = navel.el.select('span',true).first();
28648         }
28649         
28650         var _this = this;
28651         
28652         if(this.buttons){
28653             Roo.each(_this.buttons, function(e){ // this might need to use render????
28654                Roo.factory(e).render(_this.el);
28655             });
28656         }
28657             
28658         Roo.each(_this.toolbarItems, function(e) {
28659             _this.navgroup.addItem(e);
28660         });
28661         
28662         
28663         this.first = this.navgroup.addItem({
28664             tooltip: this.firstText,
28665             cls: "prev btn-outline-secondary",
28666             html : ' <i class="fa fa-step-backward"></i>',
28667             disabled: true,
28668             preventDefault: true,
28669             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28670         });
28671         
28672         this.prev =  this.navgroup.addItem({
28673             tooltip: this.prevText,
28674             cls: "prev btn-outline-secondary",
28675             html : ' <i class="fa fa-backward"></i>',
28676             disabled: true,
28677             preventDefault: true,
28678             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28679         });
28680     //this.addSeparator();
28681         
28682         
28683         var field = this.navgroup.addItem( {
28684             tagtype : 'span',
28685             cls : 'x-paging-position  btn-outline-secondary',
28686              disabled: true,
28687             html : this.beforePageText  +
28688                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28689                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28690          } ); //?? escaped?
28691         
28692         this.field = field.el.select('input', true).first();
28693         this.field.on("keydown", this.onPagingKeydown, this);
28694         this.field.on("focus", function(){this.dom.select();});
28695     
28696     
28697         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28698         //this.field.setHeight(18);
28699         //this.addSeparator();
28700         this.next = this.navgroup.addItem({
28701             tooltip: this.nextText,
28702             cls: "next btn-outline-secondary",
28703             html : ' <i class="fa fa-forward"></i>',
28704             disabled: true,
28705             preventDefault: true,
28706             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28707         });
28708         this.last = this.navgroup.addItem({
28709             tooltip: this.lastText,
28710             html : ' <i class="fa fa-step-forward"></i>',
28711             cls: "next btn-outline-secondary",
28712             disabled: true,
28713             preventDefault: true,
28714             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28715         });
28716     //this.addSeparator();
28717         this.loading = this.navgroup.addItem({
28718             tooltip: this.refreshText,
28719             cls: "btn-outline-secondary",
28720             html : ' <i class="fa fa-refresh"></i>',
28721             preventDefault: true,
28722             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28723         });
28724         
28725     },
28726
28727     // private
28728     updateInfo : function(){
28729         if(this.displayEl){
28730             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28731             var msg = count == 0 ?
28732                 this.emptyMsg :
28733                 String.format(
28734                     this.displayMsg,
28735                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28736                 );
28737             this.displayEl.update(msg);
28738         }
28739     },
28740
28741     // private
28742     onLoad : function(ds, r, o)
28743     {
28744         this.cursor = o.params && o.params.start ? o.params.start : 0;
28745         
28746         var d = this.getPageData(),
28747             ap = d.activePage,
28748             ps = d.pages;
28749         
28750         
28751         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28752         this.field.dom.value = ap;
28753         this.first.setDisabled(ap == 1);
28754         this.prev.setDisabled(ap == 1);
28755         this.next.setDisabled(ap == ps);
28756         this.last.setDisabled(ap == ps);
28757         this.loading.enable();
28758         this.updateInfo();
28759     },
28760
28761     // private
28762     getPageData : function(){
28763         var total = this.ds.getTotalCount();
28764         return {
28765             total : total,
28766             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28767             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28768         };
28769     },
28770
28771     // private
28772     onLoadError : function(){
28773         this.loading.enable();
28774     },
28775
28776     // private
28777     onPagingKeydown : function(e){
28778         var k = e.getKey();
28779         var d = this.getPageData();
28780         if(k == e.RETURN){
28781             var v = this.field.dom.value, pageNum;
28782             if(!v || isNaN(pageNum = parseInt(v, 10))){
28783                 this.field.dom.value = d.activePage;
28784                 return;
28785             }
28786             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28787             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28788             e.stopEvent();
28789         }
28790         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))
28791         {
28792           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28793           this.field.dom.value = pageNum;
28794           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28795           e.stopEvent();
28796         }
28797         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28798         {
28799           var v = this.field.dom.value, pageNum; 
28800           var increment = (e.shiftKey) ? 10 : 1;
28801           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28802                 increment *= -1;
28803           }
28804           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28805             this.field.dom.value = d.activePage;
28806             return;
28807           }
28808           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28809           {
28810             this.field.dom.value = parseInt(v, 10) + increment;
28811             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28812             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28813           }
28814           e.stopEvent();
28815         }
28816     },
28817
28818     // private
28819     beforeLoad : function(){
28820         if(this.loading){
28821             this.loading.disable();
28822         }
28823     },
28824
28825     // private
28826     onClick : function(which){
28827         
28828         var ds = this.ds;
28829         if (!ds) {
28830             return;
28831         }
28832         
28833         switch(which){
28834             case "first":
28835                 ds.load({params:{start: 0, limit: this.pageSize}});
28836             break;
28837             case "prev":
28838                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28839             break;
28840             case "next":
28841                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28842             break;
28843             case "last":
28844                 var total = ds.getTotalCount();
28845                 var extra = total % this.pageSize;
28846                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28847                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28848             break;
28849             case "refresh":
28850                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28851             break;
28852         }
28853     },
28854
28855     /**
28856      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28857      * @param {Roo.data.Store} store The data store to unbind
28858      */
28859     unbind : function(ds){
28860         ds.un("beforeload", this.beforeLoad, this);
28861         ds.un("load", this.onLoad, this);
28862         ds.un("loadexception", this.onLoadError, this);
28863         ds.un("remove", this.updateInfo, this);
28864         ds.un("add", this.updateInfo, this);
28865         this.ds = undefined;
28866     },
28867
28868     /**
28869      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28870      * @param {Roo.data.Store} store The data store to bind
28871      */
28872     bind : function(ds){
28873         ds.on("beforeload", this.beforeLoad, this);
28874         ds.on("load", this.onLoad, this);
28875         ds.on("loadexception", this.onLoadError, this);
28876         ds.on("remove", this.updateInfo, this);
28877         ds.on("add", this.updateInfo, this);
28878         this.ds = ds;
28879     }
28880 });/*
28881  * - LGPL
28882  *
28883  * element
28884  * 
28885  */
28886
28887 /**
28888  * @class Roo.bootstrap.MessageBar
28889  * @extends Roo.bootstrap.Component
28890  * Bootstrap MessageBar class
28891  * @cfg {String} html contents of the MessageBar
28892  * @cfg {String} weight (info | success | warning | danger) default info
28893  * @cfg {String} beforeClass insert the bar before the given class
28894  * @cfg {Boolean} closable (true | false) default false
28895  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28896  * 
28897  * @constructor
28898  * Create a new Element
28899  * @param {Object} config The config object
28900  */
28901
28902 Roo.bootstrap.MessageBar = function(config){
28903     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28904 };
28905
28906 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28907     
28908     html: '',
28909     weight: 'info',
28910     closable: false,
28911     fixed: false,
28912     beforeClass: 'bootstrap-sticky-wrap',
28913     
28914     getAutoCreate : function(){
28915         
28916         var cfg = {
28917             tag: 'div',
28918             cls: 'alert alert-dismissable alert-' + this.weight,
28919             cn: [
28920                 {
28921                     tag: 'span',
28922                     cls: 'message',
28923                     html: this.html || ''
28924                 }
28925             ]
28926         };
28927         
28928         if(this.fixed){
28929             cfg.cls += ' alert-messages-fixed';
28930         }
28931         
28932         if(this.closable){
28933             cfg.cn.push({
28934                 tag: 'button',
28935                 cls: 'close',
28936                 html: 'x'
28937             });
28938         }
28939         
28940         return cfg;
28941     },
28942     
28943     onRender : function(ct, position)
28944     {
28945         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28946         
28947         if(!this.el){
28948             var cfg = Roo.apply({},  this.getAutoCreate());
28949             cfg.id = Roo.id();
28950             
28951             if (this.cls) {
28952                 cfg.cls += ' ' + this.cls;
28953             }
28954             if (this.style) {
28955                 cfg.style = this.style;
28956             }
28957             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28958             
28959             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28960         }
28961         
28962         this.el.select('>button.close').on('click', this.hide, this);
28963         
28964     },
28965     
28966     show : function()
28967     {
28968         if (!this.rendered) {
28969             this.render();
28970         }
28971         
28972         this.el.show();
28973         
28974         this.fireEvent('show', this);
28975         
28976     },
28977     
28978     hide : function()
28979     {
28980         if (!this.rendered) {
28981             this.render();
28982         }
28983         
28984         this.el.hide();
28985         
28986         this.fireEvent('hide', this);
28987     },
28988     
28989     update : function()
28990     {
28991 //        var e = this.el.dom.firstChild;
28992 //        
28993 //        if(this.closable){
28994 //            e = e.nextSibling;
28995 //        }
28996 //        
28997 //        e.data = this.html || '';
28998
28999         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29000     }
29001    
29002 });
29003
29004  
29005
29006      /*
29007  * - LGPL
29008  *
29009  * Graph
29010  * 
29011  */
29012
29013
29014 /**
29015  * @class Roo.bootstrap.Graph
29016  * @extends Roo.bootstrap.Component
29017  * Bootstrap Graph class
29018 > Prameters
29019  -sm {number} sm 4
29020  -md {number} md 5
29021  @cfg {String} graphtype  bar | vbar | pie
29022  @cfg {number} g_x coodinator | centre x (pie)
29023  @cfg {number} g_y coodinator | centre y (pie)
29024  @cfg {number} g_r radius (pie)
29025  @cfg {number} g_height height of the chart (respected by all elements in the set)
29026  @cfg {number} g_width width of the chart (respected by all elements in the set)
29027  @cfg {Object} title The title of the chart
29028     
29029  -{Array}  values
29030  -opts (object) options for the chart 
29031      o {
29032      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29033      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29034      o vgutter (number)
29035      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.
29036      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29037      o to
29038      o stretch (boolean)
29039      o }
29040  -opts (object) options for the pie
29041      o{
29042      o cut
29043      o startAngle (number)
29044      o endAngle (number)
29045      } 
29046  *
29047  * @constructor
29048  * Create a new Input
29049  * @param {Object} config The config object
29050  */
29051
29052 Roo.bootstrap.Graph = function(config){
29053     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29054     
29055     this.addEvents({
29056         // img events
29057         /**
29058          * @event click
29059          * The img click event for the img.
29060          * @param {Roo.EventObject} e
29061          */
29062         "click" : true
29063     });
29064 };
29065
29066 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29067     
29068     sm: 4,
29069     md: 5,
29070     graphtype: 'bar',
29071     g_height: 250,
29072     g_width: 400,
29073     g_x: 50,
29074     g_y: 50,
29075     g_r: 30,
29076     opts:{
29077         //g_colors: this.colors,
29078         g_type: 'soft',
29079         g_gutter: '20%'
29080
29081     },
29082     title : false,
29083
29084     getAutoCreate : function(){
29085         
29086         var cfg = {
29087             tag: 'div',
29088             html : null
29089         };
29090         
29091         
29092         return  cfg;
29093     },
29094
29095     onRender : function(ct,position){
29096         
29097         
29098         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29099         
29100         if (typeof(Raphael) == 'undefined') {
29101             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29102             return;
29103         }
29104         
29105         this.raphael = Raphael(this.el.dom);
29106         
29107                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29111                 /*
29112                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29113                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29114                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29115                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29116                 
29117                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29118                 r.barchart(330, 10, 300, 220, data1);
29119                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29120                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29121                 */
29122                 
29123                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29124                 // r.barchart(30, 30, 560, 250,  xdata, {
29125                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29126                 //     axis : "0 0 1 1",
29127                 //     axisxlabels :  xdata
29128                 //     //yvalues : cols,
29129                    
29130                 // });
29131 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29132 //        
29133 //        this.load(null,xdata,{
29134 //                axis : "0 0 1 1",
29135 //                axisxlabels :  xdata
29136 //                });
29137
29138     },
29139
29140     load : function(graphtype,xdata,opts)
29141     {
29142         this.raphael.clear();
29143         if(!graphtype) {
29144             graphtype = this.graphtype;
29145         }
29146         if(!opts){
29147             opts = this.opts;
29148         }
29149         var r = this.raphael,
29150             fin = function () {
29151                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29152             },
29153             fout = function () {
29154                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29155             },
29156             pfin = function() {
29157                 this.sector.stop();
29158                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29159
29160                 if (this.label) {
29161                     this.label[0].stop();
29162                     this.label[0].attr({ r: 7.5 });
29163                     this.label[1].attr({ "font-weight": 800 });
29164                 }
29165             },
29166             pfout = function() {
29167                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29168
29169                 if (this.label) {
29170                     this.label[0].animate({ r: 5 }, 500, "bounce");
29171                     this.label[1].attr({ "font-weight": 400 });
29172                 }
29173             };
29174
29175         switch(graphtype){
29176             case 'bar':
29177                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29178                 break;
29179             case 'hbar':
29180                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29181                 break;
29182             case 'pie':
29183 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29184 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29185 //            
29186                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29187                 
29188                 break;
29189
29190         }
29191         
29192         if(this.title){
29193             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29194         }
29195         
29196     },
29197     
29198     setTitle: function(o)
29199     {
29200         this.title = o;
29201     },
29202     
29203     initEvents: function() {
29204         
29205         if(!this.href){
29206             this.el.on('click', this.onClick, this);
29207         }
29208     },
29209     
29210     onClick : function(e)
29211     {
29212         Roo.log('img onclick');
29213         this.fireEvent('click', this, e);
29214     }
29215    
29216 });
29217
29218  
29219 /*
29220  * - LGPL
29221  *
29222  * numberBox
29223  * 
29224  */
29225 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29226
29227 /**
29228  * @class Roo.bootstrap.dash.NumberBox
29229  * @extends Roo.bootstrap.Component
29230  * Bootstrap NumberBox class
29231  * @cfg {String} headline Box headline
29232  * @cfg {String} content Box content
29233  * @cfg {String} icon Box icon
29234  * @cfg {String} footer Footer text
29235  * @cfg {String} fhref Footer href
29236  * 
29237  * @constructor
29238  * Create a new NumberBox
29239  * @param {Object} config The config object
29240  */
29241
29242
29243 Roo.bootstrap.dash.NumberBox = function(config){
29244     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29245     
29246 };
29247
29248 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29249     
29250     headline : '',
29251     content : '',
29252     icon : '',
29253     footer : '',
29254     fhref : '',
29255     ficon : '',
29256     
29257     getAutoCreate : function(){
29258         
29259         var cfg = {
29260             tag : 'div',
29261             cls : 'small-box ',
29262             cn : [
29263                 {
29264                     tag : 'div',
29265                     cls : 'inner',
29266                     cn :[
29267                         {
29268                             tag : 'h3',
29269                             cls : 'roo-headline',
29270                             html : this.headline
29271                         },
29272                         {
29273                             tag : 'p',
29274                             cls : 'roo-content',
29275                             html : this.content
29276                         }
29277                     ]
29278                 }
29279             ]
29280         };
29281         
29282         if(this.icon){
29283             cfg.cn.push({
29284                 tag : 'div',
29285                 cls : 'icon',
29286                 cn :[
29287                     {
29288                         tag : 'i',
29289                         cls : 'ion ' + this.icon
29290                     }
29291                 ]
29292             });
29293         }
29294         
29295         if(this.footer){
29296             var footer = {
29297                 tag : 'a',
29298                 cls : 'small-box-footer',
29299                 href : this.fhref || '#',
29300                 html : this.footer
29301             };
29302             
29303             cfg.cn.push(footer);
29304             
29305         }
29306         
29307         return  cfg;
29308     },
29309
29310     onRender : function(ct,position){
29311         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29312
29313
29314        
29315                 
29316     },
29317
29318     setHeadline: function (value)
29319     {
29320         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29321     },
29322     
29323     setFooter: function (value, href)
29324     {
29325         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29326         
29327         if(href){
29328             this.el.select('a.small-box-footer',true).first().attr('href', href);
29329         }
29330         
29331     },
29332
29333     setContent: function (value)
29334     {
29335         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29336     },
29337
29338     initEvents: function() 
29339     {   
29340         
29341     }
29342     
29343 });
29344
29345  
29346 /*
29347  * - LGPL
29348  *
29349  * TabBox
29350  * 
29351  */
29352 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29353
29354 /**
29355  * @class Roo.bootstrap.dash.TabBox
29356  * @extends Roo.bootstrap.Component
29357  * @children Roo.bootstrap.dash.TabPane
29358  * Bootstrap TabBox class
29359  * @cfg {String} title Title of the TabBox
29360  * @cfg {String} icon Icon of the TabBox
29361  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29362  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29363  * 
29364  * @constructor
29365  * Create a new TabBox
29366  * @param {Object} config The config object
29367  */
29368
29369
29370 Roo.bootstrap.dash.TabBox = function(config){
29371     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29372     this.addEvents({
29373         // raw events
29374         /**
29375          * @event addpane
29376          * When a pane is added
29377          * @param {Roo.bootstrap.dash.TabPane} pane
29378          */
29379         "addpane" : true,
29380         /**
29381          * @event activatepane
29382          * When a pane is activated
29383          * @param {Roo.bootstrap.dash.TabPane} pane
29384          */
29385         "activatepane" : true
29386         
29387          
29388     });
29389     
29390     this.panes = [];
29391 };
29392
29393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29394
29395     title : '',
29396     icon : false,
29397     showtabs : true,
29398     tabScrollable : false,
29399     
29400     getChildContainer : function()
29401     {
29402         return this.el.select('.tab-content', true).first();
29403     },
29404     
29405     getAutoCreate : function(){
29406         
29407         var header = {
29408             tag: 'li',
29409             cls: 'pull-left header',
29410             html: this.title,
29411             cn : []
29412         };
29413         
29414         if(this.icon){
29415             header.cn.push({
29416                 tag: 'i',
29417                 cls: 'fa ' + this.icon
29418             });
29419         }
29420         
29421         var h = {
29422             tag: 'ul',
29423             cls: 'nav nav-tabs pull-right',
29424             cn: [
29425                 header
29426             ]
29427         };
29428         
29429         if(this.tabScrollable){
29430             h = {
29431                 tag: 'div',
29432                 cls: 'tab-header',
29433                 cn: [
29434                     {
29435                         tag: 'ul',
29436                         cls: 'nav nav-tabs pull-right',
29437                         cn: [
29438                             header
29439                         ]
29440                     }
29441                 ]
29442             };
29443         }
29444         
29445         var cfg = {
29446             tag: 'div',
29447             cls: 'nav-tabs-custom',
29448             cn: [
29449                 h,
29450                 {
29451                     tag: 'div',
29452                     cls: 'tab-content no-padding',
29453                     cn: []
29454                 }
29455             ]
29456         };
29457
29458         return  cfg;
29459     },
29460     initEvents : function()
29461     {
29462         //Roo.log('add add pane handler');
29463         this.on('addpane', this.onAddPane, this);
29464     },
29465      /**
29466      * Updates the box title
29467      * @param {String} html to set the title to.
29468      */
29469     setTitle : function(value)
29470     {
29471         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29472     },
29473     onAddPane : function(pane)
29474     {
29475         this.panes.push(pane);
29476         //Roo.log('addpane');
29477         //Roo.log(pane);
29478         // tabs are rendere left to right..
29479         if(!this.showtabs){
29480             return;
29481         }
29482         
29483         var ctr = this.el.select('.nav-tabs', true).first();
29484          
29485          
29486         var existing = ctr.select('.nav-tab',true);
29487         var qty = existing.getCount();;
29488         
29489         
29490         var tab = ctr.createChild({
29491             tag : 'li',
29492             cls : 'nav-tab' + (qty ? '' : ' active'),
29493             cn : [
29494                 {
29495                     tag : 'a',
29496                     href:'#',
29497                     html : pane.title
29498                 }
29499             ]
29500         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29501         pane.tab = tab;
29502         
29503         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29504         if (!qty) {
29505             pane.el.addClass('active');
29506         }
29507         
29508                 
29509     },
29510     onTabClick : function(ev,un,ob,pane)
29511     {
29512         //Roo.log('tab - prev default');
29513         ev.preventDefault();
29514         
29515         
29516         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29517         pane.tab.addClass('active');
29518         //Roo.log(pane.title);
29519         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29520         // technically we should have a deactivate event.. but maybe add later.
29521         // and it should not de-activate the selected tab...
29522         this.fireEvent('activatepane', pane);
29523         pane.el.addClass('active');
29524         pane.fireEvent('activate');
29525         
29526         
29527     },
29528     
29529     getActivePane : function()
29530     {
29531         var r = false;
29532         Roo.each(this.panes, function(p) {
29533             if(p.el.hasClass('active')){
29534                 r = p;
29535                 return false;
29536             }
29537             
29538             return;
29539         });
29540         
29541         return r;
29542     }
29543     
29544     
29545 });
29546
29547  
29548 /*
29549  * - LGPL
29550  *
29551  * Tab pane
29552  * 
29553  */
29554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29555 /**
29556  * @class Roo.bootstrap.TabPane
29557  * @extends Roo.bootstrap.Component
29558  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29559  * Bootstrap TabPane class
29560  * @cfg {Boolean} active (false | true) Default false
29561  * @cfg {String} title title of panel
29562
29563  * 
29564  * @constructor
29565  * Create a new TabPane
29566  * @param {Object} config The config object
29567  */
29568
29569 Roo.bootstrap.dash.TabPane = function(config){
29570     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29571     
29572     this.addEvents({
29573         // raw events
29574         /**
29575          * @event activate
29576          * When a pane is activated
29577          * @param {Roo.bootstrap.dash.TabPane} pane
29578          */
29579         "activate" : true
29580          
29581     });
29582 };
29583
29584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29585     
29586     active : false,
29587     title : '',
29588     
29589     // the tabBox that this is attached to.
29590     tab : false,
29591      
29592     getAutoCreate : function() 
29593     {
29594         var cfg = {
29595             tag: 'div',
29596             cls: 'tab-pane'
29597         };
29598         
29599         if(this.active){
29600             cfg.cls += ' active';
29601         }
29602         
29603         return cfg;
29604     },
29605     initEvents  : function()
29606     {
29607         //Roo.log('trigger add pane handler');
29608         this.parent().fireEvent('addpane', this)
29609     },
29610     
29611      /**
29612      * Updates the tab title 
29613      * @param {String} html to set the title to.
29614      */
29615     setTitle: function(str)
29616     {
29617         if (!this.tab) {
29618             return;
29619         }
29620         this.title = str;
29621         this.tab.select('a', true).first().dom.innerHTML = str;
29622         
29623     }
29624     
29625     
29626     
29627 });
29628
29629  
29630
29631
29632  /*
29633  * - LGPL
29634  *
29635  * Tooltip
29636  * 
29637  */
29638
29639 /**
29640  * @class Roo.bootstrap.Tooltip
29641  * Bootstrap Tooltip class
29642  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29643  * to determine which dom element triggers the tooltip.
29644  * 
29645  * It needs to add support for additional attributes like tooltip-position
29646  * 
29647  * @constructor
29648  * Create a new Toolti
29649  * @param {Object} config The config object
29650  */
29651
29652 Roo.bootstrap.Tooltip = function(config){
29653     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29654     
29655     this.alignment = Roo.bootstrap.Tooltip.alignment;
29656     
29657     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29658         this.alignment = config.alignment;
29659     }
29660     
29661 };
29662
29663 Roo.apply(Roo.bootstrap.Tooltip, {
29664     /**
29665      * @function init initialize tooltip monitoring.
29666      * @static
29667      */
29668     currentEl : false,
29669     currentTip : false,
29670     currentRegion : false,
29671     
29672     //  init : delay?
29673     
29674     init : function()
29675     {
29676         Roo.get(document).on('mouseover', this.enter ,this);
29677         Roo.get(document).on('mouseout', this.leave, this);
29678          
29679         
29680         this.currentTip = new Roo.bootstrap.Tooltip();
29681     },
29682     
29683     enter : function(ev)
29684     {
29685         var dom = ev.getTarget();
29686         
29687         //Roo.log(['enter',dom]);
29688         var el = Roo.fly(dom);
29689         if (this.currentEl) {
29690             //Roo.log(dom);
29691             //Roo.log(this.currentEl);
29692             //Roo.log(this.currentEl.contains(dom));
29693             if (this.currentEl == el) {
29694                 return;
29695             }
29696             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29697                 return;
29698             }
29699
29700         }
29701         
29702         if (this.currentTip.el) {
29703             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29704         }    
29705         //Roo.log(ev);
29706         
29707         if(!el || el.dom == document){
29708             return;
29709         }
29710         
29711         var bindEl = el; 
29712         var pel = false;
29713         if (!el.attr('tooltip')) {
29714             pel = el.findParent("[tooltip]");
29715             if (pel) {
29716                 bindEl = Roo.get(pel);
29717             }
29718         }
29719         
29720        
29721         
29722         // you can not look for children, as if el is the body.. then everythign is the child..
29723         if (!pel && !el.attr('tooltip')) { //
29724             if (!el.select("[tooltip]").elements.length) {
29725                 return;
29726             }
29727             // is the mouse over this child...?
29728             bindEl = el.select("[tooltip]").first();
29729             var xy = ev.getXY();
29730             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29731                 //Roo.log("not in region.");
29732                 return;
29733             }
29734             //Roo.log("child element over..");
29735             
29736         }
29737         this.currentEl = el;
29738         this.currentTip.bind(bindEl);
29739         this.currentRegion = Roo.lib.Region.getRegion(dom);
29740         this.currentTip.enter();
29741         
29742     },
29743     leave : function(ev)
29744     {
29745         var dom = ev.getTarget();
29746         //Roo.log(['leave',dom]);
29747         if (!this.currentEl) {
29748             return;
29749         }
29750         
29751         
29752         if (dom != this.currentEl.dom) {
29753             return;
29754         }
29755         var xy = ev.getXY();
29756         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29757             return;
29758         }
29759         // only activate leave if mouse cursor is outside... bounding box..
29760         
29761         
29762         
29763         
29764         if (this.currentTip) {
29765             this.currentTip.leave();
29766         }
29767         //Roo.log('clear currentEl');
29768         this.currentEl = false;
29769         
29770         
29771     },
29772     alignment : {
29773         'left' : ['r-l', [-2,0], 'right'],
29774         'right' : ['l-r', [2,0], 'left'],
29775         'bottom' : ['t-b', [0,2], 'top'],
29776         'top' : [ 'b-t', [0,-2], 'bottom']
29777     }
29778     
29779 });
29780
29781
29782 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29783     
29784     
29785     bindEl : false,
29786     
29787     delay : null, // can be { show : 300 , hide: 500}
29788     
29789     timeout : null,
29790     
29791     hoverState : null, //???
29792     
29793     placement : 'bottom', 
29794     
29795     alignment : false,
29796     
29797     getAutoCreate : function(){
29798     
29799         var cfg = {
29800            cls : 'tooltip',   
29801            role : 'tooltip',
29802            cn : [
29803                 {
29804                     cls : 'tooltip-arrow arrow'
29805                 },
29806                 {
29807                     cls : 'tooltip-inner'
29808                 }
29809            ]
29810         };
29811         
29812         return cfg;
29813     },
29814     bind : function(el)
29815     {
29816         this.bindEl = el;
29817     },
29818     
29819     initEvents : function()
29820     {
29821         this.arrowEl = this.el.select('.arrow', true).first();
29822         this.innerEl = this.el.select('.tooltip-inner', true).first();
29823     },
29824     
29825     enter : function () {
29826        
29827         if (this.timeout != null) {
29828             clearTimeout(this.timeout);
29829         }
29830         
29831         this.hoverState = 'in';
29832          //Roo.log("enter - show");
29833         if (!this.delay || !this.delay.show) {
29834             this.show();
29835             return;
29836         }
29837         var _t = this;
29838         this.timeout = setTimeout(function () {
29839             if (_t.hoverState == 'in') {
29840                 _t.show();
29841             }
29842         }, this.delay.show);
29843     },
29844     leave : function()
29845     {
29846         clearTimeout(this.timeout);
29847     
29848         this.hoverState = 'out';
29849          if (!this.delay || !this.delay.hide) {
29850             this.hide();
29851             return;
29852         }
29853        
29854         var _t = this;
29855         this.timeout = setTimeout(function () {
29856             //Roo.log("leave - timeout");
29857             
29858             if (_t.hoverState == 'out') {
29859                 _t.hide();
29860                 Roo.bootstrap.Tooltip.currentEl = false;
29861             }
29862         }, delay);
29863     },
29864     
29865     show : function (msg)
29866     {
29867         if (!this.el) {
29868             this.render(document.body);
29869         }
29870         // set content.
29871         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29872         
29873         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29874         
29875         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29876         
29877         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29878                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29879         
29880         var placement = typeof this.placement == 'function' ?
29881             this.placement.call(this, this.el, on_el) :
29882             this.placement;
29883             
29884         var autoToken = /\s?auto?\s?/i;
29885         var autoPlace = autoToken.test(placement);
29886         if (autoPlace) {
29887             placement = placement.replace(autoToken, '') || 'top';
29888         }
29889         
29890         //this.el.detach()
29891         //this.el.setXY([0,0]);
29892         this.el.show();
29893         //this.el.dom.style.display='block';
29894         
29895         //this.el.appendTo(on_el);
29896         
29897         var p = this.getPosition();
29898         var box = this.el.getBox();
29899         
29900         if (autoPlace) {
29901             // fixme..
29902         }
29903         
29904         var align = this.alignment[placement];
29905         
29906         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29907         
29908         if(placement == 'top' || placement == 'bottom'){
29909             if(xy[0] < 0){
29910                 placement = 'right';
29911             }
29912             
29913             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29914                 placement = 'left';
29915             }
29916             
29917             var scroll = Roo.select('body', true).first().getScroll();
29918             
29919             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29920                 placement = 'top';
29921             }
29922             
29923             align = this.alignment[placement];
29924             
29925             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29926             
29927         }
29928         
29929         var elems = document.getElementsByTagName('div');
29930         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29931         for (var i = 0; i < elems.length; i++) {
29932           var zindex = Number.parseInt(
29933                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29934                 10
29935           );
29936           if (zindex > highest) {
29937             highest = zindex;
29938           }
29939         }
29940         
29941         
29942         
29943         this.el.dom.style.zIndex = highest;
29944         
29945         this.el.alignTo(this.bindEl, align[0],align[1]);
29946         //var arrow = this.el.select('.arrow',true).first();
29947         //arrow.set(align[2], 
29948         
29949         this.el.addClass(placement);
29950         this.el.addClass("bs-tooltip-"+ placement);
29951         
29952         this.el.addClass('in fade show');
29953         
29954         this.hoverState = null;
29955         
29956         if (this.el.hasClass('fade')) {
29957             // fade it?
29958         }
29959         
29960         
29961         
29962         
29963         
29964     },
29965     hide : function()
29966     {
29967          
29968         if (!this.el) {
29969             return;
29970         }
29971         //this.el.setXY([0,0]);
29972         this.el.removeClass(['show', 'in']);
29973         //this.el.hide();
29974         
29975     }
29976     
29977 });
29978  
29979
29980  /*
29981  * - LGPL
29982  *
29983  * Location Picker
29984  * 
29985  */
29986
29987 /**
29988  * @class Roo.bootstrap.LocationPicker
29989  * @extends Roo.bootstrap.Component
29990  * Bootstrap LocationPicker class
29991  * @cfg {Number} latitude Position when init default 0
29992  * @cfg {Number} longitude Position when init default 0
29993  * @cfg {Number} zoom default 15
29994  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29995  * @cfg {Boolean} mapTypeControl default false
29996  * @cfg {Boolean} disableDoubleClickZoom default false
29997  * @cfg {Boolean} scrollwheel default true
29998  * @cfg {Boolean} streetViewControl default false
29999  * @cfg {Number} radius default 0
30000  * @cfg {String} locationName
30001  * @cfg {Boolean} draggable default true
30002  * @cfg {Boolean} enableAutocomplete default false
30003  * @cfg {Boolean} enableReverseGeocode default true
30004  * @cfg {String} markerTitle
30005  * 
30006  * @constructor
30007  * Create a new LocationPicker
30008  * @param {Object} config The config object
30009  */
30010
30011
30012 Roo.bootstrap.LocationPicker = function(config){
30013     
30014     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30015     
30016     this.addEvents({
30017         /**
30018          * @event initial
30019          * Fires when the picker initialized.
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          * @param {Google Location} location
30022          */
30023         initial : true,
30024         /**
30025          * @event positionchanged
30026          * Fires when the picker position changed.
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          * @param {Google Location} location
30029          */
30030         positionchanged : true,
30031         /**
30032          * @event resize
30033          * Fires when the map resize.
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          */
30036         resize : true,
30037         /**
30038          * @event show
30039          * Fires when the map show.
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         show : true,
30043         /**
30044          * @event hide
30045          * Fires when the map hide.
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          */
30048         hide : true,
30049         /**
30050          * @event mapClick
30051          * Fires when click the map.
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          * @param {Map event} e
30054          */
30055         mapClick : true,
30056         /**
30057          * @event mapRightClick
30058          * Fires when right click the map.
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          * @param {Map event} e
30061          */
30062         mapRightClick : true,
30063         /**
30064          * @event markerClick
30065          * Fires when click the marker.
30066          * @param {Roo.bootstrap.LocationPicker} this
30067          * @param {Map event} e
30068          */
30069         markerClick : true,
30070         /**
30071          * @event markerRightClick
30072          * Fires when right click the marker.
30073          * @param {Roo.bootstrap.LocationPicker} this
30074          * @param {Map event} e
30075          */
30076         markerRightClick : true,
30077         /**
30078          * @event OverlayViewDraw
30079          * Fires when OverlayView Draw
30080          * @param {Roo.bootstrap.LocationPicker} this
30081          */
30082         OverlayViewDraw : true,
30083         /**
30084          * @event OverlayViewOnAdd
30085          * Fires when OverlayView Draw
30086          * @param {Roo.bootstrap.LocationPicker} this
30087          */
30088         OverlayViewOnAdd : true,
30089         /**
30090          * @event OverlayViewOnRemove
30091          * Fires when OverlayView Draw
30092          * @param {Roo.bootstrap.LocationPicker} this
30093          */
30094         OverlayViewOnRemove : true,
30095         /**
30096          * @event OverlayViewShow
30097          * Fires when OverlayView Draw
30098          * @param {Roo.bootstrap.LocationPicker} this
30099          * @param {Pixel} cpx
30100          */
30101         OverlayViewShow : true,
30102         /**
30103          * @event OverlayViewHide
30104          * Fires when OverlayView Draw
30105          * @param {Roo.bootstrap.LocationPicker} this
30106          */
30107         OverlayViewHide : true,
30108         /**
30109          * @event loadexception
30110          * Fires when load google lib failed.
30111          * @param {Roo.bootstrap.LocationPicker} this
30112          */
30113         loadexception : true
30114     });
30115         
30116 };
30117
30118 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30119     
30120     gMapContext: false,
30121     
30122     latitude: 0,
30123     longitude: 0,
30124     zoom: 15,
30125     mapTypeId: false,
30126     mapTypeControl: false,
30127     disableDoubleClickZoom: false,
30128     scrollwheel: true,
30129     streetViewControl: false,
30130     radius: 0,
30131     locationName: '',
30132     draggable: true,
30133     enableAutocomplete: false,
30134     enableReverseGeocode: true,
30135     markerTitle: '',
30136     
30137     getAutoCreate: function()
30138     {
30139
30140         var cfg = {
30141             tag: 'div',
30142             cls: 'roo-location-picker'
30143         };
30144         
30145         return cfg
30146     },
30147     
30148     initEvents: function(ct, position)
30149     {       
30150         if(!this.el.getWidth() || this.isApplied()){
30151             return;
30152         }
30153         
30154         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30155         
30156         this.initial();
30157     },
30158     
30159     initial: function()
30160     {
30161         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30162             this.fireEvent('loadexception', this);
30163             return;
30164         }
30165         
30166         if(!this.mapTypeId){
30167             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30168         }
30169         
30170         this.gMapContext = this.GMapContext();
30171         
30172         this.initOverlayView();
30173         
30174         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30175         
30176         var _this = this;
30177                 
30178         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30179             _this.setPosition(_this.gMapContext.marker.position);
30180         });
30181         
30182         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30183             _this.fireEvent('mapClick', this, event);
30184             
30185         });
30186
30187         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30188             _this.fireEvent('mapRightClick', this, event);
30189             
30190         });
30191         
30192         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30193             _this.fireEvent('markerClick', this, event);
30194             
30195         });
30196
30197         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30198             _this.fireEvent('markerRightClick', this, event);
30199             
30200         });
30201         
30202         this.setPosition(this.gMapContext.location);
30203         
30204         this.fireEvent('initial', this, this.gMapContext.location);
30205     },
30206     
30207     initOverlayView: function()
30208     {
30209         var _this = this;
30210         
30211         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30212             
30213             draw: function()
30214             {
30215                 _this.fireEvent('OverlayViewDraw', _this);
30216             },
30217             
30218             onAdd: function()
30219             {
30220                 _this.fireEvent('OverlayViewOnAdd', _this);
30221             },
30222             
30223             onRemove: function()
30224             {
30225                 _this.fireEvent('OverlayViewOnRemove', _this);
30226             },
30227             
30228             show: function(cpx)
30229             {
30230                 _this.fireEvent('OverlayViewShow', _this, cpx);
30231             },
30232             
30233             hide: function()
30234             {
30235                 _this.fireEvent('OverlayViewHide', _this);
30236             }
30237             
30238         });
30239     },
30240     
30241     fromLatLngToContainerPixel: function(event)
30242     {
30243         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30244     },
30245     
30246     isApplied: function() 
30247     {
30248         return this.getGmapContext() == false ? false : true;
30249     },
30250     
30251     getGmapContext: function() 
30252     {
30253         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30254     },
30255     
30256     GMapContext: function() 
30257     {
30258         var position = new google.maps.LatLng(this.latitude, this.longitude);
30259         
30260         var _map = new google.maps.Map(this.el.dom, {
30261             center: position,
30262             zoom: this.zoom,
30263             mapTypeId: this.mapTypeId,
30264             mapTypeControl: this.mapTypeControl,
30265             disableDoubleClickZoom: this.disableDoubleClickZoom,
30266             scrollwheel: this.scrollwheel,
30267             streetViewControl: this.streetViewControl,
30268             locationName: this.locationName,
30269             draggable: this.draggable,
30270             enableAutocomplete: this.enableAutocomplete,
30271             enableReverseGeocode: this.enableReverseGeocode
30272         });
30273         
30274         var _marker = new google.maps.Marker({
30275             position: position,
30276             map: _map,
30277             title: this.markerTitle,
30278             draggable: this.draggable
30279         });
30280         
30281         return {
30282             map: _map,
30283             marker: _marker,
30284             circle: null,
30285             location: position,
30286             radius: this.radius,
30287             locationName: this.locationName,
30288             addressComponents: {
30289                 formatted_address: null,
30290                 addressLine1: null,
30291                 addressLine2: null,
30292                 streetName: null,
30293                 streetNumber: null,
30294                 city: null,
30295                 district: null,
30296                 state: null,
30297                 stateOrProvince: null
30298             },
30299             settings: this,
30300             domContainer: this.el.dom,
30301             geodecoder: new google.maps.Geocoder()
30302         };
30303     },
30304     
30305     drawCircle: function(center, radius, options) 
30306     {
30307         if (this.gMapContext.circle != null) {
30308             this.gMapContext.circle.setMap(null);
30309         }
30310         if (radius > 0) {
30311             radius *= 1;
30312             options = Roo.apply({}, options, {
30313                 strokeColor: "#0000FF",
30314                 strokeOpacity: .35,
30315                 strokeWeight: 2,
30316                 fillColor: "#0000FF",
30317                 fillOpacity: .2
30318             });
30319             
30320             options.map = this.gMapContext.map;
30321             options.radius = radius;
30322             options.center = center;
30323             this.gMapContext.circle = new google.maps.Circle(options);
30324             return this.gMapContext.circle;
30325         }
30326         
30327         return null;
30328     },
30329     
30330     setPosition: function(location) 
30331     {
30332         this.gMapContext.location = location;
30333         this.gMapContext.marker.setPosition(location);
30334         this.gMapContext.map.panTo(location);
30335         this.drawCircle(location, this.gMapContext.radius, {});
30336         
30337         var _this = this;
30338         
30339         if (this.gMapContext.settings.enableReverseGeocode) {
30340             this.gMapContext.geodecoder.geocode({
30341                 latLng: this.gMapContext.location
30342             }, function(results, status) {
30343                 
30344                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30345                     _this.gMapContext.locationName = results[0].formatted_address;
30346                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30347                     
30348                     _this.fireEvent('positionchanged', this, location);
30349                 }
30350             });
30351             
30352             return;
30353         }
30354         
30355         this.fireEvent('positionchanged', this, location);
30356     },
30357     
30358     resize: function()
30359     {
30360         google.maps.event.trigger(this.gMapContext.map, "resize");
30361         
30362         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30363         
30364         this.fireEvent('resize', this);
30365     },
30366     
30367     setPositionByLatLng: function(latitude, longitude)
30368     {
30369         this.setPosition(new google.maps.LatLng(latitude, longitude));
30370     },
30371     
30372     getCurrentPosition: function() 
30373     {
30374         return {
30375             latitude: this.gMapContext.location.lat(),
30376             longitude: this.gMapContext.location.lng()
30377         };
30378     },
30379     
30380     getAddressName: function() 
30381     {
30382         return this.gMapContext.locationName;
30383     },
30384     
30385     getAddressComponents: function() 
30386     {
30387         return this.gMapContext.addressComponents;
30388     },
30389     
30390     address_component_from_google_geocode: function(address_components) 
30391     {
30392         var result = {};
30393         
30394         for (var i = 0; i < address_components.length; i++) {
30395             var component = address_components[i];
30396             if (component.types.indexOf("postal_code") >= 0) {
30397                 result.postalCode = component.short_name;
30398             } else if (component.types.indexOf("street_number") >= 0) {
30399                 result.streetNumber = component.short_name;
30400             } else if (component.types.indexOf("route") >= 0) {
30401                 result.streetName = component.short_name;
30402             } else if (component.types.indexOf("neighborhood") >= 0) {
30403                 result.city = component.short_name;
30404             } else if (component.types.indexOf("locality") >= 0) {
30405                 result.city = component.short_name;
30406             } else if (component.types.indexOf("sublocality") >= 0) {
30407                 result.district = component.short_name;
30408             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30409                 result.stateOrProvince = component.short_name;
30410             } else if (component.types.indexOf("country") >= 0) {
30411                 result.country = component.short_name;
30412             }
30413         }
30414         
30415         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30416         result.addressLine2 = "";
30417         return result;
30418     },
30419     
30420     setZoomLevel: function(zoom)
30421     {
30422         this.gMapContext.map.setZoom(zoom);
30423     },
30424     
30425     show: function()
30426     {
30427         if(!this.el){
30428             return;
30429         }
30430         
30431         this.el.show();
30432         
30433         this.resize();
30434         
30435         this.fireEvent('show', this);
30436     },
30437     
30438     hide: function()
30439     {
30440         if(!this.el){
30441             return;
30442         }
30443         
30444         this.el.hide();
30445         
30446         this.fireEvent('hide', this);
30447     }
30448     
30449 });
30450
30451 Roo.apply(Roo.bootstrap.LocationPicker, {
30452     
30453     OverlayView : function(map, options)
30454     {
30455         options = options || {};
30456         
30457         this.setMap(map);
30458     }
30459     
30460     
30461 });/**
30462  * @class Roo.bootstrap.Alert
30463  * @extends Roo.bootstrap.Component
30464  * Bootstrap Alert class - shows an alert area box
30465  * eg
30466  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30467   Enter a valid email address
30468 </div>
30469  * @licence LGPL
30470  * @cfg {String} title The title of alert
30471  * @cfg {String} html The content of alert
30472  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30473  * @cfg {String} fa font-awesomeicon
30474  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30475  * @cfg {Boolean} close true to show a x closer
30476  * 
30477  * 
30478  * @constructor
30479  * Create a new alert
30480  * @param {Object} config The config object
30481  */
30482
30483
30484 Roo.bootstrap.Alert = function(config){
30485     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30486     
30487 };
30488
30489 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30490     
30491     title: '',
30492     html: '',
30493     weight: false,
30494     fa: false,
30495     faicon: false, // BC
30496     close : false,
30497     
30498     
30499     getAutoCreate : function()
30500     {
30501         
30502         var cfg = {
30503             tag : 'div',
30504             cls : 'alert',
30505             cn : [
30506                 {
30507                     tag: 'button',
30508                     type :  "button",
30509                     cls: "close",
30510                     html : '×',
30511                     style : this.close ? '' : 'display:none'
30512                 },
30513                 {
30514                     tag : 'i',
30515                     cls : 'roo-alert-icon'
30516                     
30517                 },
30518                 {
30519                     tag : 'b',
30520                     cls : 'roo-alert-title',
30521                     html : this.title
30522                 },
30523                 {
30524                     tag : 'span',
30525                     cls : 'roo-alert-text',
30526                     html : this.html
30527                 }
30528             ]
30529         };
30530         
30531         if(this.faicon){
30532             cfg.cn[0].cls += ' fa ' + this.faicon;
30533         }
30534         if(this.fa){
30535             cfg.cn[0].cls += ' fa ' + this.fa;
30536         }
30537         
30538         if(this.weight){
30539             cfg.cls += ' alert-' + this.weight;
30540         }
30541         
30542         return cfg;
30543     },
30544     
30545     initEvents: function() 
30546     {
30547         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30548         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30549         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30550         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30551         if (this.seconds > 0) {
30552             this.hide.defer(this.seconds, this);
30553         }
30554     },
30555     /**
30556      * Set the Title Message HTML
30557      * @param {String} html
30558      */
30559     setTitle : function(str)
30560     {
30561         this.titleEl.dom.innerHTML = str;
30562     },
30563      
30564      /**
30565      * Set the Body Message HTML
30566      * @param {String} html
30567      */
30568     setHtml : function(str)
30569     {
30570         this.htmlEl.dom.innerHTML = str;
30571     },
30572     /**
30573      * Set the Weight of the alert
30574      * @param {String} (success|info|warning|danger) weight
30575      */
30576     
30577     setWeight : function(weight)
30578     {
30579         if(this.weight){
30580             this.el.removeClass('alert-' + this.weight);
30581         }
30582         
30583         this.weight = weight;
30584         
30585         this.el.addClass('alert-' + this.weight);
30586     },
30587       /**
30588      * Set the Icon of the alert
30589      * @param {String} see fontawsome names (name without the 'fa-' bit)
30590      */
30591     setIcon : function(icon)
30592     {
30593         if(this.faicon){
30594             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30595         }
30596         
30597         this.faicon = icon;
30598         
30599         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30600     },
30601     /**
30602      * Hide the Alert
30603      */
30604     hide: function() 
30605     {
30606         this.el.hide();   
30607     },
30608     /**
30609      * Show the Alert
30610      */
30611     show: function() 
30612     {  
30613         this.el.show();   
30614     }
30615     
30616 });
30617
30618  
30619 /*
30620 * Licence: LGPL
30621 */
30622
30623 /**
30624  * @class Roo.bootstrap.UploadCropbox
30625  * @extends Roo.bootstrap.Component
30626  * Bootstrap UploadCropbox class
30627  * @cfg {String} emptyText show when image has been loaded
30628  * @cfg {String} rotateNotify show when image too small to rotate
30629  * @cfg {Number} errorTimeout default 3000
30630  * @cfg {Number} minWidth default 300
30631  * @cfg {Number} minHeight default 300
30632  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30633  * @cfg {Boolean} isDocument (true|false) default false
30634  * @cfg {String} url action url
30635  * @cfg {String} paramName default 'imageUpload'
30636  * @cfg {String} method default POST
30637  * @cfg {Boolean} loadMask (true|false) default true
30638  * @cfg {Boolean} loadingText default 'Loading...'
30639  * 
30640  * @constructor
30641  * Create a new UploadCropbox
30642  * @param {Object} config The config object
30643  */
30644
30645 Roo.bootstrap.UploadCropbox = function(config){
30646     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30647     
30648     this.addEvents({
30649         /**
30650          * @event beforeselectfile
30651          * Fire before select file
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          */
30654         "beforeselectfile" : true,
30655         /**
30656          * @event initial
30657          * Fire after initEvent
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          */
30660         "initial" : true,
30661         /**
30662          * @event crop
30663          * Fire after initEvent
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          * @param {String} data
30666          */
30667         "crop" : true,
30668         /**
30669          * @event prepare
30670          * Fire when preparing the file data
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {Object} file
30673          */
30674         "prepare" : true,
30675         /**
30676          * @event exception
30677          * Fire when get exception
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          * @param {XMLHttpRequest} xhr
30680          */
30681         "exception" : true,
30682         /**
30683          * @event beforeloadcanvas
30684          * Fire before load the canvas
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          * @param {String} src
30687          */
30688         "beforeloadcanvas" : true,
30689         /**
30690          * @event trash
30691          * Fire when trash image
30692          * @param {Roo.bootstrap.UploadCropbox} this
30693          */
30694         "trash" : true,
30695         /**
30696          * @event download
30697          * Fire when download the image
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          */
30700         "download" : true,
30701         /**
30702          * @event footerbuttonclick
30703          * Fire when footerbuttonclick
30704          * @param {Roo.bootstrap.UploadCropbox} this
30705          * @param {String} type
30706          */
30707         "footerbuttonclick" : true,
30708         /**
30709          * @event resize
30710          * Fire when resize
30711          * @param {Roo.bootstrap.UploadCropbox} this
30712          */
30713         "resize" : true,
30714         /**
30715          * @event rotate
30716          * Fire when rotate the image
30717          * @param {Roo.bootstrap.UploadCropbox} this
30718          * @param {String} pos
30719          */
30720         "rotate" : true,
30721         /**
30722          * @event inspect
30723          * Fire when inspect the file
30724          * @param {Roo.bootstrap.UploadCropbox} this
30725          * @param {Object} file
30726          */
30727         "inspect" : true,
30728         /**
30729          * @event upload
30730          * Fire when xhr upload the file
30731          * @param {Roo.bootstrap.UploadCropbox} this
30732          * @param {Object} data
30733          */
30734         "upload" : true,
30735         /**
30736          * @event arrange
30737          * Fire when arrange the file data
30738          * @param {Roo.bootstrap.UploadCropbox} this
30739          * @param {Object} formData
30740          */
30741         "arrange" : true
30742     });
30743     
30744     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30745 };
30746
30747 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30748     
30749     emptyText : 'Click to upload image',
30750     rotateNotify : 'Image is too small to rotate',
30751     errorTimeout : 3000,
30752     scale : 0,
30753     baseScale : 1,
30754     rotate : 0,
30755     dragable : false,
30756     pinching : false,
30757     mouseX : 0,
30758     mouseY : 0,
30759     cropData : false,
30760     minWidth : 300,
30761     minHeight : 300,
30762     file : false,
30763     exif : {},
30764     baseRotate : 1,
30765     cropType : 'image/jpeg',
30766     buttons : false,
30767     canvasLoaded : false,
30768     isDocument : false,
30769     method : 'POST',
30770     paramName : 'imageUpload',
30771     loadMask : true,
30772     loadingText : 'Loading...',
30773     maskEl : false,
30774     
30775     getAutoCreate : function()
30776     {
30777         var cfg = {
30778             tag : 'div',
30779             cls : 'roo-upload-cropbox',
30780             cn : [
30781                 {
30782                     tag : 'input',
30783                     cls : 'roo-upload-cropbox-selector',
30784                     type : 'file'
30785                 },
30786                 {
30787                     tag : 'div',
30788                     cls : 'roo-upload-cropbox-body',
30789                     style : 'cursor:pointer',
30790                     cn : [
30791                         {
30792                             tag : 'div',
30793                             cls : 'roo-upload-cropbox-preview'
30794                         },
30795                         {
30796                             tag : 'div',
30797                             cls : 'roo-upload-cropbox-thumb'
30798                         },
30799                         {
30800                             tag : 'div',
30801                             cls : 'roo-upload-cropbox-empty-notify',
30802                             html : this.emptyText
30803                         },
30804                         {
30805                             tag : 'div',
30806                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30807                             html : this.rotateNotify
30808                         }
30809                     ]
30810                 },
30811                 {
30812                     tag : 'div',
30813                     cls : 'roo-upload-cropbox-footer',
30814                     cn : {
30815                         tag : 'div',
30816                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30817                         cn : []
30818                     }
30819                 }
30820             ]
30821         };
30822         
30823         return cfg;
30824     },
30825     
30826     onRender : function(ct, position)
30827     {
30828         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30829         
30830         if (this.buttons.length) {
30831             
30832             Roo.each(this.buttons, function(bb) {
30833                 
30834                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30835                 
30836                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30837                 
30838             }, this);
30839         }
30840         
30841         if(this.loadMask){
30842             this.maskEl = this.el;
30843         }
30844     },
30845     
30846     initEvents : function()
30847     {
30848         this.urlAPI = (window.createObjectURL && window) || 
30849                                 (window.URL && URL.revokeObjectURL && URL) || 
30850                                 (window.webkitURL && webkitURL);
30851                         
30852         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30853         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30854         
30855         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30856         this.selectorEl.hide();
30857         
30858         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30859         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30860         
30861         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30862         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863         this.thumbEl.hide();
30864         
30865         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30866         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30867         
30868         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30869         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870         this.errorEl.hide();
30871         
30872         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30873         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874         this.footerEl.hide();
30875         
30876         this.setThumbBoxSize();
30877         
30878         this.bind();
30879         
30880         this.resize();
30881         
30882         this.fireEvent('initial', this);
30883     },
30884
30885     bind : function()
30886     {
30887         var _this = this;
30888         
30889         window.addEventListener("resize", function() { _this.resize(); } );
30890         
30891         this.bodyEl.on('click', this.beforeSelectFile, this);
30892         
30893         if(Roo.isTouch){
30894             this.bodyEl.on('touchstart', this.onTouchStart, this);
30895             this.bodyEl.on('touchmove', this.onTouchMove, this);
30896             this.bodyEl.on('touchend', this.onTouchEnd, this);
30897         }
30898         
30899         if(!Roo.isTouch){
30900             this.bodyEl.on('mousedown', this.onMouseDown, this);
30901             this.bodyEl.on('mousemove', this.onMouseMove, this);
30902             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30903             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30904             Roo.get(document).on('mouseup', this.onMouseUp, this);
30905         }
30906         
30907         this.selectorEl.on('change', this.onFileSelected, this);
30908     },
30909     
30910     reset : function()
30911     {    
30912         this.scale = 0;
30913         this.baseScale = 1;
30914         this.rotate = 0;
30915         this.baseRotate = 1;
30916         this.dragable = false;
30917         this.pinching = false;
30918         this.mouseX = 0;
30919         this.mouseY = 0;
30920         this.cropData = false;
30921         this.notifyEl.dom.innerHTML = this.emptyText;
30922         
30923         this.selectorEl.dom.value = '';
30924         
30925     },
30926     
30927     resize : function()
30928     {
30929         if(this.fireEvent('resize', this) != false){
30930             this.setThumbBoxPosition();
30931             this.setCanvasPosition();
30932         }
30933     },
30934     
30935     onFooterButtonClick : function(e, el, o, type)
30936     {
30937         switch (type) {
30938             case 'rotate-left' :
30939                 this.onRotateLeft(e);
30940                 break;
30941             case 'rotate-right' :
30942                 this.onRotateRight(e);
30943                 break;
30944             case 'picture' :
30945                 this.beforeSelectFile(e);
30946                 break;
30947             case 'trash' :
30948                 this.trash(e);
30949                 break;
30950             case 'crop' :
30951                 this.crop(e);
30952                 break;
30953             case 'download' :
30954                 this.download(e);
30955                 break;
30956             default :
30957                 break;
30958         }
30959         
30960         this.fireEvent('footerbuttonclick', this, type);
30961     },
30962     
30963     beforeSelectFile : function(e)
30964     {
30965         e.preventDefault();
30966         
30967         if(this.fireEvent('beforeselectfile', this) != false){
30968             this.selectorEl.dom.click();
30969         }
30970     },
30971     
30972     onFileSelected : function(e)
30973     {
30974         e.preventDefault();
30975         
30976         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30977             return;
30978         }
30979         
30980         var file = this.selectorEl.dom.files[0];
30981         
30982         if(this.fireEvent('inspect', this, file) != false){
30983             this.prepare(file);
30984         }
30985         
30986     },
30987     
30988     trash : function(e)
30989     {
30990         this.fireEvent('trash', this);
30991     },
30992     
30993     download : function(e)
30994     {
30995         this.fireEvent('download', this);
30996     },
30997     
30998     loadCanvas : function(src)
30999     {   
31000         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31001             
31002             this.reset();
31003             
31004             this.imageEl = document.createElement('img');
31005             
31006             var _this = this;
31007             
31008             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31009             
31010             this.imageEl.src = src;
31011         }
31012     },
31013     
31014     onLoadCanvas : function()
31015     {   
31016         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31017         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31018         
31019         this.bodyEl.un('click', this.beforeSelectFile, this);
31020         
31021         this.notifyEl.hide();
31022         this.thumbEl.show();
31023         this.footerEl.show();
31024         
31025         this.baseRotateLevel();
31026         
31027         if(this.isDocument){
31028             this.setThumbBoxSize();
31029         }
31030         
31031         this.setThumbBoxPosition();
31032         
31033         this.baseScaleLevel();
31034         
31035         this.draw();
31036         
31037         this.resize();
31038         
31039         this.canvasLoaded = true;
31040         
31041         if(this.loadMask){
31042             this.maskEl.unmask();
31043         }
31044         
31045     },
31046     
31047     setCanvasPosition : function()
31048     {   
31049         if(!this.canvasEl){
31050             return;
31051         }
31052         
31053         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31054         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31055         
31056         this.previewEl.setLeft(pw);
31057         this.previewEl.setTop(ph);
31058         
31059     },
31060     
31061     onMouseDown : function(e)
31062     {   
31063         e.stopEvent();
31064         
31065         this.dragable = true;
31066         this.pinching = false;
31067         
31068         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31069             this.dragable = false;
31070             return;
31071         }
31072         
31073         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31074         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31075         
31076     },
31077     
31078     onMouseMove : function(e)
31079     {   
31080         e.stopEvent();
31081         
31082         if(!this.canvasLoaded){
31083             return;
31084         }
31085         
31086         if (!this.dragable){
31087             return;
31088         }
31089         
31090         var minX = Math.ceil(this.thumbEl.getLeft(true));
31091         var minY = Math.ceil(this.thumbEl.getTop(true));
31092         
31093         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31094         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31095         
31096         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098         
31099         x = x - this.mouseX;
31100         y = y - this.mouseY;
31101         
31102         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31103         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31104         
31105         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31106         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31107         
31108         this.previewEl.setLeft(bgX);
31109         this.previewEl.setTop(bgY);
31110         
31111         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31112         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31113     },
31114     
31115     onMouseUp : function(e)
31116     {   
31117         e.stopEvent();
31118         
31119         this.dragable = false;
31120     },
31121     
31122     onMouseWheel : function(e)
31123     {   
31124         e.stopEvent();
31125         
31126         this.startScale = this.scale;
31127         
31128         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31129         
31130         if(!this.zoomable()){
31131             this.scale = this.startScale;
31132             return;
31133         }
31134         
31135         this.draw();
31136         
31137         return;
31138     },
31139     
31140     zoomable : function()
31141     {
31142         var minScale = this.thumbEl.getWidth() / this.minWidth;
31143         
31144         if(this.minWidth < this.minHeight){
31145             minScale = this.thumbEl.getHeight() / this.minHeight;
31146         }
31147         
31148         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31149         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31150         
31151         if(
31152                 this.isDocument &&
31153                 (this.rotate == 0 || this.rotate == 180) && 
31154                 (
31155                     width > this.imageEl.OriginWidth || 
31156                     height > this.imageEl.OriginHeight ||
31157                     (width < this.minWidth && height < this.minHeight)
31158                 )
31159         ){
31160             return false;
31161         }
31162         
31163         if(
31164                 this.isDocument &&
31165                 (this.rotate == 90 || this.rotate == 270) && 
31166                 (
31167                     width > this.imageEl.OriginWidth || 
31168                     height > this.imageEl.OriginHeight ||
31169                     (width < this.minHeight && height < this.minWidth)
31170                 )
31171         ){
31172             return false;
31173         }
31174         
31175         if(
31176                 !this.isDocument &&
31177                 (this.rotate == 0 || this.rotate == 180) && 
31178                 (
31179                     width < this.minWidth || 
31180                     width > this.imageEl.OriginWidth || 
31181                     height < this.minHeight || 
31182                     height > this.imageEl.OriginHeight
31183                 )
31184         ){
31185             return false;
31186         }
31187         
31188         if(
31189                 !this.isDocument &&
31190                 (this.rotate == 90 || this.rotate == 270) && 
31191                 (
31192                     width < this.minHeight || 
31193                     width > this.imageEl.OriginWidth || 
31194                     height < this.minWidth || 
31195                     height > this.imageEl.OriginHeight
31196                 )
31197         ){
31198             return false;
31199         }
31200         
31201         return true;
31202         
31203     },
31204     
31205     onRotateLeft : function(e)
31206     {   
31207         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208             
31209             var minScale = this.thumbEl.getWidth() / this.minWidth;
31210             
31211             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31212             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213             
31214             this.startScale = this.scale;
31215             
31216             while (this.getScaleLevel() < minScale){
31217             
31218                 this.scale = this.scale + 1;
31219                 
31220                 if(!this.zoomable()){
31221                     break;
31222                 }
31223                 
31224                 if(
31225                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31226                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31227                 ){
31228                     continue;
31229                 }
31230                 
31231                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31232
31233                 this.draw();
31234                 
31235                 return;
31236             }
31237             
31238             this.scale = this.startScale;
31239             
31240             this.onRotateFail();
31241             
31242             return false;
31243         }
31244         
31245         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31246
31247         if(this.isDocument){
31248             this.setThumbBoxSize();
31249             this.setThumbBoxPosition();
31250             this.setCanvasPosition();
31251         }
31252         
31253         this.draw();
31254         
31255         this.fireEvent('rotate', this, 'left');
31256         
31257     },
31258     
31259     onRotateRight : function(e)
31260     {
31261         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31262             
31263             var minScale = this.thumbEl.getWidth() / this.minWidth;
31264         
31265             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31266             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31267             
31268             this.startScale = this.scale;
31269             
31270             while (this.getScaleLevel() < minScale){
31271             
31272                 this.scale = this.scale + 1;
31273                 
31274                 if(!this.zoomable()){
31275                     break;
31276                 }
31277                 
31278                 if(
31279                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31280                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31281                 ){
31282                     continue;
31283                 }
31284                 
31285                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31286
31287                 this.draw();
31288                 
31289                 return;
31290             }
31291             
31292             this.scale = this.startScale;
31293             
31294             this.onRotateFail();
31295             
31296             return false;
31297         }
31298         
31299         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31300
31301         if(this.isDocument){
31302             this.setThumbBoxSize();
31303             this.setThumbBoxPosition();
31304             this.setCanvasPosition();
31305         }
31306         
31307         this.draw();
31308         
31309         this.fireEvent('rotate', this, 'right');
31310     },
31311     
31312     onRotateFail : function()
31313     {
31314         this.errorEl.show(true);
31315         
31316         var _this = this;
31317         
31318         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31319     },
31320     
31321     draw : function()
31322     {
31323         this.previewEl.dom.innerHTML = '';
31324         
31325         var canvasEl = document.createElement("canvas");
31326         
31327         var contextEl = canvasEl.getContext("2d");
31328         
31329         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31330         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31331         var center = this.imageEl.OriginWidth / 2;
31332         
31333         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31334             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31335             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31336             center = this.imageEl.OriginHeight / 2;
31337         }
31338         
31339         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31340         
31341         contextEl.translate(center, center);
31342         contextEl.rotate(this.rotate * Math.PI / 180);
31343
31344         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31345         
31346         this.canvasEl = document.createElement("canvas");
31347         
31348         this.contextEl = this.canvasEl.getContext("2d");
31349         
31350         switch (this.rotate) {
31351             case 0 :
31352                 
31353                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31354                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31355                 
31356                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31357                 
31358                 break;
31359             case 90 : 
31360                 
31361                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31362                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31363                 
31364                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365                     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);
31366                     break;
31367                 }
31368                 
31369                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31370                 
31371                 break;
31372             case 180 :
31373                 
31374                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31375                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31376                 
31377                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378                     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);
31379                     break;
31380                 }
31381                 
31382                 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);
31383                 
31384                 break;
31385             case 270 :
31386                 
31387                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31388                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31389         
31390                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31391                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31392                     break;
31393                 }
31394                 
31395                 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);
31396                 
31397                 break;
31398             default : 
31399                 break;
31400         }
31401         
31402         this.previewEl.appendChild(this.canvasEl);
31403         
31404         this.setCanvasPosition();
31405     },
31406     
31407     crop : function()
31408     {
31409         if(!this.canvasLoaded){
31410             return;
31411         }
31412         
31413         var imageCanvas = document.createElement("canvas");
31414         
31415         var imageContext = imageCanvas.getContext("2d");
31416         
31417         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31419         
31420         var center = imageCanvas.width / 2;
31421         
31422         imageContext.translate(center, center);
31423         
31424         imageContext.rotate(this.rotate * Math.PI / 180);
31425         
31426         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31427         
31428         var canvas = document.createElement("canvas");
31429         
31430         var context = canvas.getContext("2d");
31431                 
31432         canvas.width = this.minWidth;
31433         canvas.height = this.minHeight;
31434
31435         switch (this.rotate) {
31436             case 0 :
31437                 
31438                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31439                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31440                 
31441                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31442                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31443                 
31444                 var targetWidth = this.minWidth - 2 * x;
31445                 var targetHeight = this.minHeight - 2 * y;
31446                 
31447                 var scale = 1;
31448                 
31449                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31450                     scale = targetWidth / width;
31451                 }
31452                 
31453                 if(x > 0 && y == 0){
31454                     scale = targetHeight / height;
31455                 }
31456                 
31457                 if(x > 0 && y > 0){
31458                     scale = targetWidth / width;
31459                     
31460                     if(width < height){
31461                         scale = targetHeight / height;
31462                     }
31463                 }
31464                 
31465                 context.scale(scale, scale);
31466                 
31467                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31468                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31469
31470                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31471                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31472
31473                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31474                 
31475                 break;
31476             case 90 : 
31477                 
31478                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31479                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31480                 
31481                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31482                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483                 
31484                 var targetWidth = this.minWidth - 2 * x;
31485                 var targetHeight = this.minHeight - 2 * y;
31486                 
31487                 var scale = 1;
31488                 
31489                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31490                     scale = targetWidth / width;
31491                 }
31492                 
31493                 if(x > 0 && y == 0){
31494                     scale = targetHeight / height;
31495                 }
31496                 
31497                 if(x > 0 && y > 0){
31498                     scale = targetWidth / width;
31499                     
31500                     if(width < height){
31501                         scale = targetHeight / height;
31502                     }
31503                 }
31504                 
31505                 context.scale(scale, scale);
31506                 
31507                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31508                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509
31510                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31511                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512                 
31513                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514                 
31515                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31516                 
31517                 break;
31518             case 180 :
31519                 
31520                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31521                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31522                 
31523                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525                 
31526                 var targetWidth = this.minWidth - 2 * x;
31527                 var targetHeight = this.minHeight - 2 * y;
31528                 
31529                 var scale = 1;
31530                 
31531                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532                     scale = targetWidth / width;
31533                 }
31534                 
31535                 if(x > 0 && y == 0){
31536                     scale = targetHeight / height;
31537                 }
31538                 
31539                 if(x > 0 && y > 0){
31540                     scale = targetWidth / width;
31541                     
31542                     if(width < height){
31543                         scale = targetHeight / height;
31544                     }
31545                 }
31546                 
31547                 context.scale(scale, scale);
31548                 
31549                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551
31552                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554
31555                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31557                 
31558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31559                 
31560                 break;
31561             case 270 :
31562                 
31563                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31564                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31565                 
31566                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31567                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31568                 
31569                 var targetWidth = this.minWidth - 2 * x;
31570                 var targetHeight = this.minHeight - 2 * y;
31571                 
31572                 var scale = 1;
31573                 
31574                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31575                     scale = targetWidth / width;
31576                 }
31577                 
31578                 if(x > 0 && y == 0){
31579                     scale = targetHeight / height;
31580                 }
31581                 
31582                 if(x > 0 && y > 0){
31583                     scale = targetWidth / width;
31584                     
31585                     if(width < height){
31586                         scale = targetHeight / height;
31587                     }
31588                 }
31589                 
31590                 context.scale(scale, scale);
31591                 
31592                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31593                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31594
31595                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31596                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31597                 
31598                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31599                 
31600                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31601                 
31602                 break;
31603             default : 
31604                 break;
31605         }
31606         
31607         this.cropData = canvas.toDataURL(this.cropType);
31608         
31609         if(this.fireEvent('crop', this, this.cropData) !== false){
31610             this.process(this.file, this.cropData);
31611         }
31612         
31613         return;
31614         
31615     },
31616     
31617     setThumbBoxSize : function()
31618     {
31619         var width, height;
31620         
31621         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31622             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31623             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31624             
31625             this.minWidth = width;
31626             this.minHeight = height;
31627             
31628             if(this.rotate == 90 || this.rotate == 270){
31629                 this.minWidth = height;
31630                 this.minHeight = width;
31631             }
31632         }
31633         
31634         height = 300;
31635         width = Math.ceil(this.minWidth * height / this.minHeight);
31636         
31637         if(this.minWidth > this.minHeight){
31638             width = 300;
31639             height = Math.ceil(this.minHeight * width / this.minWidth);
31640         }
31641         
31642         this.thumbEl.setStyle({
31643             width : width + 'px',
31644             height : height + 'px'
31645         });
31646
31647         return;
31648             
31649     },
31650     
31651     setThumbBoxPosition : function()
31652     {
31653         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31654         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31655         
31656         this.thumbEl.setLeft(x);
31657         this.thumbEl.setTop(y);
31658         
31659     },
31660     
31661     baseRotateLevel : function()
31662     {
31663         this.baseRotate = 1;
31664         
31665         if(
31666                 typeof(this.exif) != 'undefined' &&
31667                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31668                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31669         ){
31670             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31671         }
31672         
31673         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31674         
31675     },
31676     
31677     baseScaleLevel : function()
31678     {
31679         var width, height;
31680         
31681         if(this.isDocument){
31682             
31683             if(this.baseRotate == 6 || this.baseRotate == 8){
31684             
31685                 height = this.thumbEl.getHeight();
31686                 this.baseScale = height / this.imageEl.OriginWidth;
31687
31688                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31689                     width = this.thumbEl.getWidth();
31690                     this.baseScale = width / this.imageEl.OriginHeight;
31691                 }
31692
31693                 return;
31694             }
31695
31696             height = this.thumbEl.getHeight();
31697             this.baseScale = height / this.imageEl.OriginHeight;
31698
31699             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31700                 width = this.thumbEl.getWidth();
31701                 this.baseScale = width / this.imageEl.OriginWidth;
31702             }
31703
31704             return;
31705         }
31706         
31707         if(this.baseRotate == 6 || this.baseRotate == 8){
31708             
31709             width = this.thumbEl.getHeight();
31710             this.baseScale = width / this.imageEl.OriginHeight;
31711             
31712             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31713                 height = this.thumbEl.getWidth();
31714                 this.baseScale = height / this.imageEl.OriginHeight;
31715             }
31716             
31717             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31718                 height = this.thumbEl.getWidth();
31719                 this.baseScale = height / this.imageEl.OriginHeight;
31720                 
31721                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31722                     width = this.thumbEl.getHeight();
31723                     this.baseScale = width / this.imageEl.OriginWidth;
31724                 }
31725             }
31726             
31727             return;
31728         }
31729         
31730         width = this.thumbEl.getWidth();
31731         this.baseScale = width / this.imageEl.OriginWidth;
31732         
31733         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31734             height = this.thumbEl.getHeight();
31735             this.baseScale = height / this.imageEl.OriginHeight;
31736         }
31737         
31738         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31739             
31740             height = this.thumbEl.getHeight();
31741             this.baseScale = height / this.imageEl.OriginHeight;
31742             
31743             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31744                 width = this.thumbEl.getWidth();
31745                 this.baseScale = width / this.imageEl.OriginWidth;
31746             }
31747             
31748         }
31749         
31750         return;
31751     },
31752     
31753     getScaleLevel : function()
31754     {
31755         return this.baseScale * Math.pow(1.1, this.scale);
31756     },
31757     
31758     onTouchStart : function(e)
31759     {
31760         if(!this.canvasLoaded){
31761             this.beforeSelectFile(e);
31762             return;
31763         }
31764         
31765         var touches = e.browserEvent.touches;
31766         
31767         if(!touches){
31768             return;
31769         }
31770         
31771         if(touches.length == 1){
31772             this.onMouseDown(e);
31773             return;
31774         }
31775         
31776         if(touches.length != 2){
31777             return;
31778         }
31779         
31780         var coords = [];
31781         
31782         for(var i = 0, finger; finger = touches[i]; i++){
31783             coords.push(finger.pageX, finger.pageY);
31784         }
31785         
31786         var x = Math.pow(coords[0] - coords[2], 2);
31787         var y = Math.pow(coords[1] - coords[3], 2);
31788         
31789         this.startDistance = Math.sqrt(x + y);
31790         
31791         this.startScale = this.scale;
31792         
31793         this.pinching = true;
31794         this.dragable = false;
31795         
31796     },
31797     
31798     onTouchMove : function(e)
31799     {
31800         if(!this.pinching && !this.dragable){
31801             return;
31802         }
31803         
31804         var touches = e.browserEvent.touches;
31805         
31806         if(!touches){
31807             return;
31808         }
31809         
31810         if(this.dragable){
31811             this.onMouseMove(e);
31812             return;
31813         }
31814         
31815         var coords = [];
31816         
31817         for(var i = 0, finger; finger = touches[i]; i++){
31818             coords.push(finger.pageX, finger.pageY);
31819         }
31820         
31821         var x = Math.pow(coords[0] - coords[2], 2);
31822         var y = Math.pow(coords[1] - coords[3], 2);
31823         
31824         this.endDistance = Math.sqrt(x + y);
31825         
31826         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31827         
31828         if(!this.zoomable()){
31829             this.scale = this.startScale;
31830             return;
31831         }
31832         
31833         this.draw();
31834         
31835     },
31836     
31837     onTouchEnd : function(e)
31838     {
31839         this.pinching = false;
31840         this.dragable = false;
31841         
31842     },
31843     
31844     process : function(file, crop)
31845     {
31846         if(this.loadMask){
31847             this.maskEl.mask(this.loadingText);
31848         }
31849         
31850         this.xhr = new XMLHttpRequest();
31851         
31852         file.xhr = this.xhr;
31853
31854         this.xhr.open(this.method, this.url, true);
31855         
31856         var headers = {
31857             "Accept": "application/json",
31858             "Cache-Control": "no-cache",
31859             "X-Requested-With": "XMLHttpRequest"
31860         };
31861         
31862         for (var headerName in headers) {
31863             var headerValue = headers[headerName];
31864             if (headerValue) {
31865                 this.xhr.setRequestHeader(headerName, headerValue);
31866             }
31867         }
31868         
31869         var _this = this;
31870         
31871         this.xhr.onload = function()
31872         {
31873             _this.xhrOnLoad(_this.xhr);
31874         }
31875         
31876         this.xhr.onerror = function()
31877         {
31878             _this.xhrOnError(_this.xhr);
31879         }
31880         
31881         var formData = new FormData();
31882
31883         formData.append('returnHTML', 'NO');
31884         
31885         if(crop){
31886             formData.append('crop', crop);
31887         }
31888         
31889         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31890             formData.append(this.paramName, file, file.name);
31891         }
31892         
31893         if(typeof(file.filename) != 'undefined'){
31894             formData.append('filename', file.filename);
31895         }
31896         
31897         if(typeof(file.mimetype) != 'undefined'){
31898             formData.append('mimetype', file.mimetype);
31899         }
31900         
31901         if(this.fireEvent('arrange', this, formData) != false){
31902             this.xhr.send(formData);
31903         };
31904     },
31905     
31906     xhrOnLoad : function(xhr)
31907     {
31908         if(this.loadMask){
31909             this.maskEl.unmask();
31910         }
31911         
31912         if (xhr.readyState !== 4) {
31913             this.fireEvent('exception', this, xhr);
31914             return;
31915         }
31916
31917         var response = Roo.decode(xhr.responseText);
31918         
31919         if(!response.success){
31920             this.fireEvent('exception', this, xhr);
31921             return;
31922         }
31923         
31924         var response = Roo.decode(xhr.responseText);
31925         
31926         this.fireEvent('upload', this, response);
31927         
31928     },
31929     
31930     xhrOnError : function()
31931     {
31932         if(this.loadMask){
31933             this.maskEl.unmask();
31934         }
31935         
31936         Roo.log('xhr on error');
31937         
31938         var response = Roo.decode(xhr.responseText);
31939           
31940         Roo.log(response);
31941         
31942     },
31943     
31944     prepare : function(file)
31945     {   
31946         if(this.loadMask){
31947             this.maskEl.mask(this.loadingText);
31948         }
31949         
31950         this.file = false;
31951         this.exif = {};
31952         
31953         if(typeof(file) === 'string'){
31954             this.loadCanvas(file);
31955             return;
31956         }
31957         
31958         if(!file || !this.urlAPI){
31959             return;
31960         }
31961         
31962         this.file = file;
31963         this.cropType = file.type;
31964         
31965         var _this = this;
31966         
31967         if(this.fireEvent('prepare', this, this.file) != false){
31968             
31969             var reader = new FileReader();
31970             
31971             reader.onload = function (e) {
31972                 if (e.target.error) {
31973                     Roo.log(e.target.error);
31974                     return;
31975                 }
31976                 
31977                 var buffer = e.target.result,
31978                     dataView = new DataView(buffer),
31979                     offset = 2,
31980                     maxOffset = dataView.byteLength - 4,
31981                     markerBytes,
31982                     markerLength;
31983                 
31984                 if (dataView.getUint16(0) === 0xffd8) {
31985                     while (offset < maxOffset) {
31986                         markerBytes = dataView.getUint16(offset);
31987                         
31988                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31989                             markerLength = dataView.getUint16(offset + 2) + 2;
31990                             if (offset + markerLength > dataView.byteLength) {
31991                                 Roo.log('Invalid meta data: Invalid segment size.');
31992                                 break;
31993                             }
31994                             
31995                             if(markerBytes == 0xffe1){
31996                                 _this.parseExifData(
31997                                     dataView,
31998                                     offset,
31999                                     markerLength
32000                                 );
32001                             }
32002                             
32003                             offset += markerLength;
32004                             
32005                             continue;
32006                         }
32007                         
32008                         break;
32009                     }
32010                     
32011                 }
32012                 
32013                 var url = _this.urlAPI.createObjectURL(_this.file);
32014                 
32015                 _this.loadCanvas(url);
32016                 
32017                 return;
32018             }
32019             
32020             reader.readAsArrayBuffer(this.file);
32021             
32022         }
32023         
32024     },
32025     
32026     parseExifData : function(dataView, offset, length)
32027     {
32028         var tiffOffset = offset + 10,
32029             littleEndian,
32030             dirOffset;
32031     
32032         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32033             // No Exif data, might be XMP data instead
32034             return;
32035         }
32036         
32037         // Check for the ASCII code for "Exif" (0x45786966):
32038         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039             // No Exif data, might be XMP data instead
32040             return;
32041         }
32042         if (tiffOffset + 8 > dataView.byteLength) {
32043             Roo.log('Invalid Exif data: Invalid segment size.');
32044             return;
32045         }
32046         // Check for the two null bytes:
32047         if (dataView.getUint16(offset + 8) !== 0x0000) {
32048             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32049             return;
32050         }
32051         // Check the byte alignment:
32052         switch (dataView.getUint16(tiffOffset)) {
32053         case 0x4949:
32054             littleEndian = true;
32055             break;
32056         case 0x4D4D:
32057             littleEndian = false;
32058             break;
32059         default:
32060             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32061             return;
32062         }
32063         // Check for the TIFF tag marker (0x002A):
32064         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32065             Roo.log('Invalid Exif data: Missing TIFF marker.');
32066             return;
32067         }
32068         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32069         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32070         
32071         this.parseExifTags(
32072             dataView,
32073             tiffOffset,
32074             tiffOffset + dirOffset,
32075             littleEndian
32076         );
32077     },
32078     
32079     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32080     {
32081         var tagsNumber,
32082             dirEndOffset,
32083             i;
32084         if (dirOffset + 6 > dataView.byteLength) {
32085             Roo.log('Invalid Exif data: Invalid directory offset.');
32086             return;
32087         }
32088         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32089         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32090         if (dirEndOffset + 4 > dataView.byteLength) {
32091             Roo.log('Invalid Exif data: Invalid directory size.');
32092             return;
32093         }
32094         for (i = 0; i < tagsNumber; i += 1) {
32095             this.parseExifTag(
32096                 dataView,
32097                 tiffOffset,
32098                 dirOffset + 2 + 12 * i, // tag offset
32099                 littleEndian
32100             );
32101         }
32102         // Return the offset to the next directory:
32103         return dataView.getUint32(dirEndOffset, littleEndian);
32104     },
32105     
32106     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32107     {
32108         var tag = dataView.getUint16(offset, littleEndian);
32109         
32110         this.exif[tag] = this.getExifValue(
32111             dataView,
32112             tiffOffset,
32113             offset,
32114             dataView.getUint16(offset + 2, littleEndian), // tag type
32115             dataView.getUint32(offset + 4, littleEndian), // tag length
32116             littleEndian
32117         );
32118     },
32119     
32120     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32121     {
32122         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32123             tagSize,
32124             dataOffset,
32125             values,
32126             i,
32127             str,
32128             c;
32129     
32130         if (!tagType) {
32131             Roo.log('Invalid Exif data: Invalid tag type.');
32132             return;
32133         }
32134         
32135         tagSize = tagType.size * length;
32136         // Determine if the value is contained in the dataOffset bytes,
32137         // or if the value at the dataOffset is a pointer to the actual data:
32138         dataOffset = tagSize > 4 ?
32139                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32140         if (dataOffset + tagSize > dataView.byteLength) {
32141             Roo.log('Invalid Exif data: Invalid data offset.');
32142             return;
32143         }
32144         if (length === 1) {
32145             return tagType.getValue(dataView, dataOffset, littleEndian);
32146         }
32147         values = [];
32148         for (i = 0; i < length; i += 1) {
32149             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32150         }
32151         
32152         if (tagType.ascii) {
32153             str = '';
32154             // Concatenate the chars:
32155             for (i = 0; i < values.length; i += 1) {
32156                 c = values[i];
32157                 // Ignore the terminating NULL byte(s):
32158                 if (c === '\u0000') {
32159                     break;
32160                 }
32161                 str += c;
32162             }
32163             return str;
32164         }
32165         return values;
32166     }
32167     
32168 });
32169
32170 Roo.apply(Roo.bootstrap.UploadCropbox, {
32171     tags : {
32172         'Orientation': 0x0112
32173     },
32174     
32175     Orientation: {
32176             1: 0, //'top-left',
32177 //            2: 'top-right',
32178             3: 180, //'bottom-right',
32179 //            4: 'bottom-left',
32180 //            5: 'left-top',
32181             6: 90, //'right-top',
32182 //            7: 'right-bottom',
32183             8: 270 //'left-bottom'
32184     },
32185     
32186     exifTagTypes : {
32187         // byte, 8-bit unsigned int:
32188         1: {
32189             getValue: function (dataView, dataOffset) {
32190                 return dataView.getUint8(dataOffset);
32191             },
32192             size: 1
32193         },
32194         // ascii, 8-bit byte:
32195         2: {
32196             getValue: function (dataView, dataOffset) {
32197                 return String.fromCharCode(dataView.getUint8(dataOffset));
32198             },
32199             size: 1,
32200             ascii: true
32201         },
32202         // short, 16 bit int:
32203         3: {
32204             getValue: function (dataView, dataOffset, littleEndian) {
32205                 return dataView.getUint16(dataOffset, littleEndian);
32206             },
32207             size: 2
32208         },
32209         // long, 32 bit int:
32210         4: {
32211             getValue: function (dataView, dataOffset, littleEndian) {
32212                 return dataView.getUint32(dataOffset, littleEndian);
32213             },
32214             size: 4
32215         },
32216         // rational = two long values, first is numerator, second is denominator:
32217         5: {
32218             getValue: function (dataView, dataOffset, littleEndian) {
32219                 return dataView.getUint32(dataOffset, littleEndian) /
32220                     dataView.getUint32(dataOffset + 4, littleEndian);
32221             },
32222             size: 8
32223         },
32224         // slong, 32 bit signed int:
32225         9: {
32226             getValue: function (dataView, dataOffset, littleEndian) {
32227                 return dataView.getInt32(dataOffset, littleEndian);
32228             },
32229             size: 4
32230         },
32231         // srational, two slongs, first is numerator, second is denominator:
32232         10: {
32233             getValue: function (dataView, dataOffset, littleEndian) {
32234                 return dataView.getInt32(dataOffset, littleEndian) /
32235                     dataView.getInt32(dataOffset + 4, littleEndian);
32236             },
32237             size: 8
32238         }
32239     },
32240     
32241     footer : {
32242         STANDARD : [
32243             {
32244                 tag : 'div',
32245                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32246                 action : 'rotate-left',
32247                 cn : [
32248                     {
32249                         tag : 'button',
32250                         cls : 'btn btn-default',
32251                         html : '<i class="fa fa-undo"></i>'
32252                     }
32253                 ]
32254             },
32255             {
32256                 tag : 'div',
32257                 cls : 'btn-group roo-upload-cropbox-picture',
32258                 action : 'picture',
32259                 cn : [
32260                     {
32261                         tag : 'button',
32262                         cls : 'btn btn-default',
32263                         html : '<i class="fa fa-picture-o"></i>'
32264                     }
32265                 ]
32266             },
32267             {
32268                 tag : 'div',
32269                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32270                 action : 'rotate-right',
32271                 cn : [
32272                     {
32273                         tag : 'button',
32274                         cls : 'btn btn-default',
32275                         html : '<i class="fa fa-repeat"></i>'
32276                     }
32277                 ]
32278             }
32279         ],
32280         DOCUMENT : [
32281             {
32282                 tag : 'div',
32283                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32284                 action : 'rotate-left',
32285                 cn : [
32286                     {
32287                         tag : 'button',
32288                         cls : 'btn btn-default',
32289                         html : '<i class="fa fa-undo"></i>'
32290                     }
32291                 ]
32292             },
32293             {
32294                 tag : 'div',
32295                 cls : 'btn-group roo-upload-cropbox-download',
32296                 action : 'download',
32297                 cn : [
32298                     {
32299                         tag : 'button',
32300                         cls : 'btn btn-default',
32301                         html : '<i class="fa fa-download"></i>'
32302                     }
32303                 ]
32304             },
32305             {
32306                 tag : 'div',
32307                 cls : 'btn-group roo-upload-cropbox-crop',
32308                 action : 'crop',
32309                 cn : [
32310                     {
32311                         tag : 'button',
32312                         cls : 'btn btn-default',
32313                         html : '<i class="fa fa-crop"></i>'
32314                     }
32315                 ]
32316             },
32317             {
32318                 tag : 'div',
32319                 cls : 'btn-group roo-upload-cropbox-trash',
32320                 action : 'trash',
32321                 cn : [
32322                     {
32323                         tag : 'button',
32324                         cls : 'btn btn-default',
32325                         html : '<i class="fa fa-trash"></i>'
32326                     }
32327                 ]
32328             },
32329             {
32330                 tag : 'div',
32331                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32332                 action : 'rotate-right',
32333                 cn : [
32334                     {
32335                         tag : 'button',
32336                         cls : 'btn btn-default',
32337                         html : '<i class="fa fa-repeat"></i>'
32338                     }
32339                 ]
32340             }
32341         ],
32342         ROTATOR : [
32343             {
32344                 tag : 'div',
32345                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32346                 action : 'rotate-left',
32347                 cn : [
32348                     {
32349                         tag : 'button',
32350                         cls : 'btn btn-default',
32351                         html : '<i class="fa fa-undo"></i>'
32352                     }
32353                 ]
32354             },
32355             {
32356                 tag : 'div',
32357                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32358                 action : 'rotate-right',
32359                 cn : [
32360                     {
32361                         tag : 'button',
32362                         cls : 'btn btn-default',
32363                         html : '<i class="fa fa-repeat"></i>'
32364                     }
32365                 ]
32366             }
32367         ]
32368     }
32369 });
32370
32371 /*
32372 * Licence: LGPL
32373 */
32374
32375 /**
32376  * @class Roo.bootstrap.DocumentManager
32377  * @extends Roo.bootstrap.Component
32378  * Bootstrap DocumentManager class
32379  * @cfg {String} paramName default 'imageUpload'
32380  * @cfg {String} toolTipName default 'filename'
32381  * @cfg {String} method default POST
32382  * @cfg {String} url action url
32383  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32384  * @cfg {Boolean} multiple multiple upload default true
32385  * @cfg {Number} thumbSize default 300
32386  * @cfg {String} fieldLabel
32387  * @cfg {Number} labelWidth default 4
32388  * @cfg {String} labelAlign (left|top) default left
32389  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32390 * @cfg {Number} labellg set the width of label (1-12)
32391  * @cfg {Number} labelmd set the width of label (1-12)
32392  * @cfg {Number} labelsm set the width of label (1-12)
32393  * @cfg {Number} labelxs set the width of label (1-12)
32394  * 
32395  * @constructor
32396  * Create a new DocumentManager
32397  * @param {Object} config The config object
32398  */
32399
32400 Roo.bootstrap.DocumentManager = function(config){
32401     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32402     
32403     this.files = [];
32404     this.delegates = [];
32405     
32406     this.addEvents({
32407         /**
32408          * @event initial
32409          * Fire when initial the DocumentManager
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          */
32412         "initial" : true,
32413         /**
32414          * @event inspect
32415          * inspect selected file
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          * @param {File} file
32418          */
32419         "inspect" : true,
32420         /**
32421          * @event exception
32422          * Fire when xhr load exception
32423          * @param {Roo.bootstrap.DocumentManager} this
32424          * @param {XMLHttpRequest} xhr
32425          */
32426         "exception" : true,
32427         /**
32428          * @event afterupload
32429          * Fire when xhr load exception
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          * @param {XMLHttpRequest} xhr
32432          */
32433         "afterupload" : true,
32434         /**
32435          * @event prepare
32436          * prepare the form data
32437          * @param {Roo.bootstrap.DocumentManager} this
32438          * @param {Object} formData
32439          */
32440         "prepare" : true,
32441         /**
32442          * @event remove
32443          * Fire when remove the file
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {Object} file
32446          */
32447         "remove" : true,
32448         /**
32449          * @event refresh
32450          * Fire after refresh the file
32451          * @param {Roo.bootstrap.DocumentManager} this
32452          */
32453         "refresh" : true,
32454         /**
32455          * @event click
32456          * Fire after click the image
32457          * @param {Roo.bootstrap.DocumentManager} this
32458          * @param {Object} file
32459          */
32460         "click" : true,
32461         /**
32462          * @event edit
32463          * Fire when upload a image and editable set to true
32464          * @param {Roo.bootstrap.DocumentManager} this
32465          * @param {Object} file
32466          */
32467         "edit" : true,
32468         /**
32469          * @event beforeselectfile
32470          * Fire before select file
32471          * @param {Roo.bootstrap.DocumentManager} this
32472          */
32473         "beforeselectfile" : true,
32474         /**
32475          * @event process
32476          * Fire before process file
32477          * @param {Roo.bootstrap.DocumentManager} this
32478          * @param {Object} file
32479          */
32480         "process" : true,
32481         /**
32482          * @event previewrendered
32483          * Fire when preview rendered
32484          * @param {Roo.bootstrap.DocumentManager} this
32485          * @param {Object} file
32486          */
32487         "previewrendered" : true,
32488         /**
32489          */
32490         "previewResize" : true
32491         
32492     });
32493 };
32494
32495 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32496     
32497     boxes : 0,
32498     inputName : '',
32499     thumbSize : 300,
32500     multiple : true,
32501     files : false,
32502     method : 'POST',
32503     url : '',
32504     paramName : 'imageUpload',
32505     toolTipName : 'filename',
32506     fieldLabel : '',
32507     labelWidth : 4,
32508     labelAlign : 'left',
32509     editable : true,
32510     delegates : false,
32511     xhr : false, 
32512     
32513     labellg : 0,
32514     labelmd : 0,
32515     labelsm : 0,
32516     labelxs : 0,
32517     
32518     getAutoCreate : function()
32519     {   
32520         var managerWidget = {
32521             tag : 'div',
32522             cls : 'roo-document-manager',
32523             cn : [
32524                 {
32525                     tag : 'input',
32526                     cls : 'roo-document-manager-selector',
32527                     type : 'file'
32528                 },
32529                 {
32530                     tag : 'div',
32531                     cls : 'roo-document-manager-uploader',
32532                     cn : [
32533                         {
32534                             tag : 'div',
32535                             cls : 'roo-document-manager-upload-btn',
32536                             html : '<i class="fa fa-plus"></i>'
32537                         }
32538                     ]
32539                     
32540                 }
32541             ]
32542         };
32543         
32544         var content = [
32545             {
32546                 tag : 'div',
32547                 cls : 'column col-md-12',
32548                 cn : managerWidget
32549             }
32550         ];
32551         
32552         if(this.fieldLabel.length){
32553             
32554             content = [
32555                 {
32556                     tag : 'div',
32557                     cls : 'column col-md-12',
32558                     html : this.fieldLabel
32559                 },
32560                 {
32561                     tag : 'div',
32562                     cls : 'column col-md-12',
32563                     cn : managerWidget
32564                 }
32565             ];
32566
32567             if(this.labelAlign == 'left'){
32568                 content = [
32569                     {
32570                         tag : 'div',
32571                         cls : 'column',
32572                         html : this.fieldLabel
32573                     },
32574                     {
32575                         tag : 'div',
32576                         cls : 'column',
32577                         cn : managerWidget
32578                     }
32579                 ];
32580                 
32581                 if(this.labelWidth > 12){
32582                     content[0].style = "width: " + this.labelWidth + 'px';
32583                 }
32584
32585                 if(this.labelWidth < 13 && this.labelmd == 0){
32586                     this.labelmd = this.labelWidth;
32587                 }
32588
32589                 if(this.labellg > 0){
32590                     content[0].cls += ' col-lg-' + this.labellg;
32591                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32592                 }
32593
32594                 if(this.labelmd > 0){
32595                     content[0].cls += ' col-md-' + this.labelmd;
32596                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32597                 }
32598
32599                 if(this.labelsm > 0){
32600                     content[0].cls += ' col-sm-' + this.labelsm;
32601                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32602                 }
32603
32604                 if(this.labelxs > 0){
32605                     content[0].cls += ' col-xs-' + this.labelxs;
32606                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32607                 }
32608                 
32609             }
32610         }
32611         
32612         var cfg = {
32613             tag : 'div',
32614             cls : 'row clearfix',
32615             cn : content
32616         };
32617         
32618         return cfg;
32619         
32620     },
32621     
32622     initEvents : function()
32623     {
32624         this.managerEl = this.el.select('.roo-document-manager', true).first();
32625         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32626         
32627         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32628         this.selectorEl.hide();
32629         
32630         if(this.multiple){
32631             this.selectorEl.attr('multiple', 'multiple');
32632         }
32633         
32634         this.selectorEl.on('change', this.onFileSelected, this);
32635         
32636         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32637         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32638         
32639         this.uploader.on('click', this.onUploaderClick, this);
32640         
32641         this.renderProgressDialog();
32642         
32643         var _this = this;
32644         
32645         window.addEventListener("resize", function() { _this.refresh(); } );
32646         
32647         this.fireEvent('initial', this);
32648     },
32649     
32650     renderProgressDialog : function()
32651     {
32652         var _this = this;
32653         
32654         this.progressDialog = new Roo.bootstrap.Modal({
32655             cls : 'roo-document-manager-progress-dialog',
32656             allow_close : false,
32657             animate : false,
32658             title : '',
32659             buttons : [
32660                 {
32661                     name  :'cancel',
32662                     weight : 'danger',
32663                     html : 'Cancel'
32664                 }
32665             ], 
32666             listeners : { 
32667                 btnclick : function() {
32668                     _this.uploadCancel();
32669                     this.hide();
32670                 }
32671             }
32672         });
32673          
32674         this.progressDialog.render(Roo.get(document.body));
32675          
32676         this.progress = new Roo.bootstrap.Progress({
32677             cls : 'roo-document-manager-progress',
32678             active : true,
32679             striped : true
32680         });
32681         
32682         this.progress.render(this.progressDialog.getChildContainer());
32683         
32684         this.progressBar = new Roo.bootstrap.ProgressBar({
32685             cls : 'roo-document-manager-progress-bar',
32686             aria_valuenow : 0,
32687             aria_valuemin : 0,
32688             aria_valuemax : 12,
32689             panel : 'success'
32690         });
32691         
32692         this.progressBar.render(this.progress.getChildContainer());
32693     },
32694     
32695     onUploaderClick : function(e)
32696     {
32697         e.preventDefault();
32698      
32699         if(this.fireEvent('beforeselectfile', this) != false){
32700             this.selectorEl.dom.click();
32701         }
32702         
32703     },
32704     
32705     onFileSelected : function(e)
32706     {
32707         e.preventDefault();
32708         
32709         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32710             return;
32711         }
32712         
32713         Roo.each(this.selectorEl.dom.files, function(file){
32714             if(this.fireEvent('inspect', this, file) != false){
32715                 this.files.push(file);
32716             }
32717         }, this);
32718         
32719         this.queue();
32720         
32721     },
32722     
32723     queue : function()
32724     {
32725         this.selectorEl.dom.value = '';
32726         
32727         if(!this.files || !this.files.length){
32728             return;
32729         }
32730         
32731         if(this.boxes > 0 && this.files.length > this.boxes){
32732             this.files = this.files.slice(0, this.boxes);
32733         }
32734         
32735         this.uploader.show();
32736         
32737         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32738             this.uploader.hide();
32739         }
32740         
32741         var _this = this;
32742         
32743         var files = [];
32744         
32745         var docs = [];
32746         
32747         Roo.each(this.files, function(file){
32748             
32749             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32750                 var f = this.renderPreview(file);
32751                 files.push(f);
32752                 return;
32753             }
32754             
32755             if(file.type.indexOf('image') != -1){
32756                 this.delegates.push(
32757                     (function(){
32758                         _this.process(file);
32759                     }).createDelegate(this)
32760                 );
32761         
32762                 return;
32763             }
32764             
32765             docs.push(
32766                 (function(){
32767                     _this.process(file);
32768                 }).createDelegate(this)
32769             );
32770             
32771         }, this);
32772         
32773         this.files = files;
32774         
32775         this.delegates = this.delegates.concat(docs);
32776         
32777         if(!this.delegates.length){
32778             this.refresh();
32779             return;
32780         }
32781         
32782         this.progressBar.aria_valuemax = this.delegates.length;
32783         
32784         this.arrange();
32785         
32786         return;
32787     },
32788     
32789     arrange : function()
32790     {
32791         if(!this.delegates.length){
32792             this.progressDialog.hide();
32793             this.refresh();
32794             return;
32795         }
32796         
32797         var delegate = this.delegates.shift();
32798         
32799         this.progressDialog.show();
32800         
32801         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32802         
32803         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32804         
32805         delegate();
32806     },
32807     
32808     refresh : function()
32809     {
32810         this.uploader.show();
32811         
32812         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32813             this.uploader.hide();
32814         }
32815         
32816         Roo.isTouch ? this.closable(false) : this.closable(true);
32817         
32818         this.fireEvent('refresh', this);
32819     },
32820     
32821     onRemove : function(e, el, o)
32822     {
32823         e.preventDefault();
32824         
32825         this.fireEvent('remove', this, o);
32826         
32827     },
32828     
32829     remove : function(o)
32830     {
32831         var files = [];
32832         
32833         Roo.each(this.files, function(file){
32834             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32835                 files.push(file);
32836                 return;
32837             }
32838
32839             o.target.remove();
32840
32841         }, this);
32842         
32843         this.files = files;
32844         
32845         this.refresh();
32846     },
32847     
32848     clear : function()
32849     {
32850         Roo.each(this.files, function(file){
32851             if(!file.target){
32852                 return;
32853             }
32854             
32855             file.target.remove();
32856
32857         }, this);
32858         
32859         this.files = [];
32860         
32861         this.refresh();
32862     },
32863     
32864     onClick : function(e, el, o)
32865     {
32866         e.preventDefault();
32867         
32868         this.fireEvent('click', this, o);
32869         
32870     },
32871     
32872     closable : function(closable)
32873     {
32874         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32875             
32876             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32877             
32878             if(closable){
32879                 el.show();
32880                 return;
32881             }
32882             
32883             el.hide();
32884             
32885         }, this);
32886     },
32887     
32888     xhrOnLoad : function(xhr)
32889     {
32890         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32891             el.remove();
32892         }, this);
32893         
32894         if (xhr.readyState !== 4) {
32895             this.arrange();
32896             this.fireEvent('exception', this, xhr);
32897             return;
32898         }
32899
32900         var response = Roo.decode(xhr.responseText);
32901         
32902         if(!response.success){
32903             this.arrange();
32904             this.fireEvent('exception', this, xhr);
32905             return;
32906         }
32907         
32908         var file = this.renderPreview(response.data);
32909         
32910         this.files.push(file);
32911         
32912         this.arrange();
32913         
32914         this.fireEvent('afterupload', this, xhr);
32915         
32916     },
32917     
32918     xhrOnError : function(xhr)
32919     {
32920         Roo.log('xhr on error');
32921         
32922         var response = Roo.decode(xhr.responseText);
32923           
32924         Roo.log(response);
32925         
32926         this.arrange();
32927     },
32928     
32929     process : function(file)
32930     {
32931         if(this.fireEvent('process', this, file) !== false){
32932             if(this.editable && file.type.indexOf('image') != -1){
32933                 this.fireEvent('edit', this, file);
32934                 return;
32935             }
32936
32937             this.uploadStart(file, false);
32938
32939             return;
32940         }
32941         
32942     },
32943     
32944     uploadStart : function(file, crop)
32945     {
32946         this.xhr = new XMLHttpRequest();
32947         
32948         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32949             this.arrange();
32950             return;
32951         }
32952         
32953         file.xhr = this.xhr;
32954             
32955         this.managerEl.createChild({
32956             tag : 'div',
32957             cls : 'roo-document-manager-loading',
32958             cn : [
32959                 {
32960                     tag : 'div',
32961                     tooltip : file.name,
32962                     cls : 'roo-document-manager-thumb',
32963                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32964                 }
32965             ]
32966
32967         });
32968
32969         this.xhr.open(this.method, this.url, true);
32970         
32971         var headers = {
32972             "Accept": "application/json",
32973             "Cache-Control": "no-cache",
32974             "X-Requested-With": "XMLHttpRequest"
32975         };
32976         
32977         for (var headerName in headers) {
32978             var headerValue = headers[headerName];
32979             if (headerValue) {
32980                 this.xhr.setRequestHeader(headerName, headerValue);
32981             }
32982         }
32983         
32984         var _this = this;
32985         
32986         this.xhr.onload = function()
32987         {
32988             _this.xhrOnLoad(_this.xhr);
32989         }
32990         
32991         this.xhr.onerror = function()
32992         {
32993             _this.xhrOnError(_this.xhr);
32994         }
32995         
32996         var formData = new FormData();
32997
32998         formData.append('returnHTML', 'NO');
32999         
33000         if(crop){
33001             formData.append('crop', crop);
33002         }
33003         
33004         formData.append(this.paramName, file, file.name);
33005         
33006         var options = {
33007             file : file, 
33008             manually : false
33009         };
33010         
33011         if(this.fireEvent('prepare', this, formData, options) != false){
33012             
33013             if(options.manually){
33014                 return;
33015             }
33016             
33017             this.xhr.send(formData);
33018             return;
33019         };
33020         
33021         this.uploadCancel();
33022     },
33023     
33024     uploadCancel : function()
33025     {
33026         if (this.xhr) {
33027             this.xhr.abort();
33028         }
33029         
33030         this.delegates = [];
33031         
33032         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33033             el.remove();
33034         }, this);
33035         
33036         this.arrange();
33037     },
33038     
33039     renderPreview : function(file)
33040     {
33041         if(typeof(file.target) != 'undefined' && file.target){
33042             return file;
33043         }
33044         
33045         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33046         
33047         var previewEl = this.managerEl.createChild({
33048             tag : 'div',
33049             cls : 'roo-document-manager-preview',
33050             cn : [
33051                 {
33052                     tag : 'div',
33053                     tooltip : file[this.toolTipName],
33054                     cls : 'roo-document-manager-thumb',
33055                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33056                 },
33057                 {
33058                     tag : 'button',
33059                     cls : 'close',
33060                     html : '<i class="fa fa-times-circle"></i>'
33061                 }
33062             ]
33063         });
33064
33065         var close = previewEl.select('button.close', true).first();
33066
33067         close.on('click', this.onRemove, this, file);
33068
33069         file.target = previewEl;
33070
33071         var image = previewEl.select('img', true).first();
33072         
33073         var _this = this;
33074         
33075         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33076         
33077         image.on('click', this.onClick, this, file);
33078         
33079         this.fireEvent('previewrendered', this, file);
33080         
33081         return file;
33082         
33083     },
33084     
33085     onPreviewLoad : function(file, image)
33086     {
33087         if(typeof(file.target) == 'undefined' || !file.target){
33088             return;
33089         }
33090         
33091         var width = image.dom.naturalWidth || image.dom.width;
33092         var height = image.dom.naturalHeight || image.dom.height;
33093         
33094         if(!this.previewResize) {
33095             return;
33096         }
33097         
33098         if(width > height){
33099             file.target.addClass('wide');
33100             return;
33101         }
33102         
33103         file.target.addClass('tall');
33104         return;
33105         
33106     },
33107     
33108     uploadFromSource : function(file, crop)
33109     {
33110         this.xhr = new XMLHttpRequest();
33111         
33112         this.managerEl.createChild({
33113             tag : 'div',
33114             cls : 'roo-document-manager-loading',
33115             cn : [
33116                 {
33117                     tag : 'div',
33118                     tooltip : file.name,
33119                     cls : 'roo-document-manager-thumb',
33120                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33121                 }
33122             ]
33123
33124         });
33125
33126         this.xhr.open(this.method, this.url, true);
33127         
33128         var headers = {
33129             "Accept": "application/json",
33130             "Cache-Control": "no-cache",
33131             "X-Requested-With": "XMLHttpRequest"
33132         };
33133         
33134         for (var headerName in headers) {
33135             var headerValue = headers[headerName];
33136             if (headerValue) {
33137                 this.xhr.setRequestHeader(headerName, headerValue);
33138             }
33139         }
33140         
33141         var _this = this;
33142         
33143         this.xhr.onload = function()
33144         {
33145             _this.xhrOnLoad(_this.xhr);
33146         }
33147         
33148         this.xhr.onerror = function()
33149         {
33150             _this.xhrOnError(_this.xhr);
33151         }
33152         
33153         var formData = new FormData();
33154
33155         formData.append('returnHTML', 'NO');
33156         
33157         formData.append('crop', crop);
33158         
33159         if(typeof(file.filename) != 'undefined'){
33160             formData.append('filename', file.filename);
33161         }
33162         
33163         if(typeof(file.mimetype) != 'undefined'){
33164             formData.append('mimetype', file.mimetype);
33165         }
33166         
33167         Roo.log(formData);
33168         
33169         if(this.fireEvent('prepare', this, formData) != false){
33170             this.xhr.send(formData);
33171         };
33172     }
33173 });
33174
33175 /*
33176 * Licence: LGPL
33177 */
33178
33179 /**
33180  * @class Roo.bootstrap.DocumentViewer
33181  * @extends Roo.bootstrap.Component
33182  * Bootstrap DocumentViewer class
33183  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33184  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33185  * 
33186  * @constructor
33187  * Create a new DocumentViewer
33188  * @param {Object} config The config object
33189  */
33190
33191 Roo.bootstrap.DocumentViewer = function(config){
33192     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33193     
33194     this.addEvents({
33195         /**
33196          * @event initial
33197          * Fire after initEvent
33198          * @param {Roo.bootstrap.DocumentViewer} this
33199          */
33200         "initial" : true,
33201         /**
33202          * @event click
33203          * Fire after click
33204          * @param {Roo.bootstrap.DocumentViewer} this
33205          */
33206         "click" : true,
33207         /**
33208          * @event download
33209          * Fire after download button
33210          * @param {Roo.bootstrap.DocumentViewer} this
33211          */
33212         "download" : true,
33213         /**
33214          * @event trash
33215          * Fire after trash button
33216          * @param {Roo.bootstrap.DocumentViewer} this
33217          */
33218         "trash" : true
33219         
33220     });
33221 };
33222
33223 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33224     
33225     showDownload : true,
33226     
33227     showTrash : true,
33228     
33229     getAutoCreate : function()
33230     {
33231         var cfg = {
33232             tag : 'div',
33233             cls : 'roo-document-viewer',
33234             cn : [
33235                 {
33236                     tag : 'div',
33237                     cls : 'roo-document-viewer-body',
33238                     cn : [
33239                         {
33240                             tag : 'div',
33241                             cls : 'roo-document-viewer-thumb',
33242                             cn : [
33243                                 {
33244                                     tag : 'img',
33245                                     cls : 'roo-document-viewer-image'
33246                                 }
33247                             ]
33248                         }
33249                     ]
33250                 },
33251                 {
33252                     tag : 'div',
33253                     cls : 'roo-document-viewer-footer',
33254                     cn : {
33255                         tag : 'div',
33256                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33257                         cn : [
33258                             {
33259                                 tag : 'div',
33260                                 cls : 'btn-group roo-document-viewer-download',
33261                                 cn : [
33262                                     {
33263                                         tag : 'button',
33264                                         cls : 'btn btn-default',
33265                                         html : '<i class="fa fa-download"></i>'
33266                                     }
33267                                 ]
33268                             },
33269                             {
33270                                 tag : 'div',
33271                                 cls : 'btn-group roo-document-viewer-trash',
33272                                 cn : [
33273                                     {
33274                                         tag : 'button',
33275                                         cls : 'btn btn-default',
33276                                         html : '<i class="fa fa-trash"></i>'
33277                                     }
33278                                 ]
33279                             }
33280                         ]
33281                     }
33282                 }
33283             ]
33284         };
33285         
33286         return cfg;
33287     },
33288     
33289     initEvents : function()
33290     {
33291         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33292         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33293         
33294         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33295         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33296         
33297         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33298         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33299         
33300         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33301         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33302         
33303         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33304         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33305         
33306         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33307         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33308         
33309         this.bodyEl.on('click', this.onClick, this);
33310         this.downloadBtn.on('click', this.onDownload, this);
33311         this.trashBtn.on('click', this.onTrash, this);
33312         
33313         this.downloadBtn.hide();
33314         this.trashBtn.hide();
33315         
33316         if(this.showDownload){
33317             this.downloadBtn.show();
33318         }
33319         
33320         if(this.showTrash){
33321             this.trashBtn.show();
33322         }
33323         
33324         if(!this.showDownload && !this.showTrash) {
33325             this.footerEl.hide();
33326         }
33327         
33328     },
33329     
33330     initial : function()
33331     {
33332         this.fireEvent('initial', this);
33333         
33334     },
33335     
33336     onClick : function(e)
33337     {
33338         e.preventDefault();
33339         
33340         this.fireEvent('click', this);
33341     },
33342     
33343     onDownload : function(e)
33344     {
33345         e.preventDefault();
33346         
33347         this.fireEvent('download', this);
33348     },
33349     
33350     onTrash : function(e)
33351     {
33352         e.preventDefault();
33353         
33354         this.fireEvent('trash', this);
33355     }
33356     
33357 });
33358 /*
33359  * - LGPL
33360  *
33361  * FieldLabel
33362  * 
33363  */
33364
33365 /**
33366  * @class Roo.bootstrap.form.FieldLabel
33367  * @extends Roo.bootstrap.Component
33368  * Bootstrap FieldLabel class
33369  * @cfg {String} html contents of the element
33370  * @cfg {String} tag tag of the element default label
33371  * @cfg {String} cls class of the element
33372  * @cfg {String} target label target 
33373  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33374  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33375  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33376  * @cfg {String} iconTooltip default "This field is required"
33377  * @cfg {String} indicatorpos (left|right) default left
33378  * 
33379  * @constructor
33380  * Create a new FieldLabel
33381  * @param {Object} config The config object
33382  */
33383
33384 Roo.bootstrap.form.FieldLabel = function(config){
33385     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33386     
33387     this.addEvents({
33388             /**
33389              * @event invalid
33390              * Fires after the field has been marked as invalid.
33391              * @param {Roo.form.FieldLabel} this
33392              * @param {String} msg The validation message
33393              */
33394             invalid : true,
33395             /**
33396              * @event valid
33397              * Fires after the field has been validated with no errors.
33398              * @param {Roo.form.FieldLabel} this
33399              */
33400             valid : true
33401         });
33402 };
33403
33404 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33405     
33406     tag: 'label',
33407     cls: '',
33408     html: '',
33409     target: '',
33410     allowBlank : true,
33411     invalidClass : 'has-warning',
33412     validClass : 'has-success',
33413     iconTooltip : 'This field is required',
33414     indicatorpos : 'left',
33415     
33416     getAutoCreate : function(){
33417         
33418         var cls = "";
33419         if (!this.allowBlank) {
33420             cls  = "visible";
33421         }
33422         
33423         var cfg = {
33424             tag : this.tag,
33425             cls : 'roo-bootstrap-field-label ' + this.cls,
33426             for : this.target,
33427             cn : [
33428                 {
33429                     tag : 'i',
33430                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33431                     tooltip : this.iconTooltip
33432                 },
33433                 {
33434                     tag : 'span',
33435                     html : this.html
33436                 }
33437             ] 
33438         };
33439         
33440         if(this.indicatorpos == 'right'){
33441             var cfg = {
33442                 tag : this.tag,
33443                 cls : 'roo-bootstrap-field-label ' + this.cls,
33444                 for : this.target,
33445                 cn : [
33446                     {
33447                         tag : 'span',
33448                         html : this.html
33449                     },
33450                     {
33451                         tag : 'i',
33452                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33453                         tooltip : this.iconTooltip
33454                     }
33455                 ] 
33456             };
33457         }
33458         
33459         return cfg;
33460     },
33461     
33462     initEvents: function() 
33463     {
33464         Roo.bootstrap.Element.superclass.initEvents.call(this);
33465         
33466         this.indicator = this.indicatorEl();
33467         
33468         if(this.indicator){
33469             this.indicator.removeClass('visible');
33470             this.indicator.addClass('invisible');
33471         }
33472         
33473         Roo.bootstrap.form.FieldLabel.register(this);
33474     },
33475     
33476     indicatorEl : function()
33477     {
33478         var indicator = this.el.select('i.roo-required-indicator',true).first();
33479         
33480         if(!indicator){
33481             return false;
33482         }
33483         
33484         return indicator;
33485         
33486     },
33487     
33488     /**
33489      * Mark this field as valid
33490      */
33491     markValid : function()
33492     {
33493         if(this.indicator){
33494             this.indicator.removeClass('visible');
33495             this.indicator.addClass('invisible');
33496         }
33497         if (Roo.bootstrap.version == 3) {
33498             this.el.removeClass(this.invalidClass);
33499             this.el.addClass(this.validClass);
33500         } else {
33501             this.el.removeClass('is-invalid');
33502             this.el.addClass('is-valid');
33503         }
33504         
33505         
33506         this.fireEvent('valid', this);
33507     },
33508     
33509     /**
33510      * Mark this field as invalid
33511      * @param {String} msg The validation message
33512      */
33513     markInvalid : function(msg)
33514     {
33515         if(this.indicator){
33516             this.indicator.removeClass('invisible');
33517             this.indicator.addClass('visible');
33518         }
33519           if (Roo.bootstrap.version == 3) {
33520             this.el.removeClass(this.validClass);
33521             this.el.addClass(this.invalidClass);
33522         } else {
33523             this.el.removeClass('is-valid');
33524             this.el.addClass('is-invalid');
33525         }
33526         
33527         
33528         this.fireEvent('invalid', this, msg);
33529     }
33530     
33531    
33532 });
33533
33534 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33535     
33536     groups: {},
33537     
33538      /**
33539     * register a FieldLabel Group
33540     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33541     */
33542     register : function(label)
33543     {
33544         if(this.groups.hasOwnProperty(label.target)){
33545             return;
33546         }
33547      
33548         this.groups[label.target] = label;
33549         
33550     },
33551     /**
33552     * fetch a FieldLabel Group based on the target
33553     * @param {string} target
33554     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33555     */
33556     get: function(target) {
33557         if (typeof(this.groups[target]) == 'undefined') {
33558             return false;
33559         }
33560         
33561         return this.groups[target] ;
33562     }
33563 });
33564
33565  
33566
33567  /*
33568  * - LGPL
33569  *
33570  * page DateSplitField.
33571  * 
33572  */
33573
33574
33575 /**
33576  * @class Roo.bootstrap.form.DateSplitField
33577  * @extends Roo.bootstrap.Component
33578  * Bootstrap DateSplitField class
33579  * @cfg {string} fieldLabel - the label associated
33580  * @cfg {Number} labelWidth set the width of label (0-12)
33581  * @cfg {String} labelAlign (top|left)
33582  * @cfg {Boolean} dayAllowBlank (true|false) default false
33583  * @cfg {Boolean} monthAllowBlank (true|false) default false
33584  * @cfg {Boolean} yearAllowBlank (true|false) default false
33585  * @cfg {string} dayPlaceholder 
33586  * @cfg {string} monthPlaceholder
33587  * @cfg {string} yearPlaceholder
33588  * @cfg {string} dayFormat default 'd'
33589  * @cfg {string} monthFormat default 'm'
33590  * @cfg {string} yearFormat default 'Y'
33591  * @cfg {Number} labellg set the width of label (1-12)
33592  * @cfg {Number} labelmd set the width of label (1-12)
33593  * @cfg {Number} labelsm set the width of label (1-12)
33594  * @cfg {Number} labelxs set the width of label (1-12)
33595
33596  *     
33597  * @constructor
33598  * Create a new DateSplitField
33599  * @param {Object} config The config object
33600  */
33601
33602 Roo.bootstrap.form.DateSplitField = function(config){
33603     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33604     
33605     this.addEvents({
33606         // raw events
33607          /**
33608          * @event years
33609          * getting the data of years
33610          * @param {Roo.bootstrap.form.DateSplitField} this
33611          * @param {Object} years
33612          */
33613         "years" : true,
33614         /**
33615          * @event days
33616          * getting the data of days
33617          * @param {Roo.bootstrap.form.DateSplitField} this
33618          * @param {Object} days
33619          */
33620         "days" : true,
33621         /**
33622          * @event invalid
33623          * Fires after the field has been marked as invalid.
33624          * @param {Roo.form.Field} this
33625          * @param {String} msg The validation message
33626          */
33627         invalid : true,
33628        /**
33629          * @event valid
33630          * Fires after the field has been validated with no errors.
33631          * @param {Roo.form.Field} this
33632          */
33633         valid : true
33634     });
33635 };
33636
33637 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33638     
33639     fieldLabel : '',
33640     labelAlign : 'top',
33641     labelWidth : 3,
33642     dayAllowBlank : false,
33643     monthAllowBlank : false,
33644     yearAllowBlank : false,
33645     dayPlaceholder : '',
33646     monthPlaceholder : '',
33647     yearPlaceholder : '',
33648     dayFormat : 'd',
33649     monthFormat : 'm',
33650     yearFormat : 'Y',
33651     isFormField : true,
33652     labellg : 0,
33653     labelmd : 0,
33654     labelsm : 0,
33655     labelxs : 0,
33656     
33657     getAutoCreate : function()
33658     {
33659         var cfg = {
33660             tag : 'div',
33661             cls : 'row roo-date-split-field-group',
33662             cn : [
33663                 {
33664                     tag : 'input',
33665                     type : 'hidden',
33666                     cls : 'form-hidden-field roo-date-split-field-group-value',
33667                     name : this.name
33668                 }
33669             ]
33670         };
33671         
33672         var labelCls = 'col-md-12';
33673         var contentCls = 'col-md-4';
33674         
33675         if(this.fieldLabel){
33676             
33677             var label = {
33678                 tag : 'div',
33679                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33680                 cn : [
33681                     {
33682                         tag : 'label',
33683                         html : this.fieldLabel
33684                     }
33685                 ]
33686             };
33687             
33688             if(this.labelAlign == 'left'){
33689             
33690                 if(this.labelWidth > 12){
33691                     label.style = "width: " + this.labelWidth + 'px';
33692                 }
33693
33694                 if(this.labelWidth < 13 && this.labelmd == 0){
33695                     this.labelmd = this.labelWidth;
33696                 }
33697
33698                 if(this.labellg > 0){
33699                     labelCls = ' col-lg-' + this.labellg;
33700                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33701                 }
33702
33703                 if(this.labelmd > 0){
33704                     labelCls = ' col-md-' + this.labelmd;
33705                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33706                 }
33707
33708                 if(this.labelsm > 0){
33709                     labelCls = ' col-sm-' + this.labelsm;
33710                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33711                 }
33712
33713                 if(this.labelxs > 0){
33714                     labelCls = ' col-xs-' + this.labelxs;
33715                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33716                 }
33717             }
33718             
33719             label.cls += ' ' + labelCls;
33720             
33721             cfg.cn.push(label);
33722         }
33723         
33724         Roo.each(['day', 'month', 'year'], function(t){
33725             cfg.cn.push({
33726                 tag : 'div',
33727                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33728             });
33729         }, this);
33730         
33731         return cfg;
33732     },
33733     
33734     inputEl: function ()
33735     {
33736         return this.el.select('.roo-date-split-field-group-value', true).first();
33737     },
33738     
33739     onRender : function(ct, position) 
33740     {
33741         var _this = this;
33742         
33743         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33744         
33745         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33746         
33747         this.dayField = new Roo.bootstrap.form.ComboBox({
33748             allowBlank : this.dayAllowBlank,
33749             alwaysQuery : true,
33750             displayField : 'value',
33751             editable : false,
33752             fieldLabel : '',
33753             forceSelection : true,
33754             mode : 'local',
33755             placeholder : this.dayPlaceholder,
33756             selectOnFocus : true,
33757             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33758             triggerAction : 'all',
33759             typeAhead : true,
33760             valueField : 'value',
33761             store : new Roo.data.SimpleStore({
33762                 data : (function() {    
33763                     var days = [];
33764                     _this.fireEvent('days', _this, days);
33765                     return days;
33766                 })(),
33767                 fields : [ 'value' ]
33768             }),
33769             listeners : {
33770                 select : function (_self, record, index)
33771                 {
33772                     _this.setValue(_this.getValue());
33773                 }
33774             }
33775         });
33776
33777         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33778         
33779         this.monthField = new Roo.bootstrap.form.MonthField({
33780             after : '<i class=\"fa fa-calendar\"></i>',
33781             allowBlank : this.monthAllowBlank,
33782             placeholder : this.monthPlaceholder,
33783             readOnly : true,
33784             listeners : {
33785                 render : function (_self)
33786                 {
33787                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33788                         e.preventDefault();
33789                         _self.focus();
33790                     });
33791                 },
33792                 select : function (_self, oldvalue, newvalue)
33793                 {
33794                     _this.setValue(_this.getValue());
33795                 }
33796             }
33797         });
33798         
33799         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33800         
33801         this.yearField = new Roo.bootstrap.form.ComboBox({
33802             allowBlank : this.yearAllowBlank,
33803             alwaysQuery : true,
33804             displayField : 'value',
33805             editable : false,
33806             fieldLabel : '',
33807             forceSelection : true,
33808             mode : 'local',
33809             placeholder : this.yearPlaceholder,
33810             selectOnFocus : true,
33811             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33812             triggerAction : 'all',
33813             typeAhead : true,
33814             valueField : 'value',
33815             store : new Roo.data.SimpleStore({
33816                 data : (function() {
33817                     var years = [];
33818                     _this.fireEvent('years', _this, years);
33819                     return years;
33820                 })(),
33821                 fields : [ 'value' ]
33822             }),
33823             listeners : {
33824                 select : function (_self, record, index)
33825                 {
33826                     _this.setValue(_this.getValue());
33827                 }
33828             }
33829         });
33830
33831         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33832     },
33833     
33834     setValue : function(v, format)
33835     {
33836         this.inputEl.dom.value = v;
33837         
33838         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33839         
33840         var d = Date.parseDate(v, f);
33841         
33842         if(!d){
33843             this.validate();
33844             return;
33845         }
33846         
33847         this.setDay(d.format(this.dayFormat));
33848         this.setMonth(d.format(this.monthFormat));
33849         this.setYear(d.format(this.yearFormat));
33850         
33851         this.validate();
33852         
33853         return;
33854     },
33855     
33856     setDay : function(v)
33857     {
33858         this.dayField.setValue(v);
33859         this.inputEl.dom.value = this.getValue();
33860         this.validate();
33861         return;
33862     },
33863     
33864     setMonth : function(v)
33865     {
33866         this.monthField.setValue(v, true);
33867         this.inputEl.dom.value = this.getValue();
33868         this.validate();
33869         return;
33870     },
33871     
33872     setYear : function(v)
33873     {
33874         this.yearField.setValue(v);
33875         this.inputEl.dom.value = this.getValue();
33876         this.validate();
33877         return;
33878     },
33879     
33880     getDay : function()
33881     {
33882         return this.dayField.getValue();
33883     },
33884     
33885     getMonth : function()
33886     {
33887         return this.monthField.getValue();
33888     },
33889     
33890     getYear : function()
33891     {
33892         return this.yearField.getValue();
33893     },
33894     
33895     getValue : function()
33896     {
33897         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33898         
33899         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33900         
33901         return date;
33902     },
33903     
33904     reset : function()
33905     {
33906         this.setDay('');
33907         this.setMonth('');
33908         this.setYear('');
33909         this.inputEl.dom.value = '';
33910         this.validate();
33911         return;
33912     },
33913     
33914     validate : function()
33915     {
33916         var d = this.dayField.validate();
33917         var m = this.monthField.validate();
33918         var y = this.yearField.validate();
33919         
33920         var valid = true;
33921         
33922         if(
33923                 (!this.dayAllowBlank && !d) ||
33924                 (!this.monthAllowBlank && !m) ||
33925                 (!this.yearAllowBlank && !y)
33926         ){
33927             valid = false;
33928         }
33929         
33930         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33931             return valid;
33932         }
33933         
33934         if(valid){
33935             this.markValid();
33936             return valid;
33937         }
33938         
33939         this.markInvalid();
33940         
33941         return valid;
33942     },
33943     
33944     markValid : function()
33945     {
33946         
33947         var label = this.el.select('label', true).first();
33948         var icon = this.el.select('i.fa-star', true).first();
33949
33950         if(label && icon){
33951             icon.remove();
33952         }
33953         
33954         this.fireEvent('valid', this);
33955     },
33956     
33957      /**
33958      * Mark this field as invalid
33959      * @param {String} msg The validation message
33960      */
33961     markInvalid : function(msg)
33962     {
33963         
33964         var label = this.el.select('label', true).first();
33965         var icon = this.el.select('i.fa-star', true).first();
33966
33967         if(label && !icon){
33968             this.el.select('.roo-date-split-field-label', true).createChild({
33969                 tag : 'i',
33970                 cls : 'text-danger fa fa-lg fa-star',
33971                 tooltip : 'This field is required',
33972                 style : 'margin-right:5px;'
33973             }, label, true);
33974         }
33975         
33976         this.fireEvent('invalid', this, msg);
33977     },
33978     
33979     clearInvalid : function()
33980     {
33981         var label = this.el.select('label', true).first();
33982         var icon = this.el.select('i.fa-star', true).first();
33983
33984         if(label && icon){
33985             icon.remove();
33986         }
33987         
33988         this.fireEvent('valid', this);
33989     },
33990     
33991     getName: function()
33992     {
33993         return this.name;
33994     }
33995     
33996 });
33997
33998  
33999
34000 /**
34001  * @class Roo.bootstrap.LayoutMasonry
34002  * @extends Roo.bootstrap.Component
34003  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34004  * Bootstrap Layout Masonry class
34005  *
34006  * This is based on 
34007  * http://masonry.desandro.com
34008  *
34009  * The idea is to render all the bricks based on vertical width...
34010  *
34011  * The original code extends 'outlayer' - we might need to use that....
34012
34013  * @constructor
34014  * Create a new Element
34015  * @param {Object} config The config object
34016  */
34017
34018 Roo.bootstrap.LayoutMasonry = function(config){
34019     
34020     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34021     
34022     this.bricks = [];
34023     
34024     Roo.bootstrap.LayoutMasonry.register(this);
34025     
34026     this.addEvents({
34027         // raw events
34028         /**
34029          * @event layout
34030          * Fire after layout the items
34031          * @param {Roo.bootstrap.LayoutMasonry} this
34032          * @param {Roo.EventObject} e
34033          */
34034         "layout" : true
34035     });
34036     
34037 };
34038
34039 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34040     
34041     /**
34042      * @cfg {Boolean} isLayoutInstant = no animation?
34043      */   
34044     isLayoutInstant : false, // needed?
34045    
34046     /**
34047      * @cfg {Number} boxWidth  width of the columns
34048      */   
34049     boxWidth : 450,
34050     
34051       /**
34052      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34053      */   
34054     boxHeight : 0,
34055     
34056     /**
34057      * @cfg {Number} padWidth padding below box..
34058      */   
34059     padWidth : 10, 
34060     
34061     /**
34062      * @cfg {Number} gutter gutter width..
34063      */   
34064     gutter : 10,
34065     
34066      /**
34067      * @cfg {Number} maxCols maximum number of columns
34068      */   
34069     
34070     maxCols: 0,
34071     
34072     /**
34073      * @cfg {Boolean} isAutoInitial defalut true
34074      */   
34075     isAutoInitial : true, 
34076     
34077     containerWidth: 0,
34078     
34079     /**
34080      * @cfg {Boolean} isHorizontal defalut false
34081      */   
34082     isHorizontal : false, 
34083
34084     currentSize : null,
34085     
34086     tag: 'div',
34087     
34088     cls: '',
34089     
34090     bricks: null, //CompositeElement
34091     
34092     cols : 1,
34093     
34094     _isLayoutInited : false,
34095     
34096 //    isAlternative : false, // only use for vertical layout...
34097     
34098     /**
34099      * @cfg {Number} alternativePadWidth padding below box..
34100      */   
34101     alternativePadWidth : 50,
34102     
34103     selectedBrick : [],
34104     
34105     getAutoCreate : function(){
34106         
34107         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34108         
34109         var cfg = {
34110             tag: this.tag,
34111             cls: 'blog-masonary-wrapper ' + this.cls,
34112             cn : {
34113                 cls : 'mas-boxes masonary'
34114             }
34115         };
34116         
34117         return cfg;
34118     },
34119     
34120     getChildContainer: function( )
34121     {
34122         if (this.boxesEl) {
34123             return this.boxesEl;
34124         }
34125         
34126         this.boxesEl = this.el.select('.mas-boxes').first();
34127         
34128         return this.boxesEl;
34129     },
34130     
34131     
34132     initEvents : function()
34133     {
34134         var _this = this;
34135         
34136         if(this.isAutoInitial){
34137             Roo.log('hook children rendered');
34138             this.on('childrenrendered', function() {
34139                 Roo.log('children rendered');
34140                 _this.initial();
34141             } ,this);
34142         }
34143     },
34144     
34145     initial : function()
34146     {
34147         this.selectedBrick = [];
34148         
34149         this.currentSize = this.el.getBox(true);
34150         
34151         Roo.EventManager.onWindowResize(this.resize, this); 
34152
34153         if(!this.isAutoInitial){
34154             this.layout();
34155             return;
34156         }
34157         
34158         this.layout();
34159         
34160         return;
34161         //this.layout.defer(500,this);
34162         
34163     },
34164     
34165     resize : function()
34166     {
34167         var cs = this.el.getBox(true);
34168         
34169         if (
34170                 this.currentSize.width == cs.width && 
34171                 this.currentSize.x == cs.x && 
34172                 this.currentSize.height == cs.height && 
34173                 this.currentSize.y == cs.y 
34174         ) {
34175             Roo.log("no change in with or X or Y");
34176             return;
34177         }
34178         
34179         this.currentSize = cs;
34180         
34181         this.layout();
34182         
34183     },
34184     
34185     layout : function()
34186     {   
34187         this._resetLayout();
34188         
34189         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34190         
34191         this.layoutItems( isInstant );
34192       
34193         this._isLayoutInited = true;
34194         
34195         this.fireEvent('layout', this);
34196         
34197     },
34198     
34199     _resetLayout : function()
34200     {
34201         if(this.isHorizontal){
34202             this.horizontalMeasureColumns();
34203             return;
34204         }
34205         
34206         this.verticalMeasureColumns();
34207         
34208     },
34209     
34210     verticalMeasureColumns : function()
34211     {
34212         this.getContainerWidth();
34213         
34214 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34215 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34216 //            return;
34217 //        }
34218         
34219         var boxWidth = this.boxWidth + this.padWidth;
34220         
34221         if(this.containerWidth < this.boxWidth){
34222             boxWidth = this.containerWidth
34223         }
34224         
34225         var containerWidth = this.containerWidth;
34226         
34227         var cols = Math.floor(containerWidth / boxWidth);
34228         
34229         this.cols = Math.max( cols, 1 );
34230         
34231         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34232         
34233         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34234         
34235         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34236         
34237         this.colWidth = boxWidth + avail - this.padWidth;
34238         
34239         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34240         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34241     },
34242     
34243     horizontalMeasureColumns : function()
34244     {
34245         this.getContainerWidth();
34246         
34247         var boxWidth = this.boxWidth;
34248         
34249         if(this.containerWidth < boxWidth){
34250             boxWidth = this.containerWidth;
34251         }
34252         
34253         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34254         
34255         this.el.setHeight(boxWidth);
34256         
34257     },
34258     
34259     getContainerWidth : function()
34260     {
34261         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34262     },
34263     
34264     layoutItems : function( isInstant )
34265     {
34266         Roo.log(this.bricks);
34267         
34268         var items = Roo.apply([], this.bricks);
34269         
34270         if(this.isHorizontal){
34271             this._horizontalLayoutItems( items , isInstant );
34272             return;
34273         }
34274         
34275 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34276 //            this._verticalAlternativeLayoutItems( items , isInstant );
34277 //            return;
34278 //        }
34279         
34280         this._verticalLayoutItems( items , isInstant );
34281         
34282     },
34283     
34284     _verticalLayoutItems : function ( items , isInstant)
34285     {
34286         if ( !items || !items.length ) {
34287             return;
34288         }
34289         
34290         var standard = [
34291             ['xs', 'xs', 'xs', 'tall'],
34292             ['xs', 'xs', 'tall'],
34293             ['xs', 'xs', 'sm'],
34294             ['xs', 'xs', 'xs'],
34295             ['xs', 'tall'],
34296             ['xs', 'sm'],
34297             ['xs', 'xs'],
34298             ['xs'],
34299             
34300             ['sm', 'xs', 'xs'],
34301             ['sm', 'xs'],
34302             ['sm'],
34303             
34304             ['tall', 'xs', 'xs', 'xs'],
34305             ['tall', 'xs', 'xs'],
34306             ['tall', 'xs'],
34307             ['tall']
34308             
34309         ];
34310         
34311         var queue = [];
34312         
34313         var boxes = [];
34314         
34315         var box = [];
34316         
34317         Roo.each(items, function(item, k){
34318             
34319             switch (item.size) {
34320                 // these layouts take up a full box,
34321                 case 'md' :
34322                 case 'md-left' :
34323                 case 'md-right' :
34324                 case 'wide' :
34325                     
34326                     if(box.length){
34327                         boxes.push(box);
34328                         box = [];
34329                     }
34330                     
34331                     boxes.push([item]);
34332                     
34333                     break;
34334                     
34335                 case 'xs' :
34336                 case 'sm' :
34337                 case 'tall' :
34338                     
34339                     box.push(item);
34340                     
34341                     break;
34342                 default :
34343                     break;
34344                     
34345             }
34346             
34347         }, this);
34348         
34349         if(box.length){
34350             boxes.push(box);
34351             box = [];
34352         }
34353         
34354         var filterPattern = function(box, length)
34355         {
34356             if(!box.length){
34357                 return;
34358             }
34359             
34360             var match = false;
34361             
34362             var pattern = box.slice(0, length);
34363             
34364             var format = [];
34365             
34366             Roo.each(pattern, function(i){
34367                 format.push(i.size);
34368             }, this);
34369             
34370             Roo.each(standard, function(s){
34371                 
34372                 if(String(s) != String(format)){
34373                     return;
34374                 }
34375                 
34376                 match = true;
34377                 return false;
34378                 
34379             }, this);
34380             
34381             if(!match && length == 1){
34382                 return;
34383             }
34384             
34385             if(!match){
34386                 filterPattern(box, length - 1);
34387                 return;
34388             }
34389                 
34390             queue.push(pattern);
34391
34392             box = box.slice(length, box.length);
34393
34394             filterPattern(box, 4);
34395
34396             return;
34397             
34398         }
34399         
34400         Roo.each(boxes, function(box, k){
34401             
34402             if(!box.length){
34403                 return;
34404             }
34405             
34406             if(box.length == 1){
34407                 queue.push(box);
34408                 return;
34409             }
34410             
34411             filterPattern(box, 4);
34412             
34413         }, this);
34414         
34415         this._processVerticalLayoutQueue( queue, isInstant );
34416         
34417     },
34418     
34419 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34420 //    {
34421 //        if ( !items || !items.length ) {
34422 //            return;
34423 //        }
34424 //
34425 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34426 //        
34427 //    },
34428     
34429     _horizontalLayoutItems : function ( items , isInstant)
34430     {
34431         if ( !items || !items.length || items.length < 3) {
34432             return;
34433         }
34434         
34435         items.reverse();
34436         
34437         var eItems = items.slice(0, 3);
34438         
34439         items = items.slice(3, items.length);
34440         
34441         var standard = [
34442             ['xs', 'xs', 'xs', 'wide'],
34443             ['xs', 'xs', 'wide'],
34444             ['xs', 'xs', 'sm'],
34445             ['xs', 'xs', 'xs'],
34446             ['xs', 'wide'],
34447             ['xs', 'sm'],
34448             ['xs', 'xs'],
34449             ['xs'],
34450             
34451             ['sm', 'xs', 'xs'],
34452             ['sm', 'xs'],
34453             ['sm'],
34454             
34455             ['wide', 'xs', 'xs', 'xs'],
34456             ['wide', 'xs', 'xs'],
34457             ['wide', 'xs'],
34458             ['wide'],
34459             
34460             ['wide-thin']
34461         ];
34462         
34463         var queue = [];
34464         
34465         var boxes = [];
34466         
34467         var box = [];
34468         
34469         Roo.each(items, function(item, k){
34470             
34471             switch (item.size) {
34472                 case 'md' :
34473                 case 'md-left' :
34474                 case 'md-right' :
34475                 case 'tall' :
34476                     
34477                     if(box.length){
34478                         boxes.push(box);
34479                         box = [];
34480                     }
34481                     
34482                     boxes.push([item]);
34483                     
34484                     break;
34485                     
34486                 case 'xs' :
34487                 case 'sm' :
34488                 case 'wide' :
34489                 case 'wide-thin' :
34490                     
34491                     box.push(item);
34492                     
34493                     break;
34494                 default :
34495                     break;
34496                     
34497             }
34498             
34499         }, this);
34500         
34501         if(box.length){
34502             boxes.push(box);
34503             box = [];
34504         }
34505         
34506         var filterPattern = function(box, length)
34507         {
34508             if(!box.length){
34509                 return;
34510             }
34511             
34512             var match = false;
34513             
34514             var pattern = box.slice(0, length);
34515             
34516             var format = [];
34517             
34518             Roo.each(pattern, function(i){
34519                 format.push(i.size);
34520             }, this);
34521             
34522             Roo.each(standard, function(s){
34523                 
34524                 if(String(s) != String(format)){
34525                     return;
34526                 }
34527                 
34528                 match = true;
34529                 return false;
34530                 
34531             }, this);
34532             
34533             if(!match && length == 1){
34534                 return;
34535             }
34536             
34537             if(!match){
34538                 filterPattern(box, length - 1);
34539                 return;
34540             }
34541                 
34542             queue.push(pattern);
34543
34544             box = box.slice(length, box.length);
34545
34546             filterPattern(box, 4);
34547
34548             return;
34549             
34550         }
34551         
34552         Roo.each(boxes, function(box, k){
34553             
34554             if(!box.length){
34555                 return;
34556             }
34557             
34558             if(box.length == 1){
34559                 queue.push(box);
34560                 return;
34561             }
34562             
34563             filterPattern(box, 4);
34564             
34565         }, this);
34566         
34567         
34568         var prune = [];
34569         
34570         var pos = this.el.getBox(true);
34571         
34572         var minX = pos.x;
34573         
34574         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34575         
34576         var hit_end = false;
34577         
34578         Roo.each(queue, function(box){
34579             
34580             if(hit_end){
34581                 
34582                 Roo.each(box, function(b){
34583                 
34584                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34585                     b.el.hide();
34586
34587                 }, this);
34588
34589                 return;
34590             }
34591             
34592             var mx = 0;
34593             
34594             Roo.each(box, function(b){
34595                 
34596                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34597                 b.el.show();
34598
34599                 mx = Math.max(mx, b.x);
34600                 
34601             }, this);
34602             
34603             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34604             
34605             if(maxX < minX){
34606                 
34607                 Roo.each(box, function(b){
34608                 
34609                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34610                     b.el.hide();
34611                     
34612                 }, this);
34613                 
34614                 hit_end = true;
34615                 
34616                 return;
34617             }
34618             
34619             prune.push(box);
34620             
34621         }, this);
34622         
34623         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34624     },
34625     
34626     /** Sets position of item in DOM
34627     * @param {Element} item
34628     * @param {Number} x - horizontal position
34629     * @param {Number} y - vertical position
34630     * @param {Boolean} isInstant - disables transitions
34631     */
34632     _processVerticalLayoutQueue : function( queue, isInstant )
34633     {
34634         var pos = this.el.getBox(true);
34635         var x = pos.x;
34636         var y = pos.y;
34637         var maxY = [];
34638         
34639         for (var i = 0; i < this.cols; i++){
34640             maxY[i] = pos.y;
34641         }
34642         
34643         Roo.each(queue, function(box, k){
34644             
34645             var col = k % this.cols;
34646             
34647             Roo.each(box, function(b,kk){
34648                 
34649                 b.el.position('absolute');
34650                 
34651                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34652                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34653                 
34654                 if(b.size == 'md-left' || b.size == 'md-right'){
34655                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34656                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34657                 }
34658                 
34659                 b.el.setWidth(width);
34660                 b.el.setHeight(height);
34661                 // iframe?
34662                 b.el.select('iframe',true).setSize(width,height);
34663                 
34664             }, this);
34665             
34666             for (var i = 0; i < this.cols; i++){
34667                 
34668                 if(maxY[i] < maxY[col]){
34669                     col = i;
34670                     continue;
34671                 }
34672                 
34673                 col = Math.min(col, i);
34674                 
34675             }
34676             
34677             x = pos.x + col * (this.colWidth + this.padWidth);
34678             
34679             y = maxY[col];
34680             
34681             var positions = [];
34682             
34683             switch (box.length){
34684                 case 1 :
34685                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34686                     break;
34687                 case 2 :
34688                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34689                     break;
34690                 case 3 :
34691                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34692                     break;
34693                 case 4 :
34694                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34695                     break;
34696                 default :
34697                     break;
34698             }
34699             
34700             Roo.each(box, function(b,kk){
34701                 
34702                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34703                 
34704                 var sz = b.el.getSize();
34705                 
34706                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34707                 
34708             }, this);
34709             
34710         }, this);
34711         
34712         var mY = 0;
34713         
34714         for (var i = 0; i < this.cols; i++){
34715             mY = Math.max(mY, maxY[i]);
34716         }
34717         
34718         this.el.setHeight(mY - pos.y);
34719         
34720     },
34721     
34722 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34723 //    {
34724 //        var pos = this.el.getBox(true);
34725 //        var x = pos.x;
34726 //        var y = pos.y;
34727 //        var maxX = pos.right;
34728 //        
34729 //        var maxHeight = 0;
34730 //        
34731 //        Roo.each(items, function(item, k){
34732 //            
34733 //            var c = k % 2;
34734 //            
34735 //            item.el.position('absolute');
34736 //                
34737 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34738 //
34739 //            item.el.setWidth(width);
34740 //
34741 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34742 //
34743 //            item.el.setHeight(height);
34744 //            
34745 //            if(c == 0){
34746 //                item.el.setXY([x, y], isInstant ? false : true);
34747 //            } else {
34748 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34749 //            }
34750 //            
34751 //            y = y + height + this.alternativePadWidth;
34752 //            
34753 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34754 //            
34755 //        }, this);
34756 //        
34757 //        this.el.setHeight(maxHeight);
34758 //        
34759 //    },
34760     
34761     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34762     {
34763         var pos = this.el.getBox(true);
34764         
34765         var minX = pos.x;
34766         var minY = pos.y;
34767         
34768         var maxX = pos.right;
34769         
34770         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34771         
34772         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34773         
34774         Roo.each(queue, function(box, k){
34775             
34776             Roo.each(box, function(b, kk){
34777                 
34778                 b.el.position('absolute');
34779                 
34780                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34781                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34782                 
34783                 if(b.size == 'md-left' || b.size == 'md-right'){
34784                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34785                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34786                 }
34787                 
34788                 b.el.setWidth(width);
34789                 b.el.setHeight(height);
34790                 
34791             }, this);
34792             
34793             if(!box.length){
34794                 return;
34795             }
34796             
34797             var positions = [];
34798             
34799             switch (box.length){
34800                 case 1 :
34801                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34802                     break;
34803                 case 2 :
34804                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34805                     break;
34806                 case 3 :
34807                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34808                     break;
34809                 case 4 :
34810                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34811                     break;
34812                 default :
34813                     break;
34814             }
34815             
34816             Roo.each(box, function(b,kk){
34817                 
34818                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34819                 
34820                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34821                 
34822             }, this);
34823             
34824         }, this);
34825         
34826     },
34827     
34828     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34829     {
34830         Roo.each(eItems, function(b,k){
34831             
34832             b.size = (k == 0) ? 'sm' : 'xs';
34833             b.x = (k == 0) ? 2 : 1;
34834             b.y = (k == 0) ? 2 : 1;
34835             
34836             b.el.position('absolute');
34837             
34838             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34839                 
34840             b.el.setWidth(width);
34841             
34842             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34843             
34844             b.el.setHeight(height);
34845             
34846         }, this);
34847
34848         var positions = [];
34849         
34850         positions.push({
34851             x : maxX - this.unitWidth * 2 - this.gutter,
34852             y : minY
34853         });
34854         
34855         positions.push({
34856             x : maxX - this.unitWidth,
34857             y : minY + (this.unitWidth + this.gutter) * 2
34858         });
34859         
34860         positions.push({
34861             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34862             y : minY
34863         });
34864         
34865         Roo.each(eItems, function(b,k){
34866             
34867             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34868
34869         }, this);
34870         
34871     },
34872     
34873     getVerticalOneBoxColPositions : function(x, y, box)
34874     {
34875         var pos = [];
34876         
34877         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34878         
34879         if(box[0].size == 'md-left'){
34880             rand = 0;
34881         }
34882         
34883         if(box[0].size == 'md-right'){
34884             rand = 1;
34885         }
34886         
34887         pos.push({
34888             x : x + (this.unitWidth + this.gutter) * rand,
34889             y : y
34890         });
34891         
34892         return pos;
34893     },
34894     
34895     getVerticalTwoBoxColPositions : function(x, y, box)
34896     {
34897         var pos = [];
34898         
34899         if(box[0].size == 'xs'){
34900             
34901             pos.push({
34902                 x : x,
34903                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34904             });
34905
34906             pos.push({
34907                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34908                 y : y
34909             });
34910             
34911             return pos;
34912             
34913         }
34914         
34915         pos.push({
34916             x : x,
34917             y : y
34918         });
34919
34920         pos.push({
34921             x : x + (this.unitWidth + this.gutter) * 2,
34922             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34923         });
34924         
34925         return pos;
34926         
34927     },
34928     
34929     getVerticalThreeBoxColPositions : function(x, y, box)
34930     {
34931         var pos = [];
34932         
34933         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34934             
34935             pos.push({
34936                 x : x,
34937                 y : y
34938             });
34939
34940             pos.push({
34941                 x : x + (this.unitWidth + this.gutter) * 1,
34942                 y : y
34943             });
34944             
34945             pos.push({
34946                 x : x + (this.unitWidth + this.gutter) * 2,
34947                 y : y
34948             });
34949             
34950             return pos;
34951             
34952         }
34953         
34954         if(box[0].size == 'xs' && box[1].size == 'xs'){
34955             
34956             pos.push({
34957                 x : x,
34958                 y : y
34959             });
34960
34961             pos.push({
34962                 x : x,
34963                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34964             });
34965             
34966             pos.push({
34967                 x : x + (this.unitWidth + this.gutter) * 1,
34968                 y : y
34969             });
34970             
34971             return pos;
34972             
34973         }
34974         
34975         pos.push({
34976             x : x,
34977             y : y
34978         });
34979
34980         pos.push({
34981             x : x + (this.unitWidth + this.gutter) * 2,
34982             y : y
34983         });
34984
34985         pos.push({
34986             x : x + (this.unitWidth + this.gutter) * 2,
34987             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34988         });
34989             
34990         return pos;
34991         
34992     },
34993     
34994     getVerticalFourBoxColPositions : function(x, y, box)
34995     {
34996         var pos = [];
34997         
34998         if(box[0].size == 'xs'){
34999             
35000             pos.push({
35001                 x : x,
35002                 y : y
35003             });
35004
35005             pos.push({
35006                 x : x,
35007                 y : y + (this.unitHeight + this.gutter) * 1
35008             });
35009             
35010             pos.push({
35011                 x : x,
35012                 y : y + (this.unitHeight + this.gutter) * 2
35013             });
35014             
35015             pos.push({
35016                 x : x + (this.unitWidth + this.gutter) * 1,
35017                 y : y
35018             });
35019             
35020             return pos;
35021             
35022         }
35023         
35024         pos.push({
35025             x : x,
35026             y : y
35027         });
35028
35029         pos.push({
35030             x : x + (this.unitWidth + this.gutter) * 2,
35031             y : y
35032         });
35033
35034         pos.push({
35035             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35036             y : y + (this.unitHeight + this.gutter) * 1
35037         });
35038
35039         pos.push({
35040             x : x + (this.unitWidth + this.gutter) * 2,
35041             y : y + (this.unitWidth + this.gutter) * 2
35042         });
35043
35044         return pos;
35045         
35046     },
35047     
35048     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35049     {
35050         var pos = [];
35051         
35052         if(box[0].size == 'md-left'){
35053             pos.push({
35054                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35055                 y : minY
35056             });
35057             
35058             return pos;
35059         }
35060         
35061         if(box[0].size == 'md-right'){
35062             pos.push({
35063                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35064                 y : minY + (this.unitWidth + this.gutter) * 1
35065             });
35066             
35067             return pos;
35068         }
35069         
35070         var rand = Math.floor(Math.random() * (4 - box[0].y));
35071         
35072         pos.push({
35073             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35074             y : minY + (this.unitWidth + this.gutter) * rand
35075         });
35076         
35077         return pos;
35078         
35079     },
35080     
35081     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35082     {
35083         var pos = [];
35084         
35085         if(box[0].size == 'xs'){
35086             
35087             pos.push({
35088                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35089                 y : minY
35090             });
35091
35092             pos.push({
35093                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35094                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35095             });
35096             
35097             return pos;
35098             
35099         }
35100         
35101         pos.push({
35102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35103             y : minY
35104         });
35105
35106         pos.push({
35107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35108             y : minY + (this.unitWidth + this.gutter) * 2
35109         });
35110         
35111         return pos;
35112         
35113     },
35114     
35115     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35116     {
35117         var pos = [];
35118         
35119         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35120             
35121             pos.push({
35122                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35123                 y : minY
35124             });
35125
35126             pos.push({
35127                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35128                 y : minY + (this.unitWidth + this.gutter) * 1
35129             });
35130             
35131             pos.push({
35132                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35133                 y : minY + (this.unitWidth + this.gutter) * 2
35134             });
35135             
35136             return pos;
35137             
35138         }
35139         
35140         if(box[0].size == 'xs' && box[1].size == 'xs'){
35141             
35142             pos.push({
35143                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35144                 y : minY
35145             });
35146
35147             pos.push({
35148                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35149                 y : minY
35150             });
35151             
35152             pos.push({
35153                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35154                 y : minY + (this.unitWidth + this.gutter) * 1
35155             });
35156             
35157             return pos;
35158             
35159         }
35160         
35161         pos.push({
35162             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35163             y : minY
35164         });
35165
35166         pos.push({
35167             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35168             y : minY + (this.unitWidth + this.gutter) * 2
35169         });
35170
35171         pos.push({
35172             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35173             y : minY + (this.unitWidth + this.gutter) * 2
35174         });
35175             
35176         return pos;
35177         
35178     },
35179     
35180     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35181     {
35182         var pos = [];
35183         
35184         if(box[0].size == 'xs'){
35185             
35186             pos.push({
35187                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35188                 y : minY
35189             });
35190
35191             pos.push({
35192                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35193                 y : minY
35194             });
35195             
35196             pos.push({
35197                 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),
35198                 y : minY
35199             });
35200             
35201             pos.push({
35202                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35203                 y : minY + (this.unitWidth + this.gutter) * 1
35204             });
35205             
35206             return pos;
35207             
35208         }
35209         
35210         pos.push({
35211             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35212             y : minY
35213         });
35214         
35215         pos.push({
35216             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35217             y : minY + (this.unitWidth + this.gutter) * 2
35218         });
35219         
35220         pos.push({
35221             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35222             y : minY + (this.unitWidth + this.gutter) * 2
35223         });
35224         
35225         pos.push({
35226             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),
35227             y : minY + (this.unitWidth + this.gutter) * 2
35228         });
35229
35230         return pos;
35231         
35232     },
35233     
35234     /**
35235     * remove a Masonry Brick
35236     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35237     */
35238     removeBrick : function(brick_id)
35239     {
35240         if (!brick_id) {
35241             return;
35242         }
35243         
35244         for (var i = 0; i<this.bricks.length; i++) {
35245             if (this.bricks[i].id == brick_id) {
35246                 this.bricks.splice(i,1);
35247                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35248                 this.initial();
35249             }
35250         }
35251     },
35252     
35253     /**
35254     * adds a Masonry Brick
35255     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35256     */
35257     addBrick : function(cfg)
35258     {
35259         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35260         //this.register(cn);
35261         cn.parentId = this.id;
35262         cn.render(this.el);
35263         return cn;
35264     },
35265     
35266     /**
35267     * register a Masonry Brick
35268     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35269     */
35270     
35271     register : function(brick)
35272     {
35273         this.bricks.push(brick);
35274         brick.masonryId = this.id;
35275     },
35276     
35277     /**
35278     * clear all the Masonry Brick
35279     */
35280     clearAll : function()
35281     {
35282         this.bricks = [];
35283         //this.getChildContainer().dom.innerHTML = "";
35284         this.el.dom.innerHTML = '';
35285     },
35286     
35287     getSelected : function()
35288     {
35289         if (!this.selectedBrick) {
35290             return false;
35291         }
35292         
35293         return this.selectedBrick;
35294     }
35295 });
35296
35297 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35298     
35299     groups: {},
35300      /**
35301     * register a Masonry Layout
35302     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35303     */
35304     
35305     register : function(layout)
35306     {
35307         this.groups[layout.id] = layout;
35308     },
35309     /**
35310     * fetch a  Masonry Layout based on the masonry layout ID
35311     * @param {string} the masonry layout to add
35312     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35313     */
35314     
35315     get: function(layout_id) {
35316         if (typeof(this.groups[layout_id]) == 'undefined') {
35317             return false;
35318         }
35319         return this.groups[layout_id] ;
35320     }
35321     
35322     
35323     
35324 });
35325
35326  
35327
35328  /**
35329  *
35330  * This is based on 
35331  * http://masonry.desandro.com
35332  *
35333  * The idea is to render all the bricks based on vertical width...
35334  *
35335  * The original code extends 'outlayer' - we might need to use that....
35336  * 
35337  */
35338
35339
35340 /**
35341  * @class Roo.bootstrap.LayoutMasonryAuto
35342  * @extends Roo.bootstrap.Component
35343  * Bootstrap Layout Masonry class
35344  * 
35345  * @constructor
35346  * Create a new Element
35347  * @param {Object} config The config object
35348  */
35349
35350 Roo.bootstrap.LayoutMasonryAuto = function(config){
35351     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35352 };
35353
35354 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35355     
35356       /**
35357      * @cfg {Boolean} isFitWidth  - resize the width..
35358      */   
35359     isFitWidth : false,  // options..
35360     /**
35361      * @cfg {Boolean} isOriginLeft = left align?
35362      */   
35363     isOriginLeft : true,
35364     /**
35365      * @cfg {Boolean} isOriginTop = top align?
35366      */   
35367     isOriginTop : false,
35368     /**
35369      * @cfg {Boolean} isLayoutInstant = no animation?
35370      */   
35371     isLayoutInstant : false, // needed?
35372     /**
35373      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35374      */   
35375     isResizingContainer : true,
35376     /**
35377      * @cfg {Number} columnWidth  width of the columns 
35378      */   
35379     
35380     columnWidth : 0,
35381     
35382     /**
35383      * @cfg {Number} maxCols maximum number of columns
35384      */   
35385     
35386     maxCols: 0,
35387     /**
35388      * @cfg {Number} padHeight padding below box..
35389      */   
35390     
35391     padHeight : 10, 
35392     
35393     /**
35394      * @cfg {Boolean} isAutoInitial defalut true
35395      */   
35396     
35397     isAutoInitial : true, 
35398     
35399     // private?
35400     gutter : 0,
35401     
35402     containerWidth: 0,
35403     initialColumnWidth : 0,
35404     currentSize : null,
35405     
35406     colYs : null, // array.
35407     maxY : 0,
35408     padWidth: 10,
35409     
35410     
35411     tag: 'div',
35412     cls: '',
35413     bricks: null, //CompositeElement
35414     cols : 0, // array?
35415     // element : null, // wrapped now this.el
35416     _isLayoutInited : null, 
35417     
35418     
35419     getAutoCreate : function(){
35420         
35421         var cfg = {
35422             tag: this.tag,
35423             cls: 'blog-masonary-wrapper ' + this.cls,
35424             cn : {
35425                 cls : 'mas-boxes masonary'
35426             }
35427         };
35428         
35429         return cfg;
35430     },
35431     
35432     getChildContainer: function( )
35433     {
35434         if (this.boxesEl) {
35435             return this.boxesEl;
35436         }
35437         
35438         this.boxesEl = this.el.select('.mas-boxes').first();
35439         
35440         return this.boxesEl;
35441     },
35442     
35443     
35444     initEvents : function()
35445     {
35446         var _this = this;
35447         
35448         if(this.isAutoInitial){
35449             Roo.log('hook children rendered');
35450             this.on('childrenrendered', function() {
35451                 Roo.log('children rendered');
35452                 _this.initial();
35453             } ,this);
35454         }
35455         
35456     },
35457     
35458     initial : function()
35459     {
35460         this.reloadItems();
35461
35462         this.currentSize = this.el.getBox(true);
35463
35464         /// was window resize... - let's see if this works..
35465         Roo.EventManager.onWindowResize(this.resize, this); 
35466
35467         if(!this.isAutoInitial){
35468             this.layout();
35469             return;
35470         }
35471         
35472         this.layout.defer(500,this);
35473     },
35474     
35475     reloadItems: function()
35476     {
35477         this.bricks = this.el.select('.masonry-brick', true);
35478         
35479         this.bricks.each(function(b) {
35480             //Roo.log(b.getSize());
35481             if (!b.attr('originalwidth')) {
35482                 b.attr('originalwidth',  b.getSize().width);
35483             }
35484             
35485         });
35486         
35487         Roo.log(this.bricks.elements.length);
35488     },
35489     
35490     resize : function()
35491     {
35492         Roo.log('resize');
35493         var cs = this.el.getBox(true);
35494         
35495         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35496             Roo.log("no change in with or X");
35497             return;
35498         }
35499         this.currentSize = cs;
35500         this.layout();
35501     },
35502     
35503     layout : function()
35504     {
35505          Roo.log('layout');
35506         this._resetLayout();
35507         //this._manageStamps();
35508       
35509         // don't animate first layout
35510         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35511         this.layoutItems( isInstant );
35512       
35513         // flag for initalized
35514         this._isLayoutInited = true;
35515     },
35516     
35517     layoutItems : function( isInstant )
35518     {
35519         //var items = this._getItemsForLayout( this.items );
35520         // original code supports filtering layout items.. we just ignore it..
35521         
35522         this._layoutItems( this.bricks , isInstant );
35523       
35524         this._postLayout();
35525     },
35526     _layoutItems : function ( items , isInstant)
35527     {
35528        //this.fireEvent( 'layout', this, items );
35529     
35530
35531         if ( !items || !items.elements.length ) {
35532           // no items, emit event with empty array
35533             return;
35534         }
35535
35536         var queue = [];
35537         items.each(function(item) {
35538             Roo.log("layout item");
35539             Roo.log(item);
35540             // get x/y object from method
35541             var position = this._getItemLayoutPosition( item );
35542             // enqueue
35543             position.item = item;
35544             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35545             queue.push( position );
35546         }, this);
35547       
35548         this._processLayoutQueue( queue );
35549     },
35550     /** Sets position of item in DOM
35551     * @param {Element} item
35552     * @param {Number} x - horizontal position
35553     * @param {Number} y - vertical position
35554     * @param {Boolean} isInstant - disables transitions
35555     */
35556     _processLayoutQueue : function( queue )
35557     {
35558         for ( var i=0, len = queue.length; i < len; i++ ) {
35559             var obj = queue[i];
35560             obj.item.position('absolute');
35561             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35562         }
35563     },
35564       
35565     
35566     /**
35567     * Any logic you want to do after each layout,
35568     * i.e. size the container
35569     */
35570     _postLayout : function()
35571     {
35572         this.resizeContainer();
35573     },
35574     
35575     resizeContainer : function()
35576     {
35577         if ( !this.isResizingContainer ) {
35578             return;
35579         }
35580         var size = this._getContainerSize();
35581         if ( size ) {
35582             this.el.setSize(size.width,size.height);
35583             this.boxesEl.setSize(size.width,size.height);
35584         }
35585     },
35586     
35587     
35588     
35589     _resetLayout : function()
35590     {
35591         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35592         this.colWidth = this.el.getWidth();
35593         //this.gutter = this.el.getWidth(); 
35594         
35595         this.measureColumns();
35596
35597         // reset column Y
35598         var i = this.cols;
35599         this.colYs = [];
35600         while (i--) {
35601             this.colYs.push( 0 );
35602         }
35603     
35604         this.maxY = 0;
35605     },
35606
35607     measureColumns : function()
35608     {
35609         this.getContainerWidth();
35610       // if columnWidth is 0, default to outerWidth of first item
35611         if ( !this.columnWidth ) {
35612             var firstItem = this.bricks.first();
35613             Roo.log(firstItem);
35614             this.columnWidth  = this.containerWidth;
35615             if (firstItem && firstItem.attr('originalwidth') ) {
35616                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35617             }
35618             // columnWidth fall back to item of first element
35619             Roo.log("set column width?");
35620                         this.initialColumnWidth = this.columnWidth  ;
35621
35622             // if first elem has no width, default to size of container
35623             
35624         }
35625         
35626         
35627         if (this.initialColumnWidth) {
35628             this.columnWidth = this.initialColumnWidth;
35629         }
35630         
35631         
35632             
35633         // column width is fixed at the top - however if container width get's smaller we should
35634         // reduce it...
35635         
35636         // this bit calcs how man columns..
35637             
35638         var columnWidth = this.columnWidth += this.gutter;
35639       
35640         // calculate columns
35641         var containerWidth = this.containerWidth + this.gutter;
35642         
35643         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35644         // fix rounding errors, typically with gutters
35645         var excess = columnWidth - containerWidth % columnWidth;
35646         
35647         
35648         // if overshoot is less than a pixel, round up, otherwise floor it
35649         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35650         cols = Math[ mathMethod ]( cols );
35651         this.cols = Math.max( cols, 1 );
35652         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35653         
35654          // padding positioning..
35655         var totalColWidth = this.cols * this.columnWidth;
35656         var padavail = this.containerWidth - totalColWidth;
35657         // so for 2 columns - we need 3 'pads'
35658         
35659         var padNeeded = (1+this.cols) * this.padWidth;
35660         
35661         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35662         
35663         this.columnWidth += padExtra
35664         //this.padWidth = Math.floor(padavail /  ( this.cols));
35665         
35666         // adjust colum width so that padding is fixed??
35667         
35668         // we have 3 columns ... total = width * 3
35669         // we have X left over... that should be used by 
35670         
35671         //if (this.expandC) {
35672             
35673         //}
35674         
35675         
35676         
35677     },
35678     
35679     getContainerWidth : function()
35680     {
35681        /* // container is parent if fit width
35682         var container = this.isFitWidth ? this.element.parentNode : this.element;
35683         // check that this.size and size are there
35684         // IE8 triggers resize on body size change, so they might not be
35685         
35686         var size = getSize( container );  //FIXME
35687         this.containerWidth = size && size.innerWidth; //FIXME
35688         */
35689          
35690         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35691         
35692     },
35693     
35694     _getItemLayoutPosition : function( item )  // what is item?
35695     {
35696         // we resize the item to our columnWidth..
35697       
35698         item.setWidth(this.columnWidth);
35699         item.autoBoxAdjust  = false;
35700         
35701         var sz = item.getSize();
35702  
35703         // how many columns does this brick span
35704         var remainder = this.containerWidth % this.columnWidth;
35705         
35706         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35707         // round if off by 1 pixel, otherwise use ceil
35708         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35709         colSpan = Math.min( colSpan, this.cols );
35710         
35711         // normally this should be '1' as we dont' currently allow multi width columns..
35712         
35713         var colGroup = this._getColGroup( colSpan );
35714         // get the minimum Y value from the columns
35715         var minimumY = Math.min.apply( Math, colGroup );
35716         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35717         
35718         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35719          
35720         // position the brick
35721         var position = {
35722             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35723             y: this.currentSize.y + minimumY + this.padHeight
35724         };
35725         
35726         Roo.log(position);
35727         // apply setHeight to necessary columns
35728         var setHeight = minimumY + sz.height + this.padHeight;
35729         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35730         
35731         var setSpan = this.cols + 1 - colGroup.length;
35732         for ( var i = 0; i < setSpan; i++ ) {
35733           this.colYs[ shortColIndex + i ] = setHeight ;
35734         }
35735       
35736         return position;
35737     },
35738     
35739     /**
35740      * @param {Number} colSpan - number of columns the element spans
35741      * @returns {Array} colGroup
35742      */
35743     _getColGroup : function( colSpan )
35744     {
35745         if ( colSpan < 2 ) {
35746           // if brick spans only one column, use all the column Ys
35747           return this.colYs;
35748         }
35749       
35750         var colGroup = [];
35751         // how many different places could this brick fit horizontally
35752         var groupCount = this.cols + 1 - colSpan;
35753         // for each group potential horizontal position
35754         for ( var i = 0; i < groupCount; i++ ) {
35755           // make an array of colY values for that one group
35756           var groupColYs = this.colYs.slice( i, i + colSpan );
35757           // and get the max value of the array
35758           colGroup[i] = Math.max.apply( Math, groupColYs );
35759         }
35760         return colGroup;
35761     },
35762     /*
35763     _manageStamp : function( stamp )
35764     {
35765         var stampSize =  stamp.getSize();
35766         var offset = stamp.getBox();
35767         // get the columns that this stamp affects
35768         var firstX = this.isOriginLeft ? offset.x : offset.right;
35769         var lastX = firstX + stampSize.width;
35770         var firstCol = Math.floor( firstX / this.columnWidth );
35771         firstCol = Math.max( 0, firstCol );
35772         
35773         var lastCol = Math.floor( lastX / this.columnWidth );
35774         // lastCol should not go over if multiple of columnWidth #425
35775         lastCol -= lastX % this.columnWidth ? 0 : 1;
35776         lastCol = Math.min( this.cols - 1, lastCol );
35777         
35778         // set colYs to bottom of the stamp
35779         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35780             stampSize.height;
35781             
35782         for ( var i = firstCol; i <= lastCol; i++ ) {
35783           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35784         }
35785     },
35786     */
35787     
35788     _getContainerSize : function()
35789     {
35790         this.maxY = Math.max.apply( Math, this.colYs );
35791         var size = {
35792             height: this.maxY
35793         };
35794       
35795         if ( this.isFitWidth ) {
35796             size.width = this._getContainerFitWidth();
35797         }
35798       
35799         return size;
35800     },
35801     
35802     _getContainerFitWidth : function()
35803     {
35804         var unusedCols = 0;
35805         // count unused columns
35806         var i = this.cols;
35807         while ( --i ) {
35808           if ( this.colYs[i] !== 0 ) {
35809             break;
35810           }
35811           unusedCols++;
35812         }
35813         // fit container to columns that have been used
35814         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35815     },
35816     
35817     needsResizeLayout : function()
35818     {
35819         var previousWidth = this.containerWidth;
35820         this.getContainerWidth();
35821         return previousWidth !== this.containerWidth;
35822     }
35823  
35824 });
35825
35826  
35827
35828  /*
35829  * - LGPL
35830  *
35831  * element
35832  * 
35833  */
35834
35835 /**
35836  * @class Roo.bootstrap.MasonryBrick
35837  * @extends Roo.bootstrap.Component
35838  * Bootstrap MasonryBrick class
35839  * 
35840  * @constructor
35841  * Create a new MasonryBrick
35842  * @param {Object} config The config object
35843  */
35844
35845 Roo.bootstrap.MasonryBrick = function(config){
35846     
35847     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35848     
35849     Roo.bootstrap.MasonryBrick.register(this);
35850     
35851     this.addEvents({
35852         // raw events
35853         /**
35854          * @event click
35855          * When a MasonryBrick is clcik
35856          * @param {Roo.bootstrap.MasonryBrick} this
35857          * @param {Roo.EventObject} e
35858          */
35859         "click" : true
35860     });
35861 };
35862
35863 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35864     
35865     /**
35866      * @cfg {String} title
35867      */   
35868     title : '',
35869     /**
35870      * @cfg {String} html
35871      */   
35872     html : '',
35873     /**
35874      * @cfg {String} bgimage
35875      */   
35876     bgimage : '',
35877     /**
35878      * @cfg {String} videourl
35879      */   
35880     videourl : '',
35881     /**
35882      * @cfg {String} cls
35883      */   
35884     cls : '',
35885     /**
35886      * @cfg {String} href
35887      */   
35888     href : '',
35889     /**
35890      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35891      */   
35892     size : 'xs',
35893     
35894     /**
35895      * @cfg {String} placetitle (center|bottom)
35896      */   
35897     placetitle : '',
35898     
35899     /**
35900      * @cfg {Boolean} isFitContainer defalut true
35901      */   
35902     isFitContainer : true, 
35903     
35904     /**
35905      * @cfg {Boolean} preventDefault defalut false
35906      */   
35907     preventDefault : false, 
35908     
35909     /**
35910      * @cfg {Boolean} inverse defalut false
35911      */   
35912     maskInverse : false, 
35913     
35914     getAutoCreate : function()
35915     {
35916         if(!this.isFitContainer){
35917             return this.getSplitAutoCreate();
35918         }
35919         
35920         var cls = 'masonry-brick masonry-brick-full';
35921         
35922         if(this.href.length){
35923             cls += ' masonry-brick-link';
35924         }
35925         
35926         if(this.bgimage.length){
35927             cls += ' masonry-brick-image';
35928         }
35929         
35930         if(this.maskInverse){
35931             cls += ' mask-inverse';
35932         }
35933         
35934         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35935             cls += ' enable-mask';
35936         }
35937         
35938         if(this.size){
35939             cls += ' masonry-' + this.size + '-brick';
35940         }
35941         
35942         if(this.placetitle.length){
35943             
35944             switch (this.placetitle) {
35945                 case 'center' :
35946                     cls += ' masonry-center-title';
35947                     break;
35948                 case 'bottom' :
35949                     cls += ' masonry-bottom-title';
35950                     break;
35951                 default:
35952                     break;
35953             }
35954             
35955         } else {
35956             if(!this.html.length && !this.bgimage.length){
35957                 cls += ' masonry-center-title';
35958             }
35959
35960             if(!this.html.length && this.bgimage.length){
35961                 cls += ' masonry-bottom-title';
35962             }
35963         }
35964         
35965         if(this.cls){
35966             cls += ' ' + this.cls;
35967         }
35968         
35969         var cfg = {
35970             tag: (this.href.length) ? 'a' : 'div',
35971             cls: cls,
35972             cn: [
35973                 {
35974                     tag: 'div',
35975                     cls: 'masonry-brick-mask'
35976                 },
35977                 {
35978                     tag: 'div',
35979                     cls: 'masonry-brick-paragraph',
35980                     cn: []
35981                 }
35982             ]
35983         };
35984         
35985         if(this.href.length){
35986             cfg.href = this.href;
35987         }
35988         
35989         var cn = cfg.cn[1].cn;
35990         
35991         if(this.title.length){
35992             cn.push({
35993                 tag: 'h4',
35994                 cls: 'masonry-brick-title',
35995                 html: this.title
35996             });
35997         }
35998         
35999         if(this.html.length){
36000             cn.push({
36001                 tag: 'p',
36002                 cls: 'masonry-brick-text',
36003                 html: this.html
36004             });
36005         }
36006         
36007         if (!this.title.length && !this.html.length) {
36008             cfg.cn[1].cls += ' hide';
36009         }
36010         
36011         if(this.bgimage.length){
36012             cfg.cn.push({
36013                 tag: 'img',
36014                 cls: 'masonry-brick-image-view',
36015                 src: this.bgimage
36016             });
36017         }
36018         
36019         if(this.videourl.length){
36020             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36021             // youtube support only?
36022             cfg.cn.push({
36023                 tag: 'iframe',
36024                 cls: 'masonry-brick-image-view',
36025                 src: vurl,
36026                 frameborder : 0,
36027                 allowfullscreen : true
36028             });
36029         }
36030         
36031         return cfg;
36032         
36033     },
36034     
36035     getSplitAutoCreate : function()
36036     {
36037         var cls = 'masonry-brick masonry-brick-split';
36038         
36039         if(this.href.length){
36040             cls += ' masonry-brick-link';
36041         }
36042         
36043         if(this.bgimage.length){
36044             cls += ' masonry-brick-image';
36045         }
36046         
36047         if(this.size){
36048             cls += ' masonry-' + this.size + '-brick';
36049         }
36050         
36051         switch (this.placetitle) {
36052             case 'center' :
36053                 cls += ' masonry-center-title';
36054                 break;
36055             case 'bottom' :
36056                 cls += ' masonry-bottom-title';
36057                 break;
36058             default:
36059                 if(!this.bgimage.length){
36060                     cls += ' masonry-center-title';
36061                 }
36062
36063                 if(this.bgimage.length){
36064                     cls += ' masonry-bottom-title';
36065                 }
36066                 break;
36067         }
36068         
36069         if(this.cls){
36070             cls += ' ' + this.cls;
36071         }
36072         
36073         var cfg = {
36074             tag: (this.href.length) ? 'a' : 'div',
36075             cls: cls,
36076             cn: [
36077                 {
36078                     tag: 'div',
36079                     cls: 'masonry-brick-split-head',
36080                     cn: [
36081                         {
36082                             tag: 'div',
36083                             cls: 'masonry-brick-paragraph',
36084                             cn: []
36085                         }
36086                     ]
36087                 },
36088                 {
36089                     tag: 'div',
36090                     cls: 'masonry-brick-split-body',
36091                     cn: []
36092                 }
36093             ]
36094         };
36095         
36096         if(this.href.length){
36097             cfg.href = this.href;
36098         }
36099         
36100         if(this.title.length){
36101             cfg.cn[0].cn[0].cn.push({
36102                 tag: 'h4',
36103                 cls: 'masonry-brick-title',
36104                 html: this.title
36105             });
36106         }
36107         
36108         if(this.html.length){
36109             cfg.cn[1].cn.push({
36110                 tag: 'p',
36111                 cls: 'masonry-brick-text',
36112                 html: this.html
36113             });
36114         }
36115
36116         if(this.bgimage.length){
36117             cfg.cn[0].cn.push({
36118                 tag: 'img',
36119                 cls: 'masonry-brick-image-view',
36120                 src: this.bgimage
36121             });
36122         }
36123         
36124         if(this.videourl.length){
36125             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36126             // youtube support only?
36127             cfg.cn[0].cn.cn.push({
36128                 tag: 'iframe',
36129                 cls: 'masonry-brick-image-view',
36130                 src: vurl,
36131                 frameborder : 0,
36132                 allowfullscreen : true
36133             });
36134         }
36135         
36136         return cfg;
36137     },
36138     
36139     initEvents: function() 
36140     {
36141         switch (this.size) {
36142             case 'xs' :
36143                 this.x = 1;
36144                 this.y = 1;
36145                 break;
36146             case 'sm' :
36147                 this.x = 2;
36148                 this.y = 2;
36149                 break;
36150             case 'md' :
36151             case 'md-left' :
36152             case 'md-right' :
36153                 this.x = 3;
36154                 this.y = 3;
36155                 break;
36156             case 'tall' :
36157                 this.x = 2;
36158                 this.y = 3;
36159                 break;
36160             case 'wide' :
36161                 this.x = 3;
36162                 this.y = 2;
36163                 break;
36164             case 'wide-thin' :
36165                 this.x = 3;
36166                 this.y = 1;
36167                 break;
36168                         
36169             default :
36170                 break;
36171         }
36172         
36173         if(Roo.isTouch){
36174             this.el.on('touchstart', this.onTouchStart, this);
36175             this.el.on('touchmove', this.onTouchMove, this);
36176             this.el.on('touchend', this.onTouchEnd, this);
36177             this.el.on('contextmenu', this.onContextMenu, this);
36178         } else {
36179             this.el.on('mouseenter'  ,this.enter, this);
36180             this.el.on('mouseleave', this.leave, this);
36181             this.el.on('click', this.onClick, this);
36182         }
36183         
36184         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36185             this.parent().bricks.push(this);   
36186         }
36187         
36188     },
36189     
36190     onClick: function(e, el)
36191     {
36192         var time = this.endTimer - this.startTimer;
36193         // Roo.log(e.preventDefault());
36194         if(Roo.isTouch){
36195             if(time > 1000){
36196                 e.preventDefault();
36197                 return;
36198             }
36199         }
36200         
36201         if(!this.preventDefault){
36202             return;
36203         }
36204         
36205         e.preventDefault();
36206         
36207         if (this.activeClass != '') {
36208             this.selectBrick();
36209         }
36210         
36211         this.fireEvent('click', this, e);
36212     },
36213     
36214     enter: function(e, el)
36215     {
36216         e.preventDefault();
36217         
36218         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36219             return;
36220         }
36221         
36222         if(this.bgimage.length && this.html.length){
36223             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36224         }
36225     },
36226     
36227     leave: function(e, el)
36228     {
36229         e.preventDefault();
36230         
36231         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36232             return;
36233         }
36234         
36235         if(this.bgimage.length && this.html.length){
36236             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36237         }
36238     },
36239     
36240     onTouchStart: function(e, el)
36241     {
36242 //        e.preventDefault();
36243         
36244         this.touchmoved = false;
36245         
36246         if(!this.isFitContainer){
36247             return;
36248         }
36249         
36250         if(!this.bgimage.length || !this.html.length){
36251             return;
36252         }
36253         
36254         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36255         
36256         this.timer = new Date().getTime();
36257         
36258     },
36259     
36260     onTouchMove: function(e, el)
36261     {
36262         this.touchmoved = true;
36263     },
36264     
36265     onContextMenu : function(e,el)
36266     {
36267         e.preventDefault();
36268         e.stopPropagation();
36269         return false;
36270     },
36271     
36272     onTouchEnd: function(e, el)
36273     {
36274 //        e.preventDefault();
36275         
36276         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36277         
36278             this.leave(e,el);
36279             
36280             return;
36281         }
36282         
36283         if(!this.bgimage.length || !this.html.length){
36284             
36285             if(this.href.length){
36286                 window.location.href = this.href;
36287             }
36288             
36289             return;
36290         }
36291         
36292         if(!this.isFitContainer){
36293             return;
36294         }
36295         
36296         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36297         
36298         window.location.href = this.href;
36299     },
36300     
36301     //selection on single brick only
36302     selectBrick : function() {
36303         
36304         if (!this.parentId) {
36305             return;
36306         }
36307         
36308         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36309         var index = m.selectedBrick.indexOf(this.id);
36310         
36311         if ( index > -1) {
36312             m.selectedBrick.splice(index,1);
36313             this.el.removeClass(this.activeClass);
36314             return;
36315         }
36316         
36317         for(var i = 0; i < m.selectedBrick.length; i++) {
36318             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36319             b.el.removeClass(b.activeClass);
36320         }
36321         
36322         m.selectedBrick = [];
36323         
36324         m.selectedBrick.push(this.id);
36325         this.el.addClass(this.activeClass);
36326         return;
36327     },
36328     
36329     isSelected : function(){
36330         return this.el.hasClass(this.activeClass);
36331         
36332     }
36333 });
36334
36335 Roo.apply(Roo.bootstrap.MasonryBrick, {
36336     
36337     //groups: {},
36338     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36339      /**
36340     * register a Masonry Brick
36341     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36342     */
36343     
36344     register : function(brick)
36345     {
36346         //this.groups[brick.id] = brick;
36347         this.groups.add(brick.id, brick);
36348     },
36349     /**
36350     * fetch a  masonry brick based on the masonry brick ID
36351     * @param {string} the masonry brick to add
36352     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36353     */
36354     
36355     get: function(brick_id) 
36356     {
36357         // if (typeof(this.groups[brick_id]) == 'undefined') {
36358         //     return false;
36359         // }
36360         // return this.groups[brick_id] ;
36361         
36362         if(this.groups.key(brick_id)) {
36363             return this.groups.key(brick_id);
36364         }
36365         
36366         return false;
36367     }
36368     
36369     
36370     
36371 });
36372
36373  /*
36374  * - LGPL
36375  *
36376  * element
36377  * 
36378  */
36379
36380 /**
36381  * @class Roo.bootstrap.Brick
36382  * @extends Roo.bootstrap.Component
36383  * Bootstrap Brick class
36384  * 
36385  * @constructor
36386  * Create a new Brick
36387  * @param {Object} config The config object
36388  */
36389
36390 Roo.bootstrap.Brick = function(config){
36391     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36392     
36393     this.addEvents({
36394         // raw events
36395         /**
36396          * @event click
36397          * When a Brick is click
36398          * @param {Roo.bootstrap.Brick} this
36399          * @param {Roo.EventObject} e
36400          */
36401         "click" : true
36402     });
36403 };
36404
36405 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36406     
36407     /**
36408      * @cfg {String} title
36409      */   
36410     title : '',
36411     /**
36412      * @cfg {String} html
36413      */   
36414     html : '',
36415     /**
36416      * @cfg {String} bgimage
36417      */   
36418     bgimage : '',
36419     /**
36420      * @cfg {String} cls
36421      */   
36422     cls : '',
36423     /**
36424      * @cfg {String} href
36425      */   
36426     href : '',
36427     /**
36428      * @cfg {String} video
36429      */   
36430     video : '',
36431     /**
36432      * @cfg {Boolean} square
36433      */   
36434     square : true,
36435     
36436     getAutoCreate : function()
36437     {
36438         var cls = 'roo-brick';
36439         
36440         if(this.href.length){
36441             cls += ' roo-brick-link';
36442         }
36443         
36444         if(this.bgimage.length){
36445             cls += ' roo-brick-image';
36446         }
36447         
36448         if(!this.html.length && !this.bgimage.length){
36449             cls += ' roo-brick-center-title';
36450         }
36451         
36452         if(!this.html.length && this.bgimage.length){
36453             cls += ' roo-brick-bottom-title';
36454         }
36455         
36456         if(this.cls){
36457             cls += ' ' + this.cls;
36458         }
36459         
36460         var cfg = {
36461             tag: (this.href.length) ? 'a' : 'div',
36462             cls: cls,
36463             cn: [
36464                 {
36465                     tag: 'div',
36466                     cls: 'roo-brick-paragraph',
36467                     cn: []
36468                 }
36469             ]
36470         };
36471         
36472         if(this.href.length){
36473             cfg.href = this.href;
36474         }
36475         
36476         var cn = cfg.cn[0].cn;
36477         
36478         if(this.title.length){
36479             cn.push({
36480                 tag: 'h4',
36481                 cls: 'roo-brick-title',
36482                 html: this.title
36483             });
36484         }
36485         
36486         if(this.html.length){
36487             cn.push({
36488                 tag: 'p',
36489                 cls: 'roo-brick-text',
36490                 html: this.html
36491             });
36492         } else {
36493             cn.cls += ' hide';
36494         }
36495         
36496         if(this.bgimage.length){
36497             cfg.cn.push({
36498                 tag: 'img',
36499                 cls: 'roo-brick-image-view',
36500                 src: this.bgimage
36501             });
36502         }
36503         
36504         return cfg;
36505     },
36506     
36507     initEvents: function() 
36508     {
36509         if(this.title.length || this.html.length){
36510             this.el.on('mouseenter'  ,this.enter, this);
36511             this.el.on('mouseleave', this.leave, this);
36512         }
36513         
36514         Roo.EventManager.onWindowResize(this.resize, this); 
36515         
36516         if(this.bgimage.length){
36517             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36518             this.imageEl.on('load', this.onImageLoad, this);
36519             return;
36520         }
36521         
36522         this.resize();
36523     },
36524     
36525     onImageLoad : function()
36526     {
36527         this.resize();
36528     },
36529     
36530     resize : function()
36531     {
36532         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36533         
36534         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36535         
36536         if(this.bgimage.length){
36537             var image = this.el.select('.roo-brick-image-view', true).first();
36538             
36539             image.setWidth(paragraph.getWidth());
36540             
36541             if(this.square){
36542                 image.setHeight(paragraph.getWidth());
36543             }
36544             
36545             this.el.setHeight(image.getHeight());
36546             paragraph.setHeight(image.getHeight());
36547             
36548         }
36549         
36550     },
36551     
36552     enter: function(e, el)
36553     {
36554         e.preventDefault();
36555         
36556         if(this.bgimage.length){
36557             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36558             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36559         }
36560     },
36561     
36562     leave: function(e, el)
36563     {
36564         e.preventDefault();
36565         
36566         if(this.bgimage.length){
36567             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36568             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36569         }
36570     }
36571     
36572 });
36573
36574  
36575
36576  /*
36577  * - LGPL
36578  *
36579  * Number field 
36580  */
36581
36582 /**
36583  * @class Roo.bootstrap.form.NumberField
36584  * @extends Roo.bootstrap.form.Input
36585  * Bootstrap NumberField class
36586  * 
36587  * 
36588  * 
36589  * 
36590  * @constructor
36591  * Create a new NumberField
36592  * @param {Object} config The config object
36593  */
36594
36595 Roo.bootstrap.form.NumberField = function(config){
36596     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36597 };
36598
36599 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36600     
36601     /**
36602      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36603      */
36604     allowDecimals : true,
36605     /**
36606      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36607      */
36608     decimalSeparator : ".",
36609     /**
36610      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36611      */
36612     decimalPrecision : 2,
36613     /**
36614      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36615      */
36616     allowNegative : true,
36617     
36618     /**
36619      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36620      */
36621     allowZero: true,
36622     /**
36623      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36624      */
36625     minValue : Number.NEGATIVE_INFINITY,
36626     /**
36627      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36628      */
36629     maxValue : Number.MAX_VALUE,
36630     /**
36631      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36632      */
36633     minText : "The minimum value for this field is {0}",
36634     /**
36635      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36636      */
36637     maxText : "The maximum value for this field is {0}",
36638     /**
36639      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36640      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36641      */
36642     nanText : "{0} is not a valid number",
36643     /**
36644      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36645      */
36646     thousandsDelimiter : false,
36647     /**
36648      * @cfg {String} valueAlign alignment of value
36649      */
36650     valueAlign : "left",
36651
36652     getAutoCreate : function()
36653     {
36654         var hiddenInput = {
36655             tag: 'input',
36656             type: 'hidden',
36657             id: Roo.id(),
36658             cls: 'hidden-number-input'
36659         };
36660         
36661         if (this.name) {
36662             hiddenInput.name = this.name;
36663         }
36664         
36665         this.name = '';
36666         
36667         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36668         
36669         this.name = hiddenInput.name;
36670         
36671         if(cfg.cn.length > 0) {
36672             cfg.cn.push(hiddenInput);
36673         }
36674         
36675         return cfg;
36676     },
36677
36678     // private
36679     initEvents : function()
36680     {   
36681         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36682         
36683         var allowed = "0123456789";
36684         
36685         if(this.allowDecimals){
36686             allowed += this.decimalSeparator;
36687         }
36688         
36689         if(this.allowNegative){
36690             allowed += "-";
36691         }
36692         
36693         if(this.thousandsDelimiter) {
36694             allowed += ",";
36695         }
36696         
36697         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36698         
36699         var keyPress = function(e){
36700             
36701             var k = e.getKey();
36702             
36703             var c = e.getCharCode();
36704             
36705             if(
36706                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36707                     allowed.indexOf(String.fromCharCode(c)) === -1
36708             ){
36709                 e.stopEvent();
36710                 return;
36711             }
36712             
36713             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36714                 return;
36715             }
36716             
36717             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36718                 e.stopEvent();
36719             }
36720         };
36721         
36722         this.el.on("keypress", keyPress, this);
36723     },
36724     
36725     validateValue : function(value)
36726     {
36727         
36728         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36729             return false;
36730         }
36731         
36732         var num = this.parseValue(value);
36733         
36734         if(isNaN(num)){
36735             this.markInvalid(String.format(this.nanText, value));
36736             return false;
36737         }
36738         
36739         if(num < this.minValue){
36740             this.markInvalid(String.format(this.minText, this.minValue));
36741             return false;
36742         }
36743         
36744         if(num > this.maxValue){
36745             this.markInvalid(String.format(this.maxText, this.maxValue));
36746             return false;
36747         }
36748         
36749         return true;
36750     },
36751
36752     getValue : function()
36753     {
36754         var v = this.hiddenEl().getValue();
36755         
36756         return this.fixPrecision(this.parseValue(v));
36757     },
36758
36759     parseValue : function(value)
36760     {
36761         if(this.thousandsDelimiter) {
36762             value += "";
36763             r = new RegExp(",", "g");
36764             value = value.replace(r, "");
36765         }
36766         
36767         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36768         return isNaN(value) ? '' : value;
36769     },
36770
36771     fixPrecision : function(value)
36772     {
36773         if(this.thousandsDelimiter) {
36774             value += "";
36775             r = new RegExp(",", "g");
36776             value = value.replace(r, "");
36777         }
36778         
36779         var nan = isNaN(value);
36780         
36781         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36782             return nan ? '' : value;
36783         }
36784         return parseFloat(value).toFixed(this.decimalPrecision);
36785     },
36786
36787     setValue : function(v)
36788     {
36789         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36790         
36791         this.value = v;
36792         
36793         if(this.rendered){
36794             
36795             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36796             
36797             this.inputEl().dom.value = (v == '') ? '' :
36798                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36799             
36800             if(!this.allowZero && v === '0') {
36801                 this.hiddenEl().dom.value = '';
36802                 this.inputEl().dom.value = '';
36803             }
36804             
36805             this.validate();
36806         }
36807     },
36808
36809     decimalPrecisionFcn : function(v)
36810     {
36811         return Math.floor(v);
36812     },
36813
36814     beforeBlur : function()
36815     {
36816         var v = this.parseValue(this.getRawValue());
36817         
36818         if(v || v === 0 || v === ''){
36819             this.setValue(v);
36820         }
36821     },
36822     
36823     hiddenEl : function()
36824     {
36825         return this.el.select('input.hidden-number-input',true).first();
36826     }
36827     
36828 });
36829
36830  
36831
36832 /*
36833 * Licence: LGPL
36834 */
36835
36836 /**
36837  * @class Roo.bootstrap.DocumentSlider
36838  * @extends Roo.bootstrap.Component
36839  * Bootstrap DocumentSlider class
36840  * 
36841  * @constructor
36842  * Create a new DocumentViewer
36843  * @param {Object} config The config object
36844  */
36845
36846 Roo.bootstrap.DocumentSlider = function(config){
36847     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36848     
36849     this.files = [];
36850     
36851     this.addEvents({
36852         /**
36853          * @event initial
36854          * Fire after initEvent
36855          * @param {Roo.bootstrap.DocumentSlider} this
36856          */
36857         "initial" : true,
36858         /**
36859          * @event update
36860          * Fire after update
36861          * @param {Roo.bootstrap.DocumentSlider} this
36862          */
36863         "update" : true,
36864         /**
36865          * @event click
36866          * Fire after click
36867          * @param {Roo.bootstrap.DocumentSlider} this
36868          */
36869         "click" : true
36870     });
36871 };
36872
36873 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36874     
36875     files : false,
36876     
36877     indicator : 0,
36878     
36879     getAutoCreate : function()
36880     {
36881         var cfg = {
36882             tag : 'div',
36883             cls : 'roo-document-slider',
36884             cn : [
36885                 {
36886                     tag : 'div',
36887                     cls : 'roo-document-slider-header',
36888                     cn : [
36889                         {
36890                             tag : 'div',
36891                             cls : 'roo-document-slider-header-title'
36892                         }
36893                     ]
36894                 },
36895                 {
36896                     tag : 'div',
36897                     cls : 'roo-document-slider-body',
36898                     cn : [
36899                         {
36900                             tag : 'div',
36901                             cls : 'roo-document-slider-prev',
36902                             cn : [
36903                                 {
36904                                     tag : 'i',
36905                                     cls : 'fa fa-chevron-left'
36906                                 }
36907                             ]
36908                         },
36909                         {
36910                             tag : 'div',
36911                             cls : 'roo-document-slider-thumb',
36912                             cn : [
36913                                 {
36914                                     tag : 'img',
36915                                     cls : 'roo-document-slider-image'
36916                                 }
36917                             ]
36918                         },
36919                         {
36920                             tag : 'div',
36921                             cls : 'roo-document-slider-next',
36922                             cn : [
36923                                 {
36924                                     tag : 'i',
36925                                     cls : 'fa fa-chevron-right'
36926                                 }
36927                             ]
36928                         }
36929                     ]
36930                 }
36931             ]
36932         };
36933         
36934         return cfg;
36935     },
36936     
36937     initEvents : function()
36938     {
36939         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36940         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36941         
36942         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36943         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36944         
36945         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36946         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36947         
36948         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36949         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36950         
36951         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36952         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36953         
36954         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36955         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36956         
36957         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36958         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36959         
36960         this.thumbEl.on('click', this.onClick, this);
36961         
36962         this.prevIndicator.on('click', this.prev, this);
36963         
36964         this.nextIndicator.on('click', this.next, this);
36965         
36966     },
36967     
36968     initial : function()
36969     {
36970         if(this.files.length){
36971             this.indicator = 1;
36972             this.update()
36973         }
36974         
36975         this.fireEvent('initial', this);
36976     },
36977     
36978     update : function()
36979     {
36980         this.imageEl.attr('src', this.files[this.indicator - 1]);
36981         
36982         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36983         
36984         this.prevIndicator.show();
36985         
36986         if(this.indicator == 1){
36987             this.prevIndicator.hide();
36988         }
36989         
36990         this.nextIndicator.show();
36991         
36992         if(this.indicator == this.files.length){
36993             this.nextIndicator.hide();
36994         }
36995         
36996         this.thumbEl.scrollTo('top');
36997         
36998         this.fireEvent('update', this);
36999     },
37000     
37001     onClick : function(e)
37002     {
37003         e.preventDefault();
37004         
37005         this.fireEvent('click', this);
37006     },
37007     
37008     prev : function(e)
37009     {
37010         e.preventDefault();
37011         
37012         this.indicator = Math.max(1, this.indicator - 1);
37013         
37014         this.update();
37015     },
37016     
37017     next : function(e)
37018     {
37019         e.preventDefault();
37020         
37021         this.indicator = Math.min(this.files.length, this.indicator + 1);
37022         
37023         this.update();
37024     }
37025 });
37026 /*
37027  * - LGPL
37028  *
37029  * RadioSet
37030  *
37031  *
37032  */
37033
37034 /**
37035  * @class Roo.bootstrap.form.RadioSet
37036  * @extends Roo.bootstrap.form.Input
37037  * @children Roo.bootstrap.form.Radio
37038  * Bootstrap RadioSet class
37039  * @cfg {String} indicatorpos (left|right) default left
37040  * @cfg {Boolean} inline (true|false) inline the element (default true)
37041  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37042  * @constructor
37043  * Create a new RadioSet
37044  * @param {Object} config The config object
37045  */
37046
37047 Roo.bootstrap.form.RadioSet = function(config){
37048     
37049     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37050     
37051     this.radioes = [];
37052     
37053     Roo.bootstrap.form.RadioSet.register(this);
37054     
37055     this.addEvents({
37056         /**
37057         * @event check
37058         * Fires when the element is checked or unchecked.
37059         * @param {Roo.bootstrap.form.RadioSet} this This radio
37060         * @param {Roo.bootstrap.form.Radio} item The checked item
37061         */
37062        check : true,
37063        /**
37064         * @event click
37065         * Fires when the element is click.
37066         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37067         * @param {Roo.bootstrap.form.Radio} item The checked item
37068         * @param {Roo.EventObject} e The event object
37069         */
37070        click : true
37071     });
37072     
37073 };
37074
37075 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37076
37077     radioes : false,
37078     
37079     inline : true,
37080     
37081     weight : '',
37082     
37083     indicatorpos : 'left',
37084     
37085     getAutoCreate : function()
37086     {
37087         var label = {
37088             tag : 'label',
37089             cls : 'roo-radio-set-label',
37090             cn : [
37091                 {
37092                     tag : 'span',
37093                     html : this.fieldLabel
37094                 }
37095             ]
37096         };
37097         if (Roo.bootstrap.version == 3) {
37098             
37099             
37100             if(this.indicatorpos == 'left'){
37101                 label.cn.unshift({
37102                     tag : 'i',
37103                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37104                     tooltip : 'This field is required'
37105                 });
37106             } else {
37107                 label.cn.push({
37108                     tag : 'i',
37109                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37110                     tooltip : 'This field is required'
37111                 });
37112             }
37113         }
37114         var items = {
37115             tag : 'div',
37116             cls : 'roo-radio-set-items'
37117         };
37118         
37119         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37120         
37121         if (align === 'left' && this.fieldLabel.length) {
37122             
37123             items = {
37124                 cls : "roo-radio-set-right", 
37125                 cn: [
37126                     items
37127                 ]
37128             };
37129             
37130             if(this.labelWidth > 12){
37131                 label.style = "width: " + this.labelWidth + 'px';
37132             }
37133             
37134             if(this.labelWidth < 13 && this.labelmd == 0){
37135                 this.labelmd = this.labelWidth;
37136             }
37137             
37138             if(this.labellg > 0){
37139                 label.cls += ' col-lg-' + this.labellg;
37140                 items.cls += ' col-lg-' + (12 - this.labellg);
37141             }
37142             
37143             if(this.labelmd > 0){
37144                 label.cls += ' col-md-' + this.labelmd;
37145                 items.cls += ' col-md-' + (12 - this.labelmd);
37146             }
37147             
37148             if(this.labelsm > 0){
37149                 label.cls += ' col-sm-' + this.labelsm;
37150                 items.cls += ' col-sm-' + (12 - this.labelsm);
37151             }
37152             
37153             if(this.labelxs > 0){
37154                 label.cls += ' col-xs-' + this.labelxs;
37155                 items.cls += ' col-xs-' + (12 - this.labelxs);
37156             }
37157         }
37158         
37159         var cfg = {
37160             tag : 'div',
37161             cls : 'roo-radio-set',
37162             cn : [
37163                 {
37164                     tag : 'input',
37165                     cls : 'roo-radio-set-input',
37166                     type : 'hidden',
37167                     name : this.name,
37168                     value : this.value ? this.value :  ''
37169                 },
37170                 label,
37171                 items
37172             ]
37173         };
37174         
37175         if(this.weight.length){
37176             cfg.cls += ' roo-radio-' + this.weight;
37177         }
37178         
37179         if(this.inline) {
37180             cfg.cls += ' roo-radio-set-inline';
37181         }
37182         
37183         var settings=this;
37184         ['xs','sm','md','lg'].map(function(size){
37185             if (settings[size]) {
37186                 cfg.cls += ' col-' + size + '-' + settings[size];
37187             }
37188         });
37189         
37190         return cfg;
37191         
37192     },
37193
37194     initEvents : function()
37195     {
37196         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37197         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37198         
37199         if(!this.fieldLabel.length){
37200             this.labelEl.hide();
37201         }
37202         
37203         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37204         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37205         
37206         this.indicator = this.indicatorEl();
37207         
37208         if(this.indicator){
37209             this.indicator.addClass('invisible');
37210         }
37211         
37212         this.originalValue = this.getValue();
37213         
37214     },
37215     
37216     inputEl: function ()
37217     {
37218         return this.el.select('.roo-radio-set-input', true).first();
37219     },
37220     
37221     getChildContainer : function()
37222     {
37223         return this.itemsEl;
37224     },
37225     
37226     register : function(item)
37227     {
37228         this.radioes.push(item);
37229         
37230     },
37231     
37232     validate : function()
37233     {   
37234         if(this.getVisibilityEl().hasClass('hidden')){
37235             return true;
37236         }
37237         
37238         var valid = false;
37239         
37240         Roo.each(this.radioes, function(i){
37241             if(!i.checked){
37242                 return;
37243             }
37244             
37245             valid = true;
37246             return false;
37247         });
37248         
37249         if(this.allowBlank) {
37250             return true;
37251         }
37252         
37253         if(this.disabled || valid){
37254             this.markValid();
37255             return true;
37256         }
37257         
37258         this.markInvalid();
37259         return false;
37260         
37261     },
37262     
37263     markValid : function()
37264     {
37265         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37266             this.indicatorEl().removeClass('visible');
37267             this.indicatorEl().addClass('invisible');
37268         }
37269         
37270         
37271         if (Roo.bootstrap.version == 3) {
37272             this.el.removeClass([this.invalidClass, this.validClass]);
37273             this.el.addClass(this.validClass);
37274         } else {
37275             this.el.removeClass(['is-invalid','is-valid']);
37276             this.el.addClass(['is-valid']);
37277         }
37278         this.fireEvent('valid', this);
37279     },
37280     
37281     markInvalid : function(msg)
37282     {
37283         if(this.allowBlank || this.disabled){
37284             return;
37285         }
37286         
37287         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37288             this.indicatorEl().removeClass('invisible');
37289             this.indicatorEl().addClass('visible');
37290         }
37291         if (Roo.bootstrap.version == 3) {
37292             this.el.removeClass([this.invalidClass, this.validClass]);
37293             this.el.addClass(this.invalidClass);
37294         } else {
37295             this.el.removeClass(['is-invalid','is-valid']);
37296             this.el.addClass(['is-invalid']);
37297         }
37298         
37299         this.fireEvent('invalid', this, msg);
37300         
37301     },
37302     
37303     setValue : function(v, suppressEvent)
37304     {   
37305         if(this.value === v){
37306             return;
37307         }
37308         
37309         this.value = v;
37310         
37311         if(this.rendered){
37312             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37313         }
37314         
37315         Roo.each(this.radioes, function(i){
37316             i.checked = false;
37317             i.el.removeClass('checked');
37318         });
37319         
37320         Roo.each(this.radioes, function(i){
37321             
37322             if(i.value === v || i.value.toString() === v.toString()){
37323                 i.checked = true;
37324                 i.el.addClass('checked');
37325                 
37326                 if(suppressEvent !== true){
37327                     this.fireEvent('check', this, i);
37328                 }
37329                 
37330                 return false;
37331             }
37332             
37333         }, this);
37334         
37335         this.validate();
37336     },
37337     
37338     clearInvalid : function(){
37339         
37340         if(!this.el || this.preventMark){
37341             return;
37342         }
37343         
37344         this.el.removeClass([this.invalidClass]);
37345         
37346         this.fireEvent('valid', this);
37347     }
37348     
37349 });
37350
37351 Roo.apply(Roo.bootstrap.form.RadioSet, {
37352     
37353     groups: {},
37354     
37355     register : function(set)
37356     {
37357         this.groups[set.name] = set;
37358     },
37359     
37360     get: function(name) 
37361     {
37362         if (typeof(this.groups[name]) == 'undefined') {
37363             return false;
37364         }
37365         
37366         return this.groups[name] ;
37367     }
37368     
37369 });
37370 /*
37371  * Based on:
37372  * Ext JS Library 1.1.1
37373  * Copyright(c) 2006-2007, Ext JS, LLC.
37374  *
37375  * Originally Released Under LGPL - original licence link has changed is not relivant.
37376  *
37377  * Fork - LGPL
37378  * <script type="text/javascript">
37379  */
37380
37381
37382 /**
37383  * @class Roo.bootstrap.SplitBar
37384  * @extends Roo.util.Observable
37385  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37386  * <br><br>
37387  * Usage:
37388  * <pre><code>
37389 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37390                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37391 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37392 split.minSize = 100;
37393 split.maxSize = 600;
37394 split.animate = true;
37395 split.on('moved', splitterMoved);
37396 </code></pre>
37397  * @constructor
37398  * Create a new SplitBar
37399  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37400  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37401  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37402  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37403                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37404                         position of the SplitBar).
37405  */
37406 Roo.bootstrap.SplitBar = function(cfg){
37407     
37408     /** @private */
37409     
37410     //{
37411     //  dragElement : elm
37412     //  resizingElement: el,
37413         // optional..
37414     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37415     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37416         // existingProxy ???
37417     //}
37418     
37419     this.el = Roo.get(cfg.dragElement, true);
37420     this.el.dom.unselectable = "on";
37421     /** @private */
37422     this.resizingEl = Roo.get(cfg.resizingElement, true);
37423
37424     /**
37425      * @private
37426      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37428      * @type Number
37429      */
37430     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37431     
37432     /**
37433      * The minimum size of the resizing element. (Defaults to 0)
37434      * @type Number
37435      */
37436     this.minSize = 0;
37437     
37438     /**
37439      * The maximum size of the resizing element. (Defaults to 2000)
37440      * @type Number
37441      */
37442     this.maxSize = 2000;
37443     
37444     /**
37445      * Whether to animate the transition to the new size
37446      * @type Boolean
37447      */
37448     this.animate = false;
37449     
37450     /**
37451      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37452      * @type Boolean
37453      */
37454     this.useShim = false;
37455     
37456     /** @private */
37457     this.shim = null;
37458     
37459     if(!cfg.existingProxy){
37460         /** @private */
37461         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37462     }else{
37463         this.proxy = Roo.get(cfg.existingProxy).dom;
37464     }
37465     /** @private */
37466     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37467     
37468     /** @private */
37469     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37470     
37471     /** @private */
37472     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37473     
37474     /** @private */
37475     this.dragSpecs = {};
37476     
37477     /**
37478      * @private The adapter to use to positon and resize elements
37479      */
37480     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37481     this.adapter.init(this);
37482     
37483     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37484         /** @private */
37485         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37486         this.el.addClass("roo-splitbar-h");
37487     }else{
37488         /** @private */
37489         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37490         this.el.addClass("roo-splitbar-v");
37491     }
37492     
37493     this.addEvents({
37494         /**
37495          * @event resize
37496          * Fires when the splitter is moved (alias for {@link #event-moved})
37497          * @param {Roo.bootstrap.SplitBar} this
37498          * @param {Number} newSize the new width or height
37499          */
37500         "resize" : true,
37501         /**
37502          * @event moved
37503          * Fires when the splitter is moved
37504          * @param {Roo.bootstrap.SplitBar} this
37505          * @param {Number} newSize the new width or height
37506          */
37507         "moved" : true,
37508         /**
37509          * @event beforeresize
37510          * Fires before the splitter is dragged
37511          * @param {Roo.bootstrap.SplitBar} this
37512          */
37513         "beforeresize" : true,
37514
37515         "beforeapply" : true
37516     });
37517
37518     Roo.util.Observable.call(this);
37519 };
37520
37521 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37522     onStartProxyDrag : function(x, y){
37523         this.fireEvent("beforeresize", this);
37524         if(!this.overlay){
37525             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37526             o.unselectable();
37527             o.enableDisplayMode("block");
37528             // all splitbars share the same overlay
37529             Roo.bootstrap.SplitBar.prototype.overlay = o;
37530         }
37531         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37532         this.overlay.show();
37533         Roo.get(this.proxy).setDisplayed("block");
37534         var size = this.adapter.getElementSize(this);
37535         this.activeMinSize = this.getMinimumSize();;
37536         this.activeMaxSize = this.getMaximumSize();;
37537         var c1 = size - this.activeMinSize;
37538         var c2 = Math.max(this.activeMaxSize - size, 0);
37539         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540             this.dd.resetConstraints();
37541             this.dd.setXConstraint(
37542                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37543                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37544             );
37545             this.dd.setYConstraint(0, 0);
37546         }else{
37547             this.dd.resetConstraints();
37548             this.dd.setXConstraint(0, 0);
37549             this.dd.setYConstraint(
37550                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37551                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37552             );
37553          }
37554         this.dragSpecs.startSize = size;
37555         this.dragSpecs.startPoint = [x, y];
37556         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37557     },
37558     
37559     /** 
37560      * @private Called after the drag operation by the DDProxy
37561      */
37562     onEndProxyDrag : function(e){
37563         Roo.get(this.proxy).setDisplayed(false);
37564         var endPoint = Roo.lib.Event.getXY(e);
37565         if(this.overlay){
37566             this.overlay.hide();
37567         }
37568         var newSize;
37569         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37570             newSize = this.dragSpecs.startSize + 
37571                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37572                     endPoint[0] - this.dragSpecs.startPoint[0] :
37573                     this.dragSpecs.startPoint[0] - endPoint[0]
37574                 );
37575         }else{
37576             newSize = this.dragSpecs.startSize + 
37577                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37578                     endPoint[1] - this.dragSpecs.startPoint[1] :
37579                     this.dragSpecs.startPoint[1] - endPoint[1]
37580                 );
37581         }
37582         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37583         if(newSize != this.dragSpecs.startSize){
37584             if(this.fireEvent('beforeapply', this, newSize) !== false){
37585                 this.adapter.setElementSize(this, newSize);
37586                 this.fireEvent("moved", this, newSize);
37587                 this.fireEvent("resize", this, newSize);
37588             }
37589         }
37590     },
37591     
37592     /**
37593      * Get the adapter this SplitBar uses
37594      * @return The adapter object
37595      */
37596     getAdapter : function(){
37597         return this.adapter;
37598     },
37599     
37600     /**
37601      * Set the adapter this SplitBar uses
37602      * @param {Object} adapter A SplitBar adapter object
37603      */
37604     setAdapter : function(adapter){
37605         this.adapter = adapter;
37606         this.adapter.init(this);
37607     },
37608     
37609     /**
37610      * Gets the minimum size for the resizing element
37611      * @return {Number} The minimum size
37612      */
37613     getMinimumSize : function(){
37614         return this.minSize;
37615     },
37616     
37617     /**
37618      * Sets the minimum size for the resizing element
37619      * @param {Number} minSize The minimum size
37620      */
37621     setMinimumSize : function(minSize){
37622         this.minSize = minSize;
37623     },
37624     
37625     /**
37626      * Gets the maximum size for the resizing element
37627      * @return {Number} The maximum size
37628      */
37629     getMaximumSize : function(){
37630         return this.maxSize;
37631     },
37632     
37633     /**
37634      * Sets the maximum size for the resizing element
37635      * @param {Number} maxSize The maximum size
37636      */
37637     setMaximumSize : function(maxSize){
37638         this.maxSize = maxSize;
37639     },
37640     
37641     /**
37642      * Sets the initialize size for the resizing element
37643      * @param {Number} size The initial size
37644      */
37645     setCurrentSize : function(size){
37646         var oldAnimate = this.animate;
37647         this.animate = false;
37648         this.adapter.setElementSize(this, size);
37649         this.animate = oldAnimate;
37650     },
37651     
37652     /**
37653      * Destroy this splitbar. 
37654      * @param {Boolean} removeEl True to remove the element
37655      */
37656     destroy : function(removeEl){
37657         if(this.shim){
37658             this.shim.remove();
37659         }
37660         this.dd.unreg();
37661         this.proxy.parentNode.removeChild(this.proxy);
37662         if(removeEl){
37663             this.el.remove();
37664         }
37665     }
37666 });
37667
37668 /**
37669  * @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.
37670  */
37671 Roo.bootstrap.SplitBar.createProxy = function(dir){
37672     var proxy = new Roo.Element(document.createElement("div"));
37673     proxy.unselectable();
37674     var cls = 'roo-splitbar-proxy';
37675     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37676     document.body.appendChild(proxy.dom);
37677     return proxy.dom;
37678 };
37679
37680 /** 
37681  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37682  * Default Adapter. It assumes the splitter and resizing element are not positioned
37683  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37684  */
37685 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37686 };
37687
37688 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37689     // do nothing for now
37690     init : function(s){
37691     
37692     },
37693     /**
37694      * Called before drag operations to get the current size of the resizing element. 
37695      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37696      */
37697      getElementSize : function(s){
37698         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37699             return s.resizingEl.getWidth();
37700         }else{
37701             return s.resizingEl.getHeight();
37702         }
37703     },
37704     
37705     /**
37706      * Called after drag operations to set the size of the resizing element.
37707      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37708      * @param {Number} newSize The new size to set
37709      * @param {Function} onComplete A function to be invoked when resizing is complete
37710      */
37711     setElementSize : function(s, newSize, onComplete){
37712         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37713             if(!s.animate){
37714                 s.resizingEl.setWidth(newSize);
37715                 if(onComplete){
37716                     onComplete(s, newSize);
37717                 }
37718             }else{
37719                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37720             }
37721         }else{
37722             
37723             if(!s.animate){
37724                 s.resizingEl.setHeight(newSize);
37725                 if(onComplete){
37726                     onComplete(s, newSize);
37727                 }
37728             }else{
37729                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37730             }
37731         }
37732     }
37733 };
37734
37735 /** 
37736  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37737  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37738  * Adapter that  moves the splitter element to align with the resized sizing element. 
37739  * Used with an absolute positioned SplitBar.
37740  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37741  * document.body, make sure you assign an id to the body element.
37742  */
37743 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37744     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37745     this.container = Roo.get(container);
37746 };
37747
37748 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37749     init : function(s){
37750         this.basic.init(s);
37751     },
37752     
37753     getElementSize : function(s){
37754         return this.basic.getElementSize(s);
37755     },
37756     
37757     setElementSize : function(s, newSize, onComplete){
37758         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37759     },
37760     
37761     moveSplitter : function(s){
37762         var yes = Roo.bootstrap.SplitBar;
37763         switch(s.placement){
37764             case yes.LEFT:
37765                 s.el.setX(s.resizingEl.getRight());
37766                 break;
37767             case yes.RIGHT:
37768                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37769                 break;
37770             case yes.TOP:
37771                 s.el.setY(s.resizingEl.getBottom());
37772                 break;
37773             case yes.BOTTOM:
37774                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37775                 break;
37776         }
37777     }
37778 };
37779
37780 /**
37781  * Orientation constant - Create a vertical SplitBar
37782  * @static
37783  * @type Number
37784  */
37785 Roo.bootstrap.SplitBar.VERTICAL = 1;
37786
37787 /**
37788  * Orientation constant - Create a horizontal SplitBar
37789  * @static
37790  * @type Number
37791  */
37792 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37793
37794 /**
37795  * Placement constant - The resizing element is to the left of the splitter element
37796  * @static
37797  * @type Number
37798  */
37799 Roo.bootstrap.SplitBar.LEFT = 1;
37800
37801 /**
37802  * Placement constant - The resizing element is to the right of the splitter element
37803  * @static
37804  * @type Number
37805  */
37806 Roo.bootstrap.SplitBar.RIGHT = 2;
37807
37808 /**
37809  * Placement constant - The resizing element is positioned above the splitter element
37810  * @static
37811  * @type Number
37812  */
37813 Roo.bootstrap.SplitBar.TOP = 3;
37814
37815 /**
37816  * Placement constant - The resizing element is positioned under splitter element
37817  * @static
37818  * @type Number
37819  */
37820 Roo.bootstrap.SplitBar.BOTTOM = 4;
37821 /*
37822  * Based on:
37823  * Ext JS Library 1.1.1
37824  * Copyright(c) 2006-2007, Ext JS, LLC.
37825  *
37826  * Originally Released Under LGPL - original licence link has changed is not relivant.
37827  *
37828  * Fork - LGPL
37829  * <script type="text/javascript">
37830  */
37831
37832 /**
37833  * @class Roo.bootstrap.layout.Manager
37834  * @extends Roo.bootstrap.Component
37835  * @abstract
37836  * Base class for layout managers.
37837  */
37838 Roo.bootstrap.layout.Manager = function(config)
37839 {
37840     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37841
37842
37843
37844
37845
37846     /** false to disable window resize monitoring @type Boolean */
37847     this.monitorWindowResize = true;
37848     this.regions = {};
37849     this.addEvents({
37850         /**
37851          * @event layout
37852          * Fires when a layout is performed.
37853          * @param {Roo.LayoutManager} this
37854          */
37855         "layout" : true,
37856         /**
37857          * @event regionresized
37858          * Fires when the user resizes a region.
37859          * @param {Roo.LayoutRegion} region The resized region
37860          * @param {Number} newSize The new size (width for east/west, height for north/south)
37861          */
37862         "regionresized" : true,
37863         /**
37864          * @event regioncollapsed
37865          * Fires when a region is collapsed.
37866          * @param {Roo.LayoutRegion} region The collapsed region
37867          */
37868         "regioncollapsed" : true,
37869         /**
37870          * @event regionexpanded
37871          * Fires when a region is expanded.
37872          * @param {Roo.LayoutRegion} region The expanded region
37873          */
37874         "regionexpanded" : true
37875     });
37876     this.updating = false;
37877
37878     if (config.el) {
37879         this.el = Roo.get(config.el);
37880         this.initEvents();
37881     }
37882
37883 };
37884
37885 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37886
37887
37888     regions : null,
37889
37890     monitorWindowResize : true,
37891
37892
37893     updating : false,
37894
37895
37896     onRender : function(ct, position)
37897     {
37898         if(!this.el){
37899             this.el = Roo.get(ct);
37900             this.initEvents();
37901         }
37902         //this.fireEvent('render',this);
37903     },
37904
37905
37906     initEvents: function()
37907     {
37908
37909
37910         // ie scrollbar fix
37911         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37912             document.body.scroll = "no";
37913         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37914             this.el.position('relative');
37915         }
37916         this.id = this.el.id;
37917         this.el.addClass("roo-layout-container");
37918         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37919         if(this.el.dom != document.body ) {
37920             this.el.on('resize', this.layout,this);
37921             this.el.on('show', this.layout,this);
37922         }
37923
37924     },
37925
37926     /**
37927      * Returns true if this layout is currently being updated
37928      * @return {Boolean}
37929      */
37930     isUpdating : function(){
37931         return this.updating;
37932     },
37933
37934     /**
37935      * Suspend the LayoutManager from doing auto-layouts while
37936      * making multiple add or remove calls
37937      */
37938     beginUpdate : function(){
37939         this.updating = true;
37940     },
37941
37942     /**
37943      * Restore auto-layouts and optionally disable the manager from performing a layout
37944      * @param {Boolean} noLayout true to disable a layout update
37945      */
37946     endUpdate : function(noLayout){
37947         this.updating = false;
37948         if(!noLayout){
37949             this.layout();
37950         }
37951     },
37952
37953     layout: function(){
37954         // abstract...
37955     },
37956
37957     onRegionResized : function(region, newSize){
37958         this.fireEvent("regionresized", region, newSize);
37959         this.layout();
37960     },
37961
37962     onRegionCollapsed : function(region){
37963         this.fireEvent("regioncollapsed", region);
37964     },
37965
37966     onRegionExpanded : function(region){
37967         this.fireEvent("regionexpanded", region);
37968     },
37969
37970     /**
37971      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37972      * performs box-model adjustments.
37973      * @return {Object} The size as an object {width: (the width), height: (the height)}
37974      */
37975     getViewSize : function()
37976     {
37977         var size;
37978         if(this.el.dom != document.body){
37979             size = this.el.getSize();
37980         }else{
37981             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37982         }
37983         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37984         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37985         return size;
37986     },
37987
37988     /**
37989      * Returns the Element this layout is bound to.
37990      * @return {Roo.Element}
37991      */
37992     getEl : function(){
37993         return this.el;
37994     },
37995
37996     /**
37997      * Returns the specified region.
37998      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37999      * @return {Roo.LayoutRegion}
38000      */
38001     getRegion : function(target){
38002         return this.regions[target.toLowerCase()];
38003     },
38004
38005     onWindowResize : function(){
38006         if(this.monitorWindowResize){
38007             this.layout();
38008         }
38009     }
38010 });
38011 /*
38012  * Based on:
38013  * Ext JS Library 1.1.1
38014  * Copyright(c) 2006-2007, Ext JS, LLC.
38015  *
38016  * Originally Released Under LGPL - original licence link has changed is not relivant.
38017  *
38018  * Fork - LGPL
38019  * <script type="text/javascript">
38020  */
38021 /**
38022  * @class Roo.bootstrap.layout.Border
38023  * @extends Roo.bootstrap.layout.Manager
38024  * @builder-top
38025  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38026  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38027  * please see: examples/bootstrap/nested.html<br><br>
38028  
38029 <b>The container the layout is rendered into can be either the body element or any other element.
38030 If it is not the body element, the container needs to either be an absolute positioned element,
38031 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38032 the container size if it is not the body element.</b>
38033
38034 * @constructor
38035 * Create a new Border
38036 * @param {Object} config Configuration options
38037  */
38038 Roo.bootstrap.layout.Border = function(config){
38039     config = config || {};
38040     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38041     
38042     
38043     
38044     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38045         if(config[region]){
38046             config[region].region = region;
38047             this.addRegion(config[region]);
38048         }
38049     },this);
38050     
38051 };
38052
38053 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38054
38055 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38056     
38057         /**
38058          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38059          */
38060         /**
38061          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38062          */
38063         /**
38064          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38065          */
38066         /**
38067          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38068          */
38069         /**
38070          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38071          */
38072         
38073         
38074         
38075         
38076     parent : false, // this might point to a 'nest' or a ???
38077     
38078     /**
38079      * Creates and adds a new region if it doesn't already exist.
38080      * @param {String} target The target region key (north, south, east, west or center).
38081      * @param {Object} config The regions config object
38082      * @return {BorderLayoutRegion} The new region
38083      */
38084     addRegion : function(config)
38085     {
38086         if(!this.regions[config.region]){
38087             var r = this.factory(config);
38088             this.bindRegion(r);
38089         }
38090         return this.regions[config.region];
38091     },
38092
38093     // private (kinda)
38094     bindRegion : function(r){
38095         this.regions[r.config.region] = r;
38096         
38097         r.on("visibilitychange",    this.layout, this);
38098         r.on("paneladded",          this.layout, this);
38099         r.on("panelremoved",        this.layout, this);
38100         r.on("invalidated",         this.layout, this);
38101         r.on("resized",             this.onRegionResized, this);
38102         r.on("collapsed",           this.onRegionCollapsed, this);
38103         r.on("expanded",            this.onRegionExpanded, this);
38104     },
38105
38106     /**
38107      * Performs a layout update.
38108      */
38109     layout : function()
38110     {
38111         if(this.updating) {
38112             return;
38113         }
38114         
38115         // render all the rebions if they have not been done alreayd?
38116         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38117             if(this.regions[region] && !this.regions[region].bodyEl){
38118                 this.regions[region].onRender(this.el)
38119             }
38120         },this);
38121         
38122         var size = this.getViewSize();
38123         var w = size.width;
38124         var h = size.height;
38125         var centerW = w;
38126         var centerH = h;
38127         var centerY = 0;
38128         var centerX = 0;
38129         //var x = 0, y = 0;
38130
38131         var rs = this.regions;
38132         var north = rs["north"];
38133         var south = rs["south"]; 
38134         var west = rs["west"];
38135         var east = rs["east"];
38136         var center = rs["center"];
38137         //if(this.hideOnLayout){ // not supported anymore
38138             //c.el.setStyle("display", "none");
38139         //}
38140         if(north && north.isVisible()){
38141             var b = north.getBox();
38142             var m = north.getMargins();
38143             b.width = w - (m.left+m.right);
38144             b.x = m.left;
38145             b.y = m.top;
38146             centerY = b.height + b.y + m.bottom;
38147             centerH -= centerY;
38148             north.updateBox(this.safeBox(b));
38149         }
38150         if(south && south.isVisible()){
38151             var b = south.getBox();
38152             var m = south.getMargins();
38153             b.width = w - (m.left+m.right);
38154             b.x = m.left;
38155             var totalHeight = (b.height + m.top + m.bottom);
38156             b.y = h - totalHeight + m.top;
38157             centerH -= totalHeight;
38158             south.updateBox(this.safeBox(b));
38159         }
38160         if(west && west.isVisible()){
38161             var b = west.getBox();
38162             var m = west.getMargins();
38163             b.height = centerH - (m.top+m.bottom);
38164             b.x = m.left;
38165             b.y = centerY + m.top;
38166             var totalWidth = (b.width + m.left + m.right);
38167             centerX += totalWidth;
38168             centerW -= totalWidth;
38169             west.updateBox(this.safeBox(b));
38170         }
38171         if(east && east.isVisible()){
38172             var b = east.getBox();
38173             var m = east.getMargins();
38174             b.height = centerH - (m.top+m.bottom);
38175             var totalWidth = (b.width + m.left + m.right);
38176             b.x = w - totalWidth + m.left;
38177             b.y = centerY + m.top;
38178             centerW -= totalWidth;
38179             east.updateBox(this.safeBox(b));
38180         }
38181         if(center){
38182             var m = center.getMargins();
38183             var centerBox = {
38184                 x: centerX + m.left,
38185                 y: centerY + m.top,
38186                 width: centerW - (m.left+m.right),
38187                 height: centerH - (m.top+m.bottom)
38188             };
38189             //if(this.hideOnLayout){
38190                 //center.el.setStyle("display", "block");
38191             //}
38192             center.updateBox(this.safeBox(centerBox));
38193         }
38194         this.el.repaint();
38195         this.fireEvent("layout", this);
38196     },
38197
38198     // private
38199     safeBox : function(box){
38200         box.width = Math.max(0, box.width);
38201         box.height = Math.max(0, box.height);
38202         return box;
38203     },
38204
38205     /**
38206      * Adds a ContentPanel (or subclass) to this layout.
38207      * @param {String} target The target region key (north, south, east, west or center).
38208      * @param {Roo.ContentPanel} panel The panel to add
38209      * @return {Roo.ContentPanel} The added panel
38210      */
38211     add : function(target, panel){
38212          
38213         target = target.toLowerCase();
38214         return this.regions[target].add(panel);
38215     },
38216
38217     /**
38218      * Remove a ContentPanel (or subclass) to this layout.
38219      * @param {String} target The target region key (north, south, east, west or center).
38220      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38221      * @return {Roo.ContentPanel} The removed panel
38222      */
38223     remove : function(target, panel){
38224         target = target.toLowerCase();
38225         return this.regions[target].remove(panel);
38226     },
38227
38228     /**
38229      * Searches all regions for a panel with the specified id
38230      * @param {String} panelId
38231      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38232      */
38233     findPanel : function(panelId){
38234         var rs = this.regions;
38235         for(var target in rs){
38236             if(typeof rs[target] != "function"){
38237                 var p = rs[target].getPanel(panelId);
38238                 if(p){
38239                     return p;
38240                 }
38241             }
38242         }
38243         return null;
38244     },
38245
38246     /**
38247      * Searches all regions for a panel with the specified id and activates (shows) it.
38248      * @param {String/ContentPanel} panelId The panels id or the panel itself
38249      * @return {Roo.ContentPanel} The shown panel or null
38250      */
38251     showPanel : function(panelId) {
38252       var rs = this.regions;
38253       for(var target in rs){
38254          var r = rs[target];
38255          if(typeof r != "function"){
38256             if(r.hasPanel(panelId)){
38257                return r.showPanel(panelId);
38258             }
38259          }
38260       }
38261       return null;
38262    },
38263
38264    /**
38265      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38266      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38267      */
38268    /*
38269     restoreState : function(provider){
38270         if(!provider){
38271             provider = Roo.state.Manager;
38272         }
38273         var sm = new Roo.LayoutStateManager();
38274         sm.init(this, provider);
38275     },
38276 */
38277  
38278  
38279     /**
38280      * Adds a xtype elements to the layout.
38281      * <pre><code>
38282
38283 layout.addxtype({
38284        xtype : 'ContentPanel',
38285        region: 'west',
38286        items: [ .... ]
38287    }
38288 );
38289
38290 layout.addxtype({
38291         xtype : 'NestedLayoutPanel',
38292         region: 'west',
38293         layout: {
38294            center: { },
38295            west: { }   
38296         },
38297         items : [ ... list of content panels or nested layout panels.. ]
38298    }
38299 );
38300 </code></pre>
38301      * @param {Object} cfg Xtype definition of item to add.
38302      */
38303     addxtype : function(cfg)
38304     {
38305         // basically accepts a pannel...
38306         // can accept a layout region..!?!?
38307         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38308         
38309         
38310         // theory?  children can only be panels??
38311         
38312         //if (!cfg.xtype.match(/Panel$/)) {
38313         //    return false;
38314         //}
38315         var ret = false;
38316         
38317         if (typeof(cfg.region) == 'undefined') {
38318             Roo.log("Failed to add Panel, region was not set");
38319             Roo.log(cfg);
38320             return false;
38321         }
38322         var region = cfg.region;
38323         delete cfg.region;
38324         
38325           
38326         var xitems = [];
38327         if (cfg.items) {
38328             xitems = cfg.items;
38329             delete cfg.items;
38330         }
38331         var nb = false;
38332         
38333         if ( region == 'center') {
38334             Roo.log("Center: " + cfg.title);
38335         }
38336         
38337         
38338         switch(cfg.xtype) 
38339         {
38340             case 'Content':  // ContentPanel (el, cfg)
38341             case 'Scroll':  // ContentPanel (el, cfg)
38342             case 'View': 
38343                 cfg.autoCreate = cfg.autoCreate || true;
38344                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38345                 //} else {
38346                 //    var el = this.el.createChild();
38347                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38348                 //}
38349                 
38350                 this.add(region, ret);
38351                 break;
38352             
38353             /*
38354             case 'TreePanel': // our new panel!
38355                 cfg.el = this.el.createChild();
38356                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38357                 this.add(region, ret);
38358                 break;
38359             */
38360             
38361             case 'Nest': 
38362                 // create a new Layout (which is  a Border Layout...
38363                 
38364                 var clayout = cfg.layout;
38365                 clayout.el  = this.el.createChild();
38366                 clayout.items   = clayout.items  || [];
38367                 
38368                 delete cfg.layout;
38369                 
38370                 // replace this exitems with the clayout ones..
38371                 xitems = clayout.items;
38372                  
38373                 // force background off if it's in center...
38374                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38375                     cfg.background = false;
38376                 }
38377                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38378                 
38379                 
38380                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38381                 //console.log('adding nested layout panel '  + cfg.toSource());
38382                 this.add(region, ret);
38383                 nb = {}; /// find first...
38384                 break;
38385             
38386             case 'Grid':
38387                 
38388                 // needs grid and region
38389                 
38390                 //var el = this.getRegion(region).el.createChild();
38391                 /*
38392                  *var el = this.el.createChild();
38393                 // create the grid first...
38394                 cfg.grid.container = el;
38395                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38396                 */
38397                 
38398                 if (region == 'center' && this.active ) {
38399                     cfg.background = false;
38400                 }
38401                 
38402                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38403                 
38404                 this.add(region, ret);
38405                 /*
38406                 if (cfg.background) {
38407                     // render grid on panel activation (if panel background)
38408                     ret.on('activate', function(gp) {
38409                         if (!gp.grid.rendered) {
38410                     //        gp.grid.render(el);
38411                         }
38412                     });
38413                 } else {
38414                   //  cfg.grid.render(el);
38415                 }
38416                 */
38417                 break;
38418            
38419            
38420             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38421                 // it was the old xcomponent building that caused this before.
38422                 // espeically if border is the top element in the tree.
38423                 ret = this;
38424                 break; 
38425                 
38426                     
38427                 
38428                 
38429                 
38430             default:
38431                 /*
38432                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38433                     
38434                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38435                     this.add(region, ret);
38436                 } else {
38437                 */
38438                     Roo.log(cfg);
38439                     throw "Can not add '" + cfg.xtype + "' to Border";
38440                     return null;
38441              
38442                                 
38443              
38444         }
38445         this.beginUpdate();
38446         // add children..
38447         var region = '';
38448         var abn = {};
38449         Roo.each(xitems, function(i)  {
38450             region = nb && i.region ? i.region : false;
38451             
38452             var add = ret.addxtype(i);
38453            
38454             if (region) {
38455                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38456                 if (!i.background) {
38457                     abn[region] = nb[region] ;
38458                 }
38459             }
38460             
38461         });
38462         this.endUpdate();
38463
38464         // make the last non-background panel active..
38465         //if (nb) { Roo.log(abn); }
38466         if (nb) {
38467             
38468             for(var r in abn) {
38469                 region = this.getRegion(r);
38470                 if (region) {
38471                     // tried using nb[r], but it does not work..
38472                      
38473                     region.showPanel(abn[r]);
38474                    
38475                 }
38476             }
38477         }
38478         return ret;
38479         
38480     },
38481     
38482     
38483 // private
38484     factory : function(cfg)
38485     {
38486         
38487         var validRegions = Roo.bootstrap.layout.Border.regions;
38488
38489         var target = cfg.region;
38490         cfg.mgr = this;
38491         
38492         var r = Roo.bootstrap.layout;
38493         Roo.log(target);
38494         switch(target){
38495             case "north":
38496                 return new r.North(cfg);
38497             case "south":
38498                 return new r.South(cfg);
38499             case "east":
38500                 return new r.East(cfg);
38501             case "west":
38502                 return new r.West(cfg);
38503             case "center":
38504                 return new r.Center(cfg);
38505         }
38506         throw 'Layout region "'+target+'" not supported.';
38507     }
38508     
38509     
38510 });
38511  /*
38512  * Based on:
38513  * Ext JS Library 1.1.1
38514  * Copyright(c) 2006-2007, Ext JS, LLC.
38515  *
38516  * Originally Released Under LGPL - original licence link has changed is not relivant.
38517  *
38518  * Fork - LGPL
38519  * <script type="text/javascript">
38520  */
38521  
38522 /**
38523  * @class Roo.bootstrap.layout.Basic
38524  * @extends Roo.util.Observable
38525  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38526  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38527  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38528  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38529  * @cfg {string}   region  the region that it inhabits..
38530  * @cfg {bool}   skipConfig skip config?
38531  * 
38532
38533  */
38534 Roo.bootstrap.layout.Basic = function(config){
38535     
38536     this.mgr = config.mgr;
38537     
38538     this.position = config.region;
38539     
38540     var skipConfig = config.skipConfig;
38541     
38542     this.events = {
38543         /**
38544          * @scope Roo.BasicLayoutRegion
38545          */
38546         
38547         /**
38548          * @event beforeremove
38549          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38550          * @param {Roo.LayoutRegion} this
38551          * @param {Roo.ContentPanel} panel The panel
38552          * @param {Object} e The cancel event object
38553          */
38554         "beforeremove" : true,
38555         /**
38556          * @event invalidated
38557          * Fires when the layout for this region is changed.
38558          * @param {Roo.LayoutRegion} this
38559          */
38560         "invalidated" : true,
38561         /**
38562          * @event visibilitychange
38563          * Fires when this region is shown or hidden 
38564          * @param {Roo.LayoutRegion} this
38565          * @param {Boolean} visibility true or false
38566          */
38567         "visibilitychange" : true,
38568         /**
38569          * @event paneladded
38570          * Fires when a panel is added. 
38571          * @param {Roo.LayoutRegion} this
38572          * @param {Roo.ContentPanel} panel The panel
38573          */
38574         "paneladded" : true,
38575         /**
38576          * @event panelremoved
38577          * Fires when a panel is removed. 
38578          * @param {Roo.LayoutRegion} this
38579          * @param {Roo.ContentPanel} panel The panel
38580          */
38581         "panelremoved" : true,
38582         /**
38583          * @event beforecollapse
38584          * Fires when this region before collapse.
38585          * @param {Roo.LayoutRegion} this
38586          */
38587         "beforecollapse" : true,
38588         /**
38589          * @event collapsed
38590          * Fires when this region is collapsed.
38591          * @param {Roo.LayoutRegion} this
38592          */
38593         "collapsed" : true,
38594         /**
38595          * @event expanded
38596          * Fires when this region is expanded.
38597          * @param {Roo.LayoutRegion} this
38598          */
38599         "expanded" : true,
38600         /**
38601          * @event slideshow
38602          * Fires when this region is slid into view.
38603          * @param {Roo.LayoutRegion} this
38604          */
38605         "slideshow" : true,
38606         /**
38607          * @event slidehide
38608          * Fires when this region slides out of view. 
38609          * @param {Roo.LayoutRegion} this
38610          */
38611         "slidehide" : true,
38612         /**
38613          * @event panelactivated
38614          * Fires when a panel is activated. 
38615          * @param {Roo.LayoutRegion} this
38616          * @param {Roo.ContentPanel} panel The activated panel
38617          */
38618         "panelactivated" : true,
38619         /**
38620          * @event resized
38621          * Fires when the user resizes this region. 
38622          * @param {Roo.LayoutRegion} this
38623          * @param {Number} newSize The new size (width for east/west, height for north/south)
38624          */
38625         "resized" : true
38626     };
38627     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38628     this.panels = new Roo.util.MixedCollection();
38629     this.panels.getKey = this.getPanelId.createDelegate(this);
38630     this.box = null;
38631     this.activePanel = null;
38632     // ensure listeners are added...
38633     
38634     if (config.listeners || config.events) {
38635         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38636             listeners : config.listeners || {},
38637             events : config.events || {}
38638         });
38639     }
38640     
38641     if(skipConfig !== true){
38642         this.applyConfig(config);
38643     }
38644 };
38645
38646 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38647 {
38648     getPanelId : function(p){
38649         return p.getId();
38650     },
38651     
38652     applyConfig : function(config){
38653         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38654         this.config = config;
38655         
38656     },
38657     
38658     /**
38659      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38660      * the width, for horizontal (north, south) the height.
38661      * @param {Number} newSize The new width or height
38662      */
38663     resizeTo : function(newSize){
38664         var el = this.el ? this.el :
38665                  (this.activePanel ? this.activePanel.getEl() : null);
38666         if(el){
38667             switch(this.position){
38668                 case "east":
38669                 case "west":
38670                     el.setWidth(newSize);
38671                     this.fireEvent("resized", this, newSize);
38672                 break;
38673                 case "north":
38674                 case "south":
38675                     el.setHeight(newSize);
38676                     this.fireEvent("resized", this, newSize);
38677                 break;                
38678             }
38679         }
38680     },
38681     
38682     getBox : function(){
38683         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38684     },
38685     
38686     getMargins : function(){
38687         return this.margins;
38688     },
38689     
38690     updateBox : function(box){
38691         this.box = box;
38692         var el = this.activePanel.getEl();
38693         el.dom.style.left = box.x + "px";
38694         el.dom.style.top = box.y + "px";
38695         this.activePanel.setSize(box.width, box.height);
38696     },
38697     
38698     /**
38699      * Returns the container element for this region.
38700      * @return {Roo.Element}
38701      */
38702     getEl : function(){
38703         return this.activePanel;
38704     },
38705     
38706     /**
38707      * Returns true if this region is currently visible.
38708      * @return {Boolean}
38709      */
38710     isVisible : function(){
38711         return this.activePanel ? true : false;
38712     },
38713     
38714     setActivePanel : function(panel){
38715         panel = this.getPanel(panel);
38716         if(this.activePanel && this.activePanel != panel){
38717             this.activePanel.setActiveState(false);
38718             this.activePanel.getEl().setLeftTop(-10000,-10000);
38719         }
38720         this.activePanel = panel;
38721         panel.setActiveState(true);
38722         if(this.box){
38723             panel.setSize(this.box.width, this.box.height);
38724         }
38725         this.fireEvent("panelactivated", this, panel);
38726         this.fireEvent("invalidated");
38727     },
38728     
38729     /**
38730      * Show the specified panel.
38731      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38732      * @return {Roo.ContentPanel} The shown panel or null
38733      */
38734     showPanel : function(panel){
38735         panel = this.getPanel(panel);
38736         if(panel){
38737             this.setActivePanel(panel);
38738         }
38739         return panel;
38740     },
38741     
38742     /**
38743      * Get the active panel for this region.
38744      * @return {Roo.ContentPanel} The active panel or null
38745      */
38746     getActivePanel : function(){
38747         return this.activePanel;
38748     },
38749     
38750     /**
38751      * Add the passed ContentPanel(s)
38752      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38753      * @return {Roo.ContentPanel} The panel added (if only one was added)
38754      */
38755     add : function(panel){
38756         if(arguments.length > 1){
38757             for(var i = 0, len = arguments.length; i < len; i++) {
38758                 this.add(arguments[i]);
38759             }
38760             return null;
38761         }
38762         if(this.hasPanel(panel)){
38763             this.showPanel(panel);
38764             return panel;
38765         }
38766         var el = panel.getEl();
38767         if(el.dom.parentNode != this.mgr.el.dom){
38768             this.mgr.el.dom.appendChild(el.dom);
38769         }
38770         if(panel.setRegion){
38771             panel.setRegion(this);
38772         }
38773         this.panels.add(panel);
38774         el.setStyle("position", "absolute");
38775         if(!panel.background){
38776             this.setActivePanel(panel);
38777             if(this.config.initialSize && this.panels.getCount()==1){
38778                 this.resizeTo(this.config.initialSize);
38779             }
38780         }
38781         this.fireEvent("paneladded", this, panel);
38782         return panel;
38783     },
38784     
38785     /**
38786      * Returns true if the panel is in this region.
38787      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38788      * @return {Boolean}
38789      */
38790     hasPanel : function(panel){
38791         if(typeof panel == "object"){ // must be panel obj
38792             panel = panel.getId();
38793         }
38794         return this.getPanel(panel) ? true : false;
38795     },
38796     
38797     /**
38798      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38799      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38800      * @param {Boolean} preservePanel Overrides the config preservePanel option
38801      * @return {Roo.ContentPanel} The panel that was removed
38802      */
38803     remove : function(panel, preservePanel){
38804         panel = this.getPanel(panel);
38805         if(!panel){
38806             return null;
38807         }
38808         var e = {};
38809         this.fireEvent("beforeremove", this, panel, e);
38810         if(e.cancel === true){
38811             return null;
38812         }
38813         var panelId = panel.getId();
38814         this.panels.removeKey(panelId);
38815         return panel;
38816     },
38817     
38818     /**
38819      * Returns the panel specified or null if it's not in this region.
38820      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38821      * @return {Roo.ContentPanel}
38822      */
38823     getPanel : function(id){
38824         if(typeof id == "object"){ // must be panel obj
38825             return id;
38826         }
38827         return this.panels.get(id);
38828     },
38829     
38830     /**
38831      * Returns this regions position (north/south/east/west/center).
38832      * @return {String} 
38833      */
38834     getPosition: function(){
38835         return this.position;    
38836     }
38837 });/*
38838  * Based on:
38839  * Ext JS Library 1.1.1
38840  * Copyright(c) 2006-2007, Ext JS, LLC.
38841  *
38842  * Originally Released Under LGPL - original licence link has changed is not relivant.
38843  *
38844  * Fork - LGPL
38845  * <script type="text/javascript">
38846  */
38847  
38848 /**
38849  * @class Roo.bootstrap.layout.Region
38850  * @extends Roo.bootstrap.layout.Basic
38851  * This class represents a region in a layout manager.
38852  
38853  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38854  * @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})
38855  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38856  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38857  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38858  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38859  * @cfg {String}    title           The title for the region (overrides panel titles)
38860  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38861  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38862  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38863  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38864  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38865  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38866  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38867  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38868  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38869  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38870
38871  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38872  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38873  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38874  * @cfg {Number}    width           For East/West panels
38875  * @cfg {Number}    height          For North/South panels
38876  * @cfg {Boolean}   split           To show the splitter
38877  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38878  * 
38879  * @cfg {string}   cls             Extra CSS classes to add to region
38880  * 
38881  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38882  * @cfg {string}   region  the region that it inhabits..
38883  *
38884
38885  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38886  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38887
38888  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38889  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38890  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38891  */
38892 Roo.bootstrap.layout.Region = function(config)
38893 {
38894     this.applyConfig(config);
38895
38896     var mgr = config.mgr;
38897     var pos = config.region;
38898     config.skipConfig = true;
38899     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38900     
38901     if (mgr.el) {
38902         this.onRender(mgr.el);   
38903     }
38904      
38905     this.visible = true;
38906     this.collapsed = false;
38907     this.unrendered_panels = [];
38908 };
38909
38910 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38911
38912     position: '', // set by wrapper (eg. north/south etc..)
38913     unrendered_panels : null,  // unrendered panels.
38914     
38915     tabPosition : false,
38916     
38917     mgr: false, // points to 'Border'
38918     
38919     
38920     createBody : function(){
38921         /** This region's body element 
38922         * @type Roo.Element */
38923         this.bodyEl = this.el.createChild({
38924                 tag: "div",
38925                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38926         });
38927     },
38928
38929     onRender: function(ctr, pos)
38930     {
38931         var dh = Roo.DomHelper;
38932         /** This region's container element 
38933         * @type Roo.Element */
38934         this.el = dh.append(ctr.dom, {
38935                 tag: "div",
38936                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38937             }, true);
38938         /** This region's title element 
38939         * @type Roo.Element */
38940     
38941         this.titleEl = dh.append(this.el.dom,  {
38942                 tag: "div",
38943                 unselectable: "on",
38944                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38945                 children:[
38946                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38947                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38948                 ]
38949             }, true);
38950         
38951         this.titleEl.enableDisplayMode();
38952         /** This region's title text element 
38953         * @type HTMLElement */
38954         this.titleTextEl = this.titleEl.dom.firstChild;
38955         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38956         /*
38957         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38958         this.closeBtn.enableDisplayMode();
38959         this.closeBtn.on("click", this.closeClicked, this);
38960         this.closeBtn.hide();
38961     */
38962         this.createBody(this.config);
38963         if(this.config.hideWhenEmpty){
38964             this.hide();
38965             this.on("paneladded", this.validateVisibility, this);
38966             this.on("panelremoved", this.validateVisibility, this);
38967         }
38968         if(this.autoScroll){
38969             this.bodyEl.setStyle("overflow", "auto");
38970         }else{
38971             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38972         }
38973         //if(c.titlebar !== false){
38974             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38975                 this.titleEl.hide();
38976             }else{
38977                 this.titleEl.show();
38978                 if(this.config.title){
38979                     this.titleTextEl.innerHTML = this.config.title;
38980                 }
38981             }
38982         //}
38983         if(this.config.collapsed){
38984             this.collapse(true);
38985         }
38986         if(this.config.hidden){
38987             this.hide();
38988         }
38989         
38990         if (this.unrendered_panels && this.unrendered_panels.length) {
38991             for (var i =0;i< this.unrendered_panels.length; i++) {
38992                 this.add(this.unrendered_panels[i]);
38993             }
38994             this.unrendered_panels = null;
38995             
38996         }
38997         
38998     },
38999     
39000     applyConfig : function(c)
39001     {
39002         /*
39003          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39004             var dh = Roo.DomHelper;
39005             if(c.titlebar !== false){
39006                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39007                 this.collapseBtn.on("click", this.collapse, this);
39008                 this.collapseBtn.enableDisplayMode();
39009                 /*
39010                 if(c.showPin === true || this.showPin){
39011                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39012                     this.stickBtn.enableDisplayMode();
39013                     this.stickBtn.on("click", this.expand, this);
39014                     this.stickBtn.hide();
39015                 }
39016                 
39017             }
39018             */
39019             /** This region's collapsed element
39020             * @type Roo.Element */
39021             /*
39022              *
39023             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39024                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39025             ]}, true);
39026             
39027             if(c.floatable !== false){
39028                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39029                this.collapsedEl.on("click", this.collapseClick, this);
39030             }
39031
39032             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39033                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39034                    id: "message", unselectable: "on", style:{"float":"left"}});
39035                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39036              }
39037             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39038             this.expandBtn.on("click", this.expand, this);
39039             
39040         }
39041         
39042         if(this.collapseBtn){
39043             this.collapseBtn.setVisible(c.collapsible == true);
39044         }
39045         
39046         this.cmargins = c.cmargins || this.cmargins ||
39047                          (this.position == "west" || this.position == "east" ?
39048                              {top: 0, left: 2, right:2, bottom: 0} :
39049                              {top: 2, left: 0, right:0, bottom: 2});
39050         */
39051         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39052         
39053         
39054         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39055         
39056         this.autoScroll = c.autoScroll || false;
39057         
39058         
39059        
39060         
39061         this.duration = c.duration || .30;
39062         this.slideDuration = c.slideDuration || .45;
39063         this.config = c;
39064        
39065     },
39066     /**
39067      * Returns true if this region is currently visible.
39068      * @return {Boolean}
39069      */
39070     isVisible : function(){
39071         return this.visible;
39072     },
39073
39074     /**
39075      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39076      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39077      */
39078     //setCollapsedTitle : function(title){
39079     //    title = title || "&#160;";
39080      //   if(this.collapsedTitleTextEl){
39081       //      this.collapsedTitleTextEl.innerHTML = title;
39082        // }
39083     //},
39084
39085     getBox : function(){
39086         var b;
39087       //  if(!this.collapsed){
39088             b = this.el.getBox(false, true);
39089        // }else{
39090           //  b = this.collapsedEl.getBox(false, true);
39091         //}
39092         return b;
39093     },
39094
39095     getMargins : function(){
39096         return this.margins;
39097         //return this.collapsed ? this.cmargins : this.margins;
39098     },
39099 /*
39100     highlight : function(){
39101         this.el.addClass("x-layout-panel-dragover");
39102     },
39103
39104     unhighlight : function(){
39105         this.el.removeClass("x-layout-panel-dragover");
39106     },
39107 */
39108     updateBox : function(box)
39109     {
39110         if (!this.bodyEl) {
39111             return; // not rendered yet..
39112         }
39113         
39114         this.box = box;
39115         if(!this.collapsed){
39116             this.el.dom.style.left = box.x + "px";
39117             this.el.dom.style.top = box.y + "px";
39118             this.updateBody(box.width, box.height);
39119         }else{
39120             this.collapsedEl.dom.style.left = box.x + "px";
39121             this.collapsedEl.dom.style.top = box.y + "px";
39122             this.collapsedEl.setSize(box.width, box.height);
39123         }
39124         if(this.tabs){
39125             this.tabs.autoSizeTabs();
39126         }
39127     },
39128
39129     updateBody : function(w, h)
39130     {
39131         if(w !== null){
39132             this.el.setWidth(w);
39133             w -= this.el.getBorderWidth("rl");
39134             if(this.config.adjustments){
39135                 w += this.config.adjustments[0];
39136             }
39137         }
39138         if(h !== null && h > 0){
39139             this.el.setHeight(h);
39140             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39141             h -= this.el.getBorderWidth("tb");
39142             if(this.config.adjustments){
39143                 h += this.config.adjustments[1];
39144             }
39145             this.bodyEl.setHeight(h);
39146             if(this.tabs){
39147                 h = this.tabs.syncHeight(h);
39148             }
39149         }
39150         if(this.panelSize){
39151             w = w !== null ? w : this.panelSize.width;
39152             h = h !== null ? h : this.panelSize.height;
39153         }
39154         if(this.activePanel){
39155             var el = this.activePanel.getEl();
39156             w = w !== null ? w : el.getWidth();
39157             h = h !== null ? h : el.getHeight();
39158             this.panelSize = {width: w, height: h};
39159             this.activePanel.setSize(w, h);
39160         }
39161         if(Roo.isIE && this.tabs){
39162             this.tabs.el.repaint();
39163         }
39164     },
39165
39166     /**
39167      * Returns the container element for this region.
39168      * @return {Roo.Element}
39169      */
39170     getEl : function(){
39171         return this.el;
39172     },
39173
39174     /**
39175      * Hides this region.
39176      */
39177     hide : function(){
39178         //if(!this.collapsed){
39179             this.el.dom.style.left = "-2000px";
39180             this.el.hide();
39181         //}else{
39182          //   this.collapsedEl.dom.style.left = "-2000px";
39183          //   this.collapsedEl.hide();
39184        // }
39185         this.visible = false;
39186         this.fireEvent("visibilitychange", this, false);
39187     },
39188
39189     /**
39190      * Shows this region if it was previously hidden.
39191      */
39192     show : function(){
39193         //if(!this.collapsed){
39194             this.el.show();
39195         //}else{
39196         //    this.collapsedEl.show();
39197        // }
39198         this.visible = true;
39199         this.fireEvent("visibilitychange", this, true);
39200     },
39201 /*
39202     closeClicked : function(){
39203         if(this.activePanel){
39204             this.remove(this.activePanel);
39205         }
39206     },
39207
39208     collapseClick : function(e){
39209         if(this.isSlid){
39210            e.stopPropagation();
39211            this.slideIn();
39212         }else{
39213            e.stopPropagation();
39214            this.slideOut();
39215         }
39216     },
39217 */
39218     /**
39219      * Collapses this region.
39220      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39221      */
39222     /*
39223     collapse : function(skipAnim, skipCheck = false){
39224         if(this.collapsed) {
39225             return;
39226         }
39227         
39228         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39229             
39230             this.collapsed = true;
39231             if(this.split){
39232                 this.split.el.hide();
39233             }
39234             if(this.config.animate && skipAnim !== true){
39235                 this.fireEvent("invalidated", this);
39236                 this.animateCollapse();
39237             }else{
39238                 this.el.setLocation(-20000,-20000);
39239                 this.el.hide();
39240                 this.collapsedEl.show();
39241                 this.fireEvent("collapsed", this);
39242                 this.fireEvent("invalidated", this);
39243             }
39244         }
39245         
39246     },
39247 */
39248     animateCollapse : function(){
39249         // overridden
39250     },
39251
39252     /**
39253      * Expands this region if it was previously collapsed.
39254      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39255      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39256      */
39257     /*
39258     expand : function(e, skipAnim){
39259         if(e) {
39260             e.stopPropagation();
39261         }
39262         if(!this.collapsed || this.el.hasActiveFx()) {
39263             return;
39264         }
39265         if(this.isSlid){
39266             this.afterSlideIn();
39267             skipAnim = true;
39268         }
39269         this.collapsed = false;
39270         if(this.config.animate && skipAnim !== true){
39271             this.animateExpand();
39272         }else{
39273             this.el.show();
39274             if(this.split){
39275                 this.split.el.show();
39276             }
39277             this.collapsedEl.setLocation(-2000,-2000);
39278             this.collapsedEl.hide();
39279             this.fireEvent("invalidated", this);
39280             this.fireEvent("expanded", this);
39281         }
39282     },
39283 */
39284     animateExpand : function(){
39285         // overridden
39286     },
39287
39288     initTabs : function()
39289     {
39290         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39291         
39292         var ts = new Roo.bootstrap.panel.Tabs({
39293             el: this.bodyEl.dom,
39294             region : this,
39295             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39296             disableTooltips: this.config.disableTabTips,
39297             toolbar : this.config.toolbar
39298         });
39299         
39300         if(this.config.hideTabs){
39301             ts.stripWrap.setDisplayed(false);
39302         }
39303         this.tabs = ts;
39304         ts.resizeTabs = this.config.resizeTabs === true;
39305         ts.minTabWidth = this.config.minTabWidth || 40;
39306         ts.maxTabWidth = this.config.maxTabWidth || 250;
39307         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39308         ts.monitorResize = false;
39309         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39310         ts.bodyEl.addClass('roo-layout-tabs-body');
39311         this.panels.each(this.initPanelAsTab, this);
39312     },
39313
39314     initPanelAsTab : function(panel){
39315         var ti = this.tabs.addTab(
39316             panel.getEl().id,
39317             panel.getTitle(),
39318             null,
39319             this.config.closeOnTab && panel.isClosable(),
39320             panel.tpl
39321         );
39322         if(panel.tabTip !== undefined){
39323             ti.setTooltip(panel.tabTip);
39324         }
39325         ti.on("activate", function(){
39326               this.setActivePanel(panel);
39327         }, this);
39328         
39329         if(this.config.closeOnTab){
39330             ti.on("beforeclose", function(t, e){
39331                 e.cancel = true;
39332                 this.remove(panel);
39333             }, this);
39334         }
39335         
39336         panel.tabItem = ti;
39337         
39338         return ti;
39339     },
39340
39341     updatePanelTitle : function(panel, title)
39342     {
39343         if(this.activePanel == panel){
39344             this.updateTitle(title);
39345         }
39346         if(this.tabs){
39347             var ti = this.tabs.getTab(panel.getEl().id);
39348             ti.setText(title);
39349             if(panel.tabTip !== undefined){
39350                 ti.setTooltip(panel.tabTip);
39351             }
39352         }
39353     },
39354
39355     updateTitle : function(title){
39356         if(this.titleTextEl && !this.config.title){
39357             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39358         }
39359     },
39360
39361     setActivePanel : function(panel)
39362     {
39363         panel = this.getPanel(panel);
39364         if(this.activePanel && this.activePanel != panel){
39365             if(this.activePanel.setActiveState(false) === false){
39366                 return;
39367             }
39368         }
39369         this.activePanel = panel;
39370         panel.setActiveState(true);
39371         if(this.panelSize){
39372             panel.setSize(this.panelSize.width, this.panelSize.height);
39373         }
39374         if(this.closeBtn){
39375             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39376         }
39377         this.updateTitle(panel.getTitle());
39378         if(this.tabs){
39379             this.fireEvent("invalidated", this);
39380         }
39381         this.fireEvent("panelactivated", this, panel);
39382     },
39383
39384     /**
39385      * Shows the specified panel.
39386      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39387      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39388      */
39389     showPanel : function(panel)
39390     {
39391         panel = this.getPanel(panel);
39392         if(panel){
39393             if(this.tabs){
39394                 var tab = this.tabs.getTab(panel.getEl().id);
39395                 if(tab.isHidden()){
39396                     this.tabs.unhideTab(tab.id);
39397                 }
39398                 tab.activate();
39399             }else{
39400                 this.setActivePanel(panel);
39401             }
39402         }
39403         return panel;
39404     },
39405
39406     /**
39407      * Get the active panel for this region.
39408      * @return {Roo.ContentPanel} The active panel or null
39409      */
39410     getActivePanel : function(){
39411         return this.activePanel;
39412     },
39413
39414     validateVisibility : function(){
39415         if(this.panels.getCount() < 1){
39416             this.updateTitle("&#160;");
39417             this.closeBtn.hide();
39418             this.hide();
39419         }else{
39420             if(!this.isVisible()){
39421                 this.show();
39422             }
39423         }
39424     },
39425
39426     /**
39427      * Adds the passed ContentPanel(s) to this region.
39428      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39429      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39430      */
39431     add : function(panel)
39432     {
39433         if(arguments.length > 1){
39434             for(var i = 0, len = arguments.length; i < len; i++) {
39435                 this.add(arguments[i]);
39436             }
39437             return null;
39438         }
39439         
39440         // if we have not been rendered yet, then we can not really do much of this..
39441         if (!this.bodyEl) {
39442             this.unrendered_panels.push(panel);
39443             return panel;
39444         }
39445         
39446         
39447         
39448         
39449         if(this.hasPanel(panel)){
39450             this.showPanel(panel);
39451             return panel;
39452         }
39453         panel.setRegion(this);
39454         this.panels.add(panel);
39455        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39456             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39457             // and hide them... ???
39458             this.bodyEl.dom.appendChild(panel.getEl().dom);
39459             if(panel.background !== true){
39460                 this.setActivePanel(panel);
39461             }
39462             this.fireEvent("paneladded", this, panel);
39463             return panel;
39464         }
39465         */
39466         if(!this.tabs){
39467             this.initTabs();
39468         }else{
39469             this.initPanelAsTab(panel);
39470         }
39471         
39472         
39473         if(panel.background !== true){
39474             this.tabs.activate(panel.getEl().id);
39475         }
39476         this.fireEvent("paneladded", this, panel);
39477         return panel;
39478     },
39479
39480     /**
39481      * Hides the tab for the specified panel.
39482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39483      */
39484     hidePanel : function(panel){
39485         if(this.tabs && (panel = this.getPanel(panel))){
39486             this.tabs.hideTab(panel.getEl().id);
39487         }
39488     },
39489
39490     /**
39491      * Unhides the tab for a previously hidden panel.
39492      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39493      */
39494     unhidePanel : function(panel){
39495         if(this.tabs && (panel = this.getPanel(panel))){
39496             this.tabs.unhideTab(panel.getEl().id);
39497         }
39498     },
39499
39500     clearPanels : function(){
39501         while(this.panels.getCount() > 0){
39502              this.remove(this.panels.first());
39503         }
39504     },
39505
39506     /**
39507      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39508      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39509      * @param {Boolean} preservePanel Overrides the config preservePanel option
39510      * @return {Roo.ContentPanel} The panel that was removed
39511      */
39512     remove : function(panel, preservePanel)
39513     {
39514         panel = this.getPanel(panel);
39515         if(!panel){
39516             return null;
39517         }
39518         var e = {};
39519         this.fireEvent("beforeremove", this, panel, e);
39520         if(e.cancel === true){
39521             return null;
39522         }
39523         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39524         var panelId = panel.getId();
39525         this.panels.removeKey(panelId);
39526         if(preservePanel){
39527             document.body.appendChild(panel.getEl().dom);
39528         }
39529         if(this.tabs){
39530             this.tabs.removeTab(panel.getEl().id);
39531         }else if (!preservePanel){
39532             this.bodyEl.dom.removeChild(panel.getEl().dom);
39533         }
39534         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39535             var p = this.panels.first();
39536             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39537             tempEl.appendChild(p.getEl().dom);
39538             this.bodyEl.update("");
39539             this.bodyEl.dom.appendChild(p.getEl().dom);
39540             tempEl = null;
39541             this.updateTitle(p.getTitle());
39542             this.tabs = null;
39543             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39544             this.setActivePanel(p);
39545         }
39546         panel.setRegion(null);
39547         if(this.activePanel == panel){
39548             this.activePanel = null;
39549         }
39550         if(this.config.autoDestroy !== false && preservePanel !== true){
39551             try{panel.destroy();}catch(e){}
39552         }
39553         this.fireEvent("panelremoved", this, panel);
39554         return panel;
39555     },
39556
39557     /**
39558      * Returns the TabPanel component used by this region
39559      * @return {Roo.TabPanel}
39560      */
39561     getTabs : function(){
39562         return this.tabs;
39563     },
39564
39565     createTool : function(parentEl, className){
39566         var btn = Roo.DomHelper.append(parentEl, {
39567             tag: "div",
39568             cls: "x-layout-tools-button",
39569             children: [ {
39570                 tag: "div",
39571                 cls: "roo-layout-tools-button-inner " + className,
39572                 html: "&#160;"
39573             }]
39574         }, true);
39575         btn.addClassOnOver("roo-layout-tools-button-over");
39576         return btn;
39577     }
39578 });/*
39579  * Based on:
39580  * Ext JS Library 1.1.1
39581  * Copyright(c) 2006-2007, Ext JS, LLC.
39582  *
39583  * Originally Released Under LGPL - original licence link has changed is not relivant.
39584  *
39585  * Fork - LGPL
39586  * <script type="text/javascript">
39587  */
39588  
39589
39590
39591 /**
39592  * @class Roo.SplitLayoutRegion
39593  * @extends Roo.LayoutRegion
39594  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39595  */
39596 Roo.bootstrap.layout.Split = function(config){
39597     this.cursor = config.cursor;
39598     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39599 };
39600
39601 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39602 {
39603     splitTip : "Drag to resize.",
39604     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39605     useSplitTips : false,
39606
39607     applyConfig : function(config){
39608         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39609     },
39610     
39611     onRender : function(ctr,pos) {
39612         
39613         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39614         if(!this.config.split){
39615             return;
39616         }
39617         if(!this.split){
39618             
39619             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39620                             tag: "div",
39621                             id: this.el.id + "-split",
39622                             cls: "roo-layout-split roo-layout-split-"+this.position,
39623                             html: "&#160;"
39624             });
39625             /** The SplitBar for this region 
39626             * @type Roo.SplitBar */
39627             // does not exist yet...
39628             Roo.log([this.position, this.orientation]);
39629             
39630             this.split = new Roo.bootstrap.SplitBar({
39631                 dragElement : splitEl,
39632                 resizingElement: this.el,
39633                 orientation : this.orientation
39634             });
39635             
39636             this.split.on("moved", this.onSplitMove, this);
39637             this.split.useShim = this.config.useShim === true;
39638             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39639             if(this.useSplitTips){
39640                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39641             }
39642             //if(config.collapsible){
39643             //    this.split.el.on("dblclick", this.collapse,  this);
39644             //}
39645         }
39646         if(typeof this.config.minSize != "undefined"){
39647             this.split.minSize = this.config.minSize;
39648         }
39649         if(typeof this.config.maxSize != "undefined"){
39650             this.split.maxSize = this.config.maxSize;
39651         }
39652         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39653             this.hideSplitter();
39654         }
39655         
39656     },
39657
39658     getHMaxSize : function(){
39659          var cmax = this.config.maxSize || 10000;
39660          var center = this.mgr.getRegion("center");
39661          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39662     },
39663
39664     getVMaxSize : function(){
39665          var cmax = this.config.maxSize || 10000;
39666          var center = this.mgr.getRegion("center");
39667          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39668     },
39669
39670     onSplitMove : function(split, newSize){
39671         this.fireEvent("resized", this, newSize);
39672     },
39673     
39674     /** 
39675      * Returns the {@link Roo.SplitBar} for this region.
39676      * @return {Roo.SplitBar}
39677      */
39678     getSplitBar : function(){
39679         return this.split;
39680     },
39681     
39682     hide : function(){
39683         this.hideSplitter();
39684         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39685     },
39686
39687     hideSplitter : function(){
39688         if(this.split){
39689             this.split.el.setLocation(-2000,-2000);
39690             this.split.el.hide();
39691         }
39692     },
39693
39694     show : function(){
39695         if(this.split){
39696             this.split.el.show();
39697         }
39698         Roo.bootstrap.layout.Split.superclass.show.call(this);
39699     },
39700     
39701     beforeSlide: function(){
39702         if(Roo.isGecko){// firefox overflow auto bug workaround
39703             this.bodyEl.clip();
39704             if(this.tabs) {
39705                 this.tabs.bodyEl.clip();
39706             }
39707             if(this.activePanel){
39708                 this.activePanel.getEl().clip();
39709                 
39710                 if(this.activePanel.beforeSlide){
39711                     this.activePanel.beforeSlide();
39712                 }
39713             }
39714         }
39715     },
39716     
39717     afterSlide : function(){
39718         if(Roo.isGecko){// firefox overflow auto bug workaround
39719             this.bodyEl.unclip();
39720             if(this.tabs) {
39721                 this.tabs.bodyEl.unclip();
39722             }
39723             if(this.activePanel){
39724                 this.activePanel.getEl().unclip();
39725                 if(this.activePanel.afterSlide){
39726                     this.activePanel.afterSlide();
39727                 }
39728             }
39729         }
39730     },
39731
39732     initAutoHide : function(){
39733         if(this.autoHide !== false){
39734             if(!this.autoHideHd){
39735                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39736                 this.autoHideHd = {
39737                     "mouseout": function(e){
39738                         if(!e.within(this.el, true)){
39739                             st.delay(500);
39740                         }
39741                     },
39742                     "mouseover" : function(e){
39743                         st.cancel();
39744                     },
39745                     scope : this
39746                 };
39747             }
39748             this.el.on(this.autoHideHd);
39749         }
39750     },
39751
39752     clearAutoHide : function(){
39753         if(this.autoHide !== false){
39754             this.el.un("mouseout", this.autoHideHd.mouseout);
39755             this.el.un("mouseover", this.autoHideHd.mouseover);
39756         }
39757     },
39758
39759     clearMonitor : function(){
39760         Roo.get(document).un("click", this.slideInIf, this);
39761     },
39762
39763     // these names are backwards but not changed for compat
39764     slideOut : function(){
39765         if(this.isSlid || this.el.hasActiveFx()){
39766             return;
39767         }
39768         this.isSlid = true;
39769         if(this.collapseBtn){
39770             this.collapseBtn.hide();
39771         }
39772         this.closeBtnState = this.closeBtn.getStyle('display');
39773         this.closeBtn.hide();
39774         if(this.stickBtn){
39775             this.stickBtn.show();
39776         }
39777         this.el.show();
39778         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39779         this.beforeSlide();
39780         this.el.setStyle("z-index", 10001);
39781         this.el.slideIn(this.getSlideAnchor(), {
39782             callback: function(){
39783                 this.afterSlide();
39784                 this.initAutoHide();
39785                 Roo.get(document).on("click", this.slideInIf, this);
39786                 this.fireEvent("slideshow", this);
39787             },
39788             scope: this,
39789             block: true
39790         });
39791     },
39792
39793     afterSlideIn : function(){
39794         this.clearAutoHide();
39795         this.isSlid = false;
39796         this.clearMonitor();
39797         this.el.setStyle("z-index", "");
39798         if(this.collapseBtn){
39799             this.collapseBtn.show();
39800         }
39801         this.closeBtn.setStyle('display', this.closeBtnState);
39802         if(this.stickBtn){
39803             this.stickBtn.hide();
39804         }
39805         this.fireEvent("slidehide", this);
39806     },
39807
39808     slideIn : function(cb){
39809         if(!this.isSlid || this.el.hasActiveFx()){
39810             Roo.callback(cb);
39811             return;
39812         }
39813         this.isSlid = false;
39814         this.beforeSlide();
39815         this.el.slideOut(this.getSlideAnchor(), {
39816             callback: function(){
39817                 this.el.setLeftTop(-10000, -10000);
39818                 this.afterSlide();
39819                 this.afterSlideIn();
39820                 Roo.callback(cb);
39821             },
39822             scope: this,
39823             block: true
39824         });
39825     },
39826     
39827     slideInIf : function(e){
39828         if(!e.within(this.el)){
39829             this.slideIn();
39830         }
39831     },
39832
39833     animateCollapse : function(){
39834         this.beforeSlide();
39835         this.el.setStyle("z-index", 20000);
39836         var anchor = this.getSlideAnchor();
39837         this.el.slideOut(anchor, {
39838             callback : function(){
39839                 this.el.setStyle("z-index", "");
39840                 this.collapsedEl.slideIn(anchor, {duration:.3});
39841                 this.afterSlide();
39842                 this.el.setLocation(-10000,-10000);
39843                 this.el.hide();
39844                 this.fireEvent("collapsed", this);
39845             },
39846             scope: this,
39847             block: true
39848         });
39849     },
39850
39851     animateExpand : function(){
39852         this.beforeSlide();
39853         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39854         this.el.setStyle("z-index", 20000);
39855         this.collapsedEl.hide({
39856             duration:.1
39857         });
39858         this.el.slideIn(this.getSlideAnchor(), {
39859             callback : function(){
39860                 this.el.setStyle("z-index", "");
39861                 this.afterSlide();
39862                 if(this.split){
39863                     this.split.el.show();
39864                 }
39865                 this.fireEvent("invalidated", this);
39866                 this.fireEvent("expanded", this);
39867             },
39868             scope: this,
39869             block: true
39870         });
39871     },
39872
39873     anchors : {
39874         "west" : "left",
39875         "east" : "right",
39876         "north" : "top",
39877         "south" : "bottom"
39878     },
39879
39880     sanchors : {
39881         "west" : "l",
39882         "east" : "r",
39883         "north" : "t",
39884         "south" : "b"
39885     },
39886
39887     canchors : {
39888         "west" : "tl-tr",
39889         "east" : "tr-tl",
39890         "north" : "tl-bl",
39891         "south" : "bl-tl"
39892     },
39893
39894     getAnchor : function(){
39895         return this.anchors[this.position];
39896     },
39897
39898     getCollapseAnchor : function(){
39899         return this.canchors[this.position];
39900     },
39901
39902     getSlideAnchor : function(){
39903         return this.sanchors[this.position];
39904     },
39905
39906     getAlignAdj : function(){
39907         var cm = this.cmargins;
39908         switch(this.position){
39909             case "west":
39910                 return [0, 0];
39911             break;
39912             case "east":
39913                 return [0, 0];
39914             break;
39915             case "north":
39916                 return [0, 0];
39917             break;
39918             case "south":
39919                 return [0, 0];
39920             break;
39921         }
39922     },
39923
39924     getExpandAdj : function(){
39925         var c = this.collapsedEl, cm = this.cmargins;
39926         switch(this.position){
39927             case "west":
39928                 return [-(cm.right+c.getWidth()+cm.left), 0];
39929             break;
39930             case "east":
39931                 return [cm.right+c.getWidth()+cm.left, 0];
39932             break;
39933             case "north":
39934                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39935             break;
39936             case "south":
39937                 return [0, cm.top+cm.bottom+c.getHeight()];
39938             break;
39939         }
39940     }
39941 });/*
39942  * Based on:
39943  * Ext JS Library 1.1.1
39944  * Copyright(c) 2006-2007, Ext JS, LLC.
39945  *
39946  * Originally Released Under LGPL - original licence link has changed is not relivant.
39947  *
39948  * Fork - LGPL
39949  * <script type="text/javascript">
39950  */
39951 /*
39952  * These classes are private internal classes
39953  */
39954 Roo.bootstrap.layout.Center = function(config){
39955     config.region = "center";
39956     Roo.bootstrap.layout.Region.call(this, config);
39957     this.visible = true;
39958     this.minWidth = config.minWidth || 20;
39959     this.minHeight = config.minHeight || 20;
39960 };
39961
39962 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39963     hide : function(){
39964         // center panel can't be hidden
39965     },
39966     
39967     show : function(){
39968         // center panel can't be hidden
39969     },
39970     
39971     getMinWidth: function(){
39972         return this.minWidth;
39973     },
39974     
39975     getMinHeight: function(){
39976         return this.minHeight;
39977     }
39978 });
39979
39980
39981
39982
39983  
39984
39985
39986
39987
39988
39989
39990 Roo.bootstrap.layout.North = function(config)
39991 {
39992     config.region = 'north';
39993     config.cursor = 'n-resize';
39994     
39995     Roo.bootstrap.layout.Split.call(this, config);
39996     
39997     
39998     if(this.split){
39999         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40000         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40001         this.split.el.addClass("roo-layout-split-v");
40002     }
40003     //var size = config.initialSize || config.height;
40004     //if(this.el && typeof size != "undefined"){
40005     //    this.el.setHeight(size);
40006     //}
40007 };
40008 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40009 {
40010     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40011      
40012      
40013     onRender : function(ctr, pos)
40014     {
40015         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40016         var size = this.config.initialSize || this.config.height;
40017         if(this.el && typeof size != "undefined"){
40018             this.el.setHeight(size);
40019         }
40020     
40021     },
40022     
40023     getBox : function(){
40024         if(this.collapsed){
40025             return this.collapsedEl.getBox();
40026         }
40027         var box = this.el.getBox();
40028         if(this.split){
40029             box.height += this.split.el.getHeight();
40030         }
40031         return box;
40032     },
40033     
40034     updateBox : function(box){
40035         if(this.split && !this.collapsed){
40036             box.height -= this.split.el.getHeight();
40037             this.split.el.setLeft(box.x);
40038             this.split.el.setTop(box.y+box.height);
40039             this.split.el.setWidth(box.width);
40040         }
40041         if(this.collapsed){
40042             this.updateBody(box.width, null);
40043         }
40044         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40045     }
40046 });
40047
40048
40049
40050
40051
40052 Roo.bootstrap.layout.South = function(config){
40053     config.region = 'south';
40054     config.cursor = 's-resize';
40055     Roo.bootstrap.layout.Split.call(this, config);
40056     if(this.split){
40057         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40058         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40059         this.split.el.addClass("roo-layout-split-v");
40060     }
40061     
40062 };
40063
40064 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40065     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40066     
40067     onRender : function(ctr, pos)
40068     {
40069         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40070         var size = this.config.initialSize || this.config.height;
40071         if(this.el && typeof size != "undefined"){
40072             this.el.setHeight(size);
40073         }
40074     
40075     },
40076     
40077     getBox : function(){
40078         if(this.collapsed){
40079             return this.collapsedEl.getBox();
40080         }
40081         var box = this.el.getBox();
40082         if(this.split){
40083             var sh = this.split.el.getHeight();
40084             box.height += sh;
40085             box.y -= sh;
40086         }
40087         return box;
40088     },
40089     
40090     updateBox : function(box){
40091         if(this.split && !this.collapsed){
40092             var sh = this.split.el.getHeight();
40093             box.height -= sh;
40094             box.y += sh;
40095             this.split.el.setLeft(box.x);
40096             this.split.el.setTop(box.y-sh);
40097             this.split.el.setWidth(box.width);
40098         }
40099         if(this.collapsed){
40100             this.updateBody(box.width, null);
40101         }
40102         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40103     }
40104 });
40105
40106 Roo.bootstrap.layout.East = function(config){
40107     config.region = "east";
40108     config.cursor = "e-resize";
40109     Roo.bootstrap.layout.Split.call(this, config);
40110     if(this.split){
40111         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40112         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40113         this.split.el.addClass("roo-layout-split-h");
40114     }
40115     
40116 };
40117 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40118     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40119     
40120     onRender : function(ctr, pos)
40121     {
40122         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40123         var size = this.config.initialSize || this.config.width;
40124         if(this.el && typeof size != "undefined"){
40125             this.el.setWidth(size);
40126         }
40127     
40128     },
40129     
40130     getBox : function(){
40131         if(this.collapsed){
40132             return this.collapsedEl.getBox();
40133         }
40134         var box = this.el.getBox();
40135         if(this.split){
40136             var sw = this.split.el.getWidth();
40137             box.width += sw;
40138             box.x -= sw;
40139         }
40140         return box;
40141     },
40142
40143     updateBox : function(box){
40144         if(this.split && !this.collapsed){
40145             var sw = this.split.el.getWidth();
40146             box.width -= sw;
40147             this.split.el.setLeft(box.x);
40148             this.split.el.setTop(box.y);
40149             this.split.el.setHeight(box.height);
40150             box.x += sw;
40151         }
40152         if(this.collapsed){
40153             this.updateBody(null, box.height);
40154         }
40155         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40156     }
40157 });
40158
40159 Roo.bootstrap.layout.West = function(config){
40160     config.region = "west";
40161     config.cursor = "w-resize";
40162     
40163     Roo.bootstrap.layout.Split.call(this, config);
40164     if(this.split){
40165         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40166         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40167         this.split.el.addClass("roo-layout-split-h");
40168     }
40169     
40170 };
40171 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40172     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40173     
40174     onRender: function(ctr, pos)
40175     {
40176         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40177         var size = this.config.initialSize || this.config.width;
40178         if(typeof size != "undefined"){
40179             this.el.setWidth(size);
40180         }
40181     },
40182     
40183     getBox : function(){
40184         if(this.collapsed){
40185             return this.collapsedEl.getBox();
40186         }
40187         var box = this.el.getBox();
40188         if (box.width == 0) {
40189             box.width = this.config.width; // kludge?
40190         }
40191         if(this.split){
40192             box.width += this.split.el.getWidth();
40193         }
40194         return box;
40195     },
40196     
40197     updateBox : function(box){
40198         if(this.split && !this.collapsed){
40199             var sw = this.split.el.getWidth();
40200             box.width -= sw;
40201             this.split.el.setLeft(box.x+box.width);
40202             this.split.el.setTop(box.y);
40203             this.split.el.setHeight(box.height);
40204         }
40205         if(this.collapsed){
40206             this.updateBody(null, box.height);
40207         }
40208         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40209     }
40210 });/*
40211  * Based on:
40212  * Ext JS Library 1.1.1
40213  * Copyright(c) 2006-2007, Ext JS, LLC.
40214  *
40215  * Originally Released Under LGPL - original licence link has changed is not relivant.
40216  *
40217  * Fork - LGPL
40218  * <script type="text/javascript">
40219  */
40220 /**
40221  * @class Roo.bootstrap.paenl.Content
40222  * @extends Roo.util.Observable
40223  * @builder-top
40224  * @children Roo.bootstrap.Component
40225  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40226  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40227  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40228  * @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
40229  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40230  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40231  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40232  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40233  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40234  * @cfg {String} title          The title for this panel
40235  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40236  * @cfg {String} url            Calls {@link #setUrl} with this value
40237  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40238  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40239  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40240  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40241  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40242  * @cfg {Boolean} badges render the badges
40243  * @cfg {String} cls  extra classes to use  
40244  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40245  
40246  * @constructor
40247  * Create a new ContentPanel.
40248  * @param {String/Object} config A string to set only the title or a config object
40249  
40250  */
40251 Roo.bootstrap.panel.Content = function( config){
40252     
40253     this.tpl = config.tpl || false;
40254     
40255     var el = config.el;
40256     var content = config.content;
40257
40258     if(config.autoCreate){ // xtype is available if this is called from factory
40259         el = Roo.id();
40260     }
40261     this.el = Roo.get(el);
40262     if(!this.el && config && config.autoCreate){
40263         if(typeof config.autoCreate == "object"){
40264             if(!config.autoCreate.id){
40265                 config.autoCreate.id = config.id||el;
40266             }
40267             this.el = Roo.DomHelper.append(document.body,
40268                         config.autoCreate, true);
40269         }else{
40270             var elcfg =  {
40271                 tag: "div",
40272                 cls: (config.cls || '') +
40273                     (config.background ? ' bg-' + config.background : '') +
40274                     " roo-layout-inactive-content",
40275                 id: config.id||el
40276             };
40277             if (config.iframe) {
40278                 elcfg.cn = [
40279                     {
40280                         tag : 'iframe',
40281                         style : 'border: 0px',
40282                         src : 'about:blank'
40283                     }
40284                 ];
40285             }
40286               
40287             if (config.html) {
40288                 elcfg.html = config.html;
40289                 
40290             }
40291                         
40292             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40293             if (config.iframe) {
40294                 this.iframeEl = this.el.select('iframe',true).first();
40295             }
40296             
40297         }
40298     } 
40299     this.closable = false;
40300     this.loaded = false;
40301     this.active = false;
40302    
40303       
40304     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40305         
40306         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40307         
40308         this.wrapEl = this.el; //this.el.wrap();
40309         var ti = [];
40310         if (config.toolbar.items) {
40311             ti = config.toolbar.items ;
40312             delete config.toolbar.items ;
40313         }
40314         
40315         var nitems = [];
40316         this.toolbar.render(this.wrapEl, 'before');
40317         for(var i =0;i < ti.length;i++) {
40318           //  Roo.log(['add child', items[i]]);
40319             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40320         }
40321         this.toolbar.items = nitems;
40322         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40323         delete config.toolbar;
40324         
40325     }
40326     /*
40327     // xtype created footer. - not sure if will work as we normally have to render first..
40328     if (this.footer && !this.footer.el && this.footer.xtype) {
40329         if (!this.wrapEl) {
40330             this.wrapEl = this.el.wrap();
40331         }
40332     
40333         this.footer.container = this.wrapEl.createChild();
40334          
40335         this.footer = Roo.factory(this.footer, Roo);
40336         
40337     }
40338     */
40339     
40340      if(typeof config == "string"){
40341         this.title = config;
40342     }else{
40343         Roo.apply(this, config);
40344     }
40345     
40346     if(this.resizeEl){
40347         this.resizeEl = Roo.get(this.resizeEl, true);
40348     }else{
40349         this.resizeEl = this.el;
40350     }
40351     // handle view.xtype
40352     
40353  
40354     
40355     
40356     this.addEvents({
40357         /**
40358          * @event activate
40359          * Fires when this panel is activated. 
40360          * @param {Roo.ContentPanel} this
40361          */
40362         "activate" : true,
40363         /**
40364          * @event deactivate
40365          * Fires when this panel is activated. 
40366          * @param {Roo.ContentPanel} this
40367          */
40368         "deactivate" : true,
40369
40370         /**
40371          * @event resize
40372          * Fires when this panel is resized if fitToFrame is true.
40373          * @param {Roo.ContentPanel} this
40374          * @param {Number} width The width after any component adjustments
40375          * @param {Number} height The height after any component adjustments
40376          */
40377         "resize" : true,
40378         
40379          /**
40380          * @event render
40381          * Fires when this tab is created
40382          * @param {Roo.ContentPanel} this
40383          */
40384         "render" : true,
40385         
40386           /**
40387          * @event scroll
40388          * Fires when this content is scrolled
40389          * @param {Roo.ContentPanel} this
40390          * @param {Event} scrollEvent
40391          */
40392         "scroll" : true
40393         
40394         
40395         
40396     });
40397     
40398
40399     
40400     
40401     if(this.autoScroll && !this.iframe){
40402         this.resizeEl.setStyle("overflow", "auto");
40403         this.resizeEl.on('scroll', this.onScroll, this);
40404     } else {
40405         // fix randome scrolling
40406         //this.el.on('scroll', function() {
40407         //    Roo.log('fix random scolling');
40408         //    this.scrollTo('top',0); 
40409         //});
40410     }
40411     content = content || this.content;
40412     if(content){
40413         this.setContent(content);
40414     }
40415     if(config && config.url){
40416         this.setUrl(this.url, this.params, this.loadOnce);
40417     }
40418     
40419     
40420     
40421     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40422     
40423     if (this.view && typeof(this.view.xtype) != 'undefined') {
40424         this.view.el = this.el.appendChild(document.createElement("div"));
40425         this.view = Roo.factory(this.view); 
40426         this.view.render  &&  this.view.render(false, '');  
40427     }
40428     
40429     
40430     this.fireEvent('render', this);
40431 };
40432
40433 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40434     
40435     cls : '',
40436     background : '',
40437     
40438     tabTip : '',
40439     
40440     iframe : false,
40441     iframeEl : false,
40442     
40443     /* Resize Element - use this to work out scroll etc. */
40444     resizeEl : false,
40445     
40446     setRegion : function(region){
40447         this.region = region;
40448         this.setActiveClass(region && !this.background);
40449     },
40450     
40451     
40452     setActiveClass: function(state)
40453     {
40454         if(state){
40455            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40456            this.el.setStyle('position','relative');
40457         }else{
40458            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40459            this.el.setStyle('position', 'absolute');
40460         } 
40461     },
40462     
40463     /**
40464      * Returns the toolbar for this Panel if one was configured. 
40465      * @return {Roo.Toolbar} 
40466      */
40467     getToolbar : function(){
40468         return this.toolbar;
40469     },
40470     
40471     setActiveState : function(active)
40472     {
40473         this.active = active;
40474         this.setActiveClass(active);
40475         if(!active){
40476             if(this.fireEvent("deactivate", this) === false){
40477                 return false;
40478             }
40479             return true;
40480         }
40481         this.fireEvent("activate", this);
40482         return true;
40483     },
40484     /**
40485      * Updates this panel's element (not for iframe)
40486      * @param {String} content The new content
40487      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40488     */
40489     setContent : function(content, loadScripts){
40490         if (this.iframe) {
40491             return;
40492         }
40493         
40494         this.el.update(content, loadScripts);
40495     },
40496
40497     ignoreResize : function(w, h){
40498         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40499             return true;
40500         }else{
40501             this.lastSize = {width: w, height: h};
40502             return false;
40503         }
40504     },
40505     /**
40506      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40507      * @return {Roo.UpdateManager} The UpdateManager
40508      */
40509     getUpdateManager : function(){
40510         if (this.iframe) {
40511             return false;
40512         }
40513         return this.el.getUpdateManager();
40514     },
40515      /**
40516      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40517      * Does not work with IFRAME contents
40518      * @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:
40519 <pre><code>
40520 panel.load({
40521     url: "your-url.php",
40522     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40523     callback: yourFunction,
40524     scope: yourObject, //(optional scope)
40525     discardUrl: false,
40526     nocache: false,
40527     text: "Loading...",
40528     timeout: 30,
40529     scripts: false
40530 });
40531 </code></pre>
40532      
40533      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40534      * 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.
40535      * @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}
40536      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40537      * @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.
40538      * @return {Roo.ContentPanel} this
40539      */
40540     load : function(){
40541         
40542         if (this.iframe) {
40543             return this;
40544         }
40545         
40546         var um = this.el.getUpdateManager();
40547         um.update.apply(um, arguments);
40548         return this;
40549     },
40550
40551
40552     /**
40553      * 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.
40554      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40555      * @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)
40556      * @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)
40557      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40558      */
40559     setUrl : function(url, params, loadOnce){
40560         if (this.iframe) {
40561             this.iframeEl.dom.src = url;
40562             return false;
40563         }
40564         
40565         if(this.refreshDelegate){
40566             this.removeListener("activate", this.refreshDelegate);
40567         }
40568         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40569         this.on("activate", this.refreshDelegate);
40570         return this.el.getUpdateManager();
40571     },
40572     
40573     _handleRefresh : function(url, params, loadOnce){
40574         if(!loadOnce || !this.loaded){
40575             var updater = this.el.getUpdateManager();
40576             updater.update(url, params, this._setLoaded.createDelegate(this));
40577         }
40578     },
40579     
40580     _setLoaded : function(){
40581         this.loaded = true;
40582     }, 
40583     
40584     /**
40585      * Returns this panel's id
40586      * @return {String} 
40587      */
40588     getId : function(){
40589         return this.el.id;
40590     },
40591     
40592     /** 
40593      * Returns this panel's element - used by regiosn to add.
40594      * @return {Roo.Element} 
40595      */
40596     getEl : function(){
40597         return this.wrapEl || this.el;
40598     },
40599     
40600    
40601     
40602     adjustForComponents : function(width, height)
40603     {
40604         //Roo.log('adjustForComponents ');
40605         if(this.resizeEl != this.el){
40606             width -= this.el.getFrameWidth('lr');
40607             height -= this.el.getFrameWidth('tb');
40608         }
40609         if(this.toolbar){
40610             var te = this.toolbar.getEl();
40611             te.setWidth(width);
40612             height -= te.getHeight();
40613         }
40614         if(this.footer){
40615             var te = this.footer.getEl();
40616             te.setWidth(width);
40617             height -= te.getHeight();
40618         }
40619         
40620         
40621         if(this.adjustments){
40622             width += this.adjustments[0];
40623             height += this.adjustments[1];
40624         }
40625         return {"width": width, "height": height};
40626     },
40627     
40628     setSize : function(width, height){
40629         if(this.fitToFrame && !this.ignoreResize(width, height)){
40630             if(this.fitContainer && this.resizeEl != this.el){
40631                 this.el.setSize(width, height);
40632             }
40633             var size = this.adjustForComponents(width, height);
40634             if (this.iframe) {
40635                 this.iframeEl.setSize(width,height);
40636             }
40637             
40638             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40639             this.fireEvent('resize', this, size.width, size.height);
40640             
40641             
40642         }
40643     },
40644     
40645     /**
40646      * Returns this panel's title
40647      * @return {String} 
40648      */
40649     getTitle : function(){
40650         
40651         if (typeof(this.title) != 'object') {
40652             return this.title;
40653         }
40654         
40655         var t = '';
40656         for (var k in this.title) {
40657             if (!this.title.hasOwnProperty(k)) {
40658                 continue;
40659             }
40660             
40661             if (k.indexOf('-') >= 0) {
40662                 var s = k.split('-');
40663                 for (var i = 0; i<s.length; i++) {
40664                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40665                 }
40666             } else {
40667                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40668             }
40669         }
40670         return t;
40671     },
40672     
40673     /**
40674      * Set this panel's title
40675      * @param {String} title
40676      */
40677     setTitle : function(title){
40678         this.title = title;
40679         if(this.region){
40680             this.region.updatePanelTitle(this, title);
40681         }
40682     },
40683     
40684     /**
40685      * Returns true is this panel was configured to be closable
40686      * @return {Boolean} 
40687      */
40688     isClosable : function(){
40689         return this.closable;
40690     },
40691     
40692     beforeSlide : function(){
40693         this.el.clip();
40694         this.resizeEl.clip();
40695     },
40696     
40697     afterSlide : function(){
40698         this.el.unclip();
40699         this.resizeEl.unclip();
40700     },
40701     
40702     /**
40703      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40704      *   Will fail silently if the {@link #setUrl} method has not been called.
40705      *   This does not activate the panel, just updates its content.
40706      */
40707     refresh : function(){
40708         if(this.refreshDelegate){
40709            this.loaded = false;
40710            this.refreshDelegate();
40711         }
40712     },
40713     
40714     /**
40715      * Destroys this panel
40716      */
40717     destroy : function(){
40718         this.el.removeAllListeners();
40719         var tempEl = document.createElement("span");
40720         tempEl.appendChild(this.el.dom);
40721         tempEl.innerHTML = "";
40722         this.el.remove();
40723         this.el = null;
40724     },
40725     
40726     /**
40727      * form - if the content panel contains a form - this is a reference to it.
40728      * @type {Roo.form.Form}
40729      */
40730     form : false,
40731     /**
40732      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40733      *    This contains a reference to it.
40734      * @type {Roo.View}
40735      */
40736     view : false,
40737     
40738       /**
40739      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40740      * <pre><code>
40741
40742 layout.addxtype({
40743        xtype : 'Form',
40744        items: [ .... ]
40745    }
40746 );
40747
40748 </code></pre>
40749      * @param {Object} cfg Xtype definition of item to add.
40750      */
40751     
40752     
40753     getChildContainer: function () {
40754         return this.getEl();
40755     },
40756     
40757     
40758     onScroll : function(e)
40759     {
40760         this.fireEvent('scroll', this, e);
40761     }
40762     
40763     
40764     /*
40765         var  ret = new Roo.factory(cfg);
40766         return ret;
40767         
40768         
40769         // add form..
40770         if (cfg.xtype.match(/^Form$/)) {
40771             
40772             var el;
40773             //if (this.footer) {
40774             //    el = this.footer.container.insertSibling(false, 'before');
40775             //} else {
40776                 el = this.el.createChild();
40777             //}
40778
40779             this.form = new  Roo.form.Form(cfg);
40780             
40781             
40782             if ( this.form.allItems.length) {
40783                 this.form.render(el.dom);
40784             }
40785             return this.form;
40786         }
40787         // should only have one of theses..
40788         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40789             // views.. should not be just added - used named prop 'view''
40790             
40791             cfg.el = this.el.appendChild(document.createElement("div"));
40792             // factory?
40793             
40794             var ret = new Roo.factory(cfg);
40795              
40796              ret.render && ret.render(false, ''); // render blank..
40797             this.view = ret;
40798             return ret;
40799         }
40800         return false;
40801     }
40802     \*/
40803 });
40804  
40805 /**
40806  * @class Roo.bootstrap.panel.Grid
40807  * @extends Roo.bootstrap.panel.Content
40808  * @constructor
40809  * Create a new GridPanel.
40810  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40811  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40812  * @param {Object} config A the config object
40813   
40814  */
40815
40816
40817
40818 Roo.bootstrap.panel.Grid = function(config)
40819 {
40820     
40821       
40822     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40823         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40824
40825     config.el = this.wrapper;
40826     //this.el = this.wrapper;
40827     
40828       if (config.container) {
40829         // ctor'ed from a Border/panel.grid
40830         
40831         
40832         this.wrapper.setStyle("overflow", "hidden");
40833         this.wrapper.addClass('roo-grid-container');
40834
40835     }
40836     
40837     
40838     if(config.toolbar){
40839         var tool_el = this.wrapper.createChild();    
40840         this.toolbar = Roo.factory(config.toolbar);
40841         var ti = [];
40842         if (config.toolbar.items) {
40843             ti = config.toolbar.items ;
40844             delete config.toolbar.items ;
40845         }
40846         
40847         var nitems = [];
40848         this.toolbar.render(tool_el);
40849         for(var i =0;i < ti.length;i++) {
40850           //  Roo.log(['add child', items[i]]);
40851             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40852         }
40853         this.toolbar.items = nitems;
40854         
40855         delete config.toolbar;
40856     }
40857     
40858     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40859     config.grid.scrollBody = true;;
40860     config.grid.monitorWindowResize = false; // turn off autosizing
40861     config.grid.autoHeight = false;
40862     config.grid.autoWidth = false;
40863     
40864     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40865     
40866     if (config.background) {
40867         // render grid on panel activation (if panel background)
40868         this.on('activate', function(gp) {
40869             if (!gp.grid.rendered) {
40870                 gp.grid.render(this.wrapper);
40871                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40872             }
40873         });
40874             
40875     } else {
40876         this.grid.render(this.wrapper);
40877         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40878
40879     }
40880     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40881     // ??? needed ??? config.el = this.wrapper;
40882     
40883     
40884     
40885   
40886     // xtype created footer. - not sure if will work as we normally have to render first..
40887     if (this.footer && !this.footer.el && this.footer.xtype) {
40888         
40889         var ctr = this.grid.getView().getFooterPanel(true);
40890         this.footer.dataSource = this.grid.dataSource;
40891         this.footer = Roo.factory(this.footer, Roo);
40892         this.footer.render(ctr);
40893         
40894     }
40895     
40896     
40897     
40898     
40899      
40900 };
40901
40902 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40903     getId : function(){
40904         return this.grid.id;
40905     },
40906     
40907     /**
40908      * Returns the grid for this panel
40909      * @return {Roo.bootstrap.Table} 
40910      */
40911     getGrid : function(){
40912         return this.grid;    
40913     },
40914     
40915     setSize : function(width, height){
40916         if(!this.ignoreResize(width, height)){
40917             var grid = this.grid;
40918             var size = this.adjustForComponents(width, height);
40919             // tfoot is not a footer?
40920           
40921             
40922             var gridel = grid.getGridEl();
40923             gridel.setSize(size.width, size.height);
40924             
40925             var tbd = grid.getGridEl().select('tbody', true).first();
40926             var thd = grid.getGridEl().select('thead',true).first();
40927             var tbf= grid.getGridEl().select('tfoot', true).first();
40928
40929             if (tbf) {
40930                 size.height -= tbf.getHeight();
40931             }
40932             if (thd) {
40933                 size.height -= thd.getHeight();
40934             }
40935             
40936             tbd.setSize(size.width, size.height );
40937             // this is for the account management tab -seems to work there.
40938             var thd = grid.getGridEl().select('thead',true).first();
40939             //if (tbd) {
40940             //    tbd.setSize(size.width, size.height - thd.getHeight());
40941             //}
40942              
40943             grid.autoSize();
40944         }
40945     },
40946      
40947     
40948     
40949     beforeSlide : function(){
40950         this.grid.getView().scroller.clip();
40951     },
40952     
40953     afterSlide : function(){
40954         this.grid.getView().scroller.unclip();
40955     },
40956     
40957     destroy : function(){
40958         this.grid.destroy();
40959         delete this.grid;
40960         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40961     }
40962 });
40963
40964 /**
40965  * @class Roo.bootstrap.panel.Nest
40966  * @extends Roo.bootstrap.panel.Content
40967  * @constructor
40968  * Create a new Panel, that can contain a layout.Border.
40969  * 
40970  * 
40971  * @param {String/Object} config A string to set only the title or a config object
40972  */
40973 Roo.bootstrap.panel.Nest = function(config)
40974 {
40975     // construct with only one argument..
40976     /* FIXME - implement nicer consturctors
40977     if (layout.layout) {
40978         config = layout;
40979         layout = config.layout;
40980         delete config.layout;
40981     }
40982     if (layout.xtype && !layout.getEl) {
40983         // then layout needs constructing..
40984         layout = Roo.factory(layout, Roo);
40985     }
40986     */
40987     
40988     config.el =  config.layout.getEl();
40989     
40990     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40991     
40992     config.layout.monitorWindowResize = false; // turn off autosizing
40993     this.layout = config.layout;
40994     this.layout.getEl().addClass("roo-layout-nested-layout");
40995     this.layout.parent = this;
40996     
40997     
40998     
40999     
41000 };
41001
41002 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41003     /**
41004     * @cfg {Roo.BorderLayout} layout The layout for this panel
41005     */
41006     layout : false,
41007
41008     setSize : function(width, height){
41009         if(!this.ignoreResize(width, height)){
41010             var size = this.adjustForComponents(width, height);
41011             var el = this.layout.getEl();
41012             if (size.height < 1) {
41013                 el.setWidth(size.width);   
41014             } else {
41015                 el.setSize(size.width, size.height);
41016             }
41017             var touch = el.dom.offsetWidth;
41018             this.layout.layout();
41019             // ie requires a double layout on the first pass
41020             if(Roo.isIE && !this.initialized){
41021                 this.initialized = true;
41022                 this.layout.layout();
41023             }
41024         }
41025     },
41026     
41027     // activate all subpanels if not currently active..
41028     
41029     setActiveState : function(active){
41030         this.active = active;
41031         this.setActiveClass(active);
41032         
41033         if(!active){
41034             this.fireEvent("deactivate", this);
41035             return;
41036         }
41037         
41038         this.fireEvent("activate", this);
41039         // not sure if this should happen before or after..
41040         if (!this.layout) {
41041             return; // should not happen..
41042         }
41043         var reg = false;
41044         for (var r in this.layout.regions) {
41045             reg = this.layout.getRegion(r);
41046             if (reg.getActivePanel()) {
41047                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41048                 reg.setActivePanel(reg.getActivePanel());
41049                 continue;
41050             }
41051             if (!reg.panels.length) {
41052                 continue;
41053             }
41054             reg.showPanel(reg.getPanel(0));
41055         }
41056         
41057         
41058         
41059         
41060     },
41061     
41062     /**
41063      * Returns the nested BorderLayout for this panel
41064      * @return {Roo.BorderLayout} 
41065      */
41066     getLayout : function(){
41067         return this.layout;
41068     },
41069     
41070      /**
41071      * Adds a xtype elements to the layout of the nested panel
41072      * <pre><code>
41073
41074 panel.addxtype({
41075        xtype : 'ContentPanel',
41076        region: 'west',
41077        items: [ .... ]
41078    }
41079 );
41080
41081 panel.addxtype({
41082         xtype : 'NestedLayoutPanel',
41083         region: 'west',
41084         layout: {
41085            center: { },
41086            west: { }   
41087         },
41088         items : [ ... list of content panels or nested layout panels.. ]
41089    }
41090 );
41091 </code></pre>
41092      * @param {Object} cfg Xtype definition of item to add.
41093      */
41094     addxtype : function(cfg) {
41095         return this.layout.addxtype(cfg);
41096     
41097     }
41098 });/*
41099  * Based on:
41100  * Ext JS Library 1.1.1
41101  * Copyright(c) 2006-2007, Ext JS, LLC.
41102  *
41103  * Originally Released Under LGPL - original licence link has changed is not relivant.
41104  *
41105  * Fork - LGPL
41106  * <script type="text/javascript">
41107  */
41108 /**
41109  * @class Roo.TabPanel
41110  * @extends Roo.util.Observable
41111  * A lightweight tab container.
41112  * <br><br>
41113  * Usage:
41114  * <pre><code>
41115 // basic tabs 1, built from existing content
41116 var tabs = new Roo.TabPanel("tabs1");
41117 tabs.addTab("script", "View Script");
41118 tabs.addTab("markup", "View Markup");
41119 tabs.activate("script");
41120
41121 // more advanced tabs, built from javascript
41122 var jtabs = new Roo.TabPanel("jtabs");
41123 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41124
41125 // set up the UpdateManager
41126 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41127 var updater = tab2.getUpdateManager();
41128 updater.setDefaultUrl("ajax1.htm");
41129 tab2.on('activate', updater.refresh, updater, true);
41130
41131 // Use setUrl for Ajax loading
41132 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41133 tab3.setUrl("ajax2.htm", null, true);
41134
41135 // Disabled tab
41136 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41137 tab4.disable();
41138
41139 jtabs.activate("jtabs-1");
41140  * </code></pre>
41141  * @constructor
41142  * Create a new TabPanel.
41143  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41144  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41145  */
41146 Roo.bootstrap.panel.Tabs = function(config){
41147     /**
41148     * The container element for this TabPanel.
41149     * @type Roo.Element
41150     */
41151     this.el = Roo.get(config.el);
41152     delete config.el;
41153     if(config){
41154         if(typeof config == "boolean"){
41155             this.tabPosition = config ? "bottom" : "top";
41156         }else{
41157             Roo.apply(this, config);
41158         }
41159     }
41160     
41161     if(this.tabPosition == "bottom"){
41162         // if tabs are at the bottom = create the body first.
41163         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41164         this.el.addClass("roo-tabs-bottom");
41165     }
41166     // next create the tabs holders
41167     
41168     if (this.tabPosition == "west"){
41169         
41170         var reg = this.region; // fake it..
41171         while (reg) {
41172             if (!reg.mgr.parent) {
41173                 break;
41174             }
41175             reg = reg.mgr.parent.region;
41176         }
41177         Roo.log("got nest?");
41178         Roo.log(reg);
41179         if (reg.mgr.getRegion('west')) {
41180             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41181             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41182             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41183             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41184             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41185         
41186             
41187         }
41188         
41189         
41190     } else {
41191      
41192         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41193         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41194         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41195         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41196     }
41197     
41198     
41199     if(Roo.isIE){
41200         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41201     }
41202     
41203     // finally - if tabs are at the top, then create the body last..
41204     if(this.tabPosition != "bottom"){
41205         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41206          * @type Roo.Element
41207          */
41208         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41209         this.el.addClass("roo-tabs-top");
41210     }
41211     this.items = [];
41212
41213     this.bodyEl.setStyle("position", "relative");
41214
41215     this.active = null;
41216     this.activateDelegate = this.activate.createDelegate(this);
41217
41218     this.addEvents({
41219         /**
41220          * @event tabchange
41221          * Fires when the active tab changes
41222          * @param {Roo.TabPanel} this
41223          * @param {Roo.TabPanelItem} activePanel The new active tab
41224          */
41225         "tabchange": true,
41226         /**
41227          * @event beforetabchange
41228          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41229          * @param {Roo.TabPanel} this
41230          * @param {Object} e Set cancel to true on this object to cancel the tab change
41231          * @param {Roo.TabPanelItem} tab The tab being changed to
41232          */
41233         "beforetabchange" : true
41234     });
41235
41236     Roo.EventManager.onWindowResize(this.onResize, this);
41237     this.cpad = this.el.getPadding("lr");
41238     this.hiddenCount = 0;
41239
41240
41241     // toolbar on the tabbar support...
41242     if (this.toolbar) {
41243         alert("no toolbar support yet");
41244         this.toolbar  = false;
41245         /*
41246         var tcfg = this.toolbar;
41247         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41248         this.toolbar = new Roo.Toolbar(tcfg);
41249         if (Roo.isSafari) {
41250             var tbl = tcfg.container.child('table', true);
41251             tbl.setAttribute('width', '100%');
41252         }
41253         */
41254         
41255     }
41256    
41257
41258
41259     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41260 };
41261
41262 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41263     /*
41264      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41265      */
41266     tabPosition : "top",
41267     /*
41268      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41269      */
41270     currentTabWidth : 0,
41271     /*
41272      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41273      */
41274     minTabWidth : 40,
41275     /*
41276      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41277      */
41278     maxTabWidth : 250,
41279     /*
41280      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41281      */
41282     preferredTabWidth : 175,
41283     /*
41284      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41285      */
41286     resizeTabs : false,
41287     /*
41288      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41289      */
41290     monitorResize : true,
41291     /*
41292      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41293      */
41294     toolbar : false,  // set by caller..
41295     
41296     region : false, /// set by caller
41297     
41298     disableTooltips : true, // not used yet...
41299
41300     /**
41301      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41302      * @param {String} id The id of the div to use <b>or create</b>
41303      * @param {String} text The text for the tab
41304      * @param {String} content (optional) Content to put in the TabPanelItem body
41305      * @param {Boolean} closable (optional) True to create a close icon on the tab
41306      * @return {Roo.TabPanelItem} The created TabPanelItem
41307      */
41308     addTab : function(id, text, content, closable, tpl)
41309     {
41310         var item = new Roo.bootstrap.panel.TabItem({
41311             panel: this,
41312             id : id,
41313             text : text,
41314             closable : closable,
41315             tpl : tpl
41316         });
41317         this.addTabItem(item);
41318         if(content){
41319             item.setContent(content);
41320         }
41321         return item;
41322     },
41323
41324     /**
41325      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41326      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41327      * @return {Roo.TabPanelItem}
41328      */
41329     getTab : function(id){
41330         return this.items[id];
41331     },
41332
41333     /**
41334      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41335      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41336      */
41337     hideTab : function(id){
41338         var t = this.items[id];
41339         if(!t.isHidden()){
41340            t.setHidden(true);
41341            this.hiddenCount++;
41342            this.autoSizeTabs();
41343         }
41344     },
41345
41346     /**
41347      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41348      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41349      */
41350     unhideTab : function(id){
41351         var t = this.items[id];
41352         if(t.isHidden()){
41353            t.setHidden(false);
41354            this.hiddenCount--;
41355            this.autoSizeTabs();
41356         }
41357     },
41358
41359     /**
41360      * Adds an existing {@link Roo.TabPanelItem}.
41361      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41362      */
41363     addTabItem : function(item)
41364     {
41365         this.items[item.id] = item;
41366         this.items.push(item);
41367         this.autoSizeTabs();
41368       //  if(this.resizeTabs){
41369     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41370   //         this.autoSizeTabs();
41371 //        }else{
41372 //            item.autoSize();
41373        // }
41374     },
41375
41376     /**
41377      * Removes a {@link Roo.TabPanelItem}.
41378      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41379      */
41380     removeTab : function(id){
41381         var items = this.items;
41382         var tab = items[id];
41383         if(!tab) { return; }
41384         var index = items.indexOf(tab);
41385         if(this.active == tab && items.length > 1){
41386             var newTab = this.getNextAvailable(index);
41387             if(newTab) {
41388                 newTab.activate();
41389             }
41390         }
41391         this.stripEl.dom.removeChild(tab.pnode.dom);
41392         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41393             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41394         }
41395         items.splice(index, 1);
41396         delete this.items[tab.id];
41397         tab.fireEvent("close", tab);
41398         tab.purgeListeners();
41399         this.autoSizeTabs();
41400     },
41401
41402     getNextAvailable : function(start){
41403         var items = this.items;
41404         var index = start;
41405         // look for a next tab that will slide over to
41406         // replace the one being removed
41407         while(index < items.length){
41408             var item = items[++index];
41409             if(item && !item.isHidden()){
41410                 return item;
41411             }
41412         }
41413         // if one isn't found select the previous tab (on the left)
41414         index = start;
41415         while(index >= 0){
41416             var item = items[--index];
41417             if(item && !item.isHidden()){
41418                 return item;
41419             }
41420         }
41421         return null;
41422     },
41423
41424     /**
41425      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41426      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41427      */
41428     disableTab : function(id){
41429         var tab = this.items[id];
41430         if(tab && this.active != tab){
41431             tab.disable();
41432         }
41433     },
41434
41435     /**
41436      * Enables a {@link Roo.TabPanelItem} that is disabled.
41437      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41438      */
41439     enableTab : function(id){
41440         var tab = this.items[id];
41441         tab.enable();
41442     },
41443
41444     /**
41445      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41446      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41447      * @return {Roo.TabPanelItem} The TabPanelItem.
41448      */
41449     activate : function(id)
41450     {
41451         //Roo.log('activite:'  + id);
41452         
41453         var tab = this.items[id];
41454         if(!tab){
41455             return null;
41456         }
41457         if(tab == this.active || tab.disabled){
41458             return tab;
41459         }
41460         var e = {};
41461         this.fireEvent("beforetabchange", this, e, tab);
41462         if(e.cancel !== true && !tab.disabled){
41463             if(this.active){
41464                 this.active.hide();
41465             }
41466             this.active = this.items[id];
41467             this.active.show();
41468             this.fireEvent("tabchange", this, this.active);
41469         }
41470         return tab;
41471     },
41472
41473     /**
41474      * Gets the active {@link Roo.TabPanelItem}.
41475      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41476      */
41477     getActiveTab : function(){
41478         return this.active;
41479     },
41480
41481     /**
41482      * Updates the tab body element to fit the height of the container element
41483      * for overflow scrolling
41484      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41485      */
41486     syncHeight : function(targetHeight){
41487         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41488         var bm = this.bodyEl.getMargins();
41489         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41490         this.bodyEl.setHeight(newHeight);
41491         return newHeight;
41492     },
41493
41494     onResize : function(){
41495         if(this.monitorResize){
41496             this.autoSizeTabs();
41497         }
41498     },
41499
41500     /**
41501      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41502      */
41503     beginUpdate : function(){
41504         this.updating = true;
41505     },
41506
41507     /**
41508      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41509      */
41510     endUpdate : function(){
41511         this.updating = false;
41512         this.autoSizeTabs();
41513     },
41514
41515     /**
41516      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41517      */
41518     autoSizeTabs : function()
41519     {
41520         var count = this.items.length;
41521         var vcount = count - this.hiddenCount;
41522         
41523         if (vcount < 2) {
41524             this.stripEl.hide();
41525         } else {
41526             this.stripEl.show();
41527         }
41528         
41529         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41530             return;
41531         }
41532         
41533         
41534         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41535         var availWidth = Math.floor(w / vcount);
41536         var b = this.stripBody;
41537         if(b.getWidth() > w){
41538             var tabs = this.items;
41539             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41540             if(availWidth < this.minTabWidth){
41541                 /*if(!this.sleft){    // incomplete scrolling code
41542                     this.createScrollButtons();
41543                 }
41544                 this.showScroll();
41545                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41546             }
41547         }else{
41548             if(this.currentTabWidth < this.preferredTabWidth){
41549                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41550             }
41551         }
41552     },
41553
41554     /**
41555      * Returns the number of tabs in this TabPanel.
41556      * @return {Number}
41557      */
41558      getCount : function(){
41559          return this.items.length;
41560      },
41561
41562     /**
41563      * Resizes all the tabs to the passed width
41564      * @param {Number} The new width
41565      */
41566     setTabWidth : function(width){
41567         this.currentTabWidth = width;
41568         for(var i = 0, len = this.items.length; i < len; i++) {
41569                 if(!this.items[i].isHidden()) {
41570                 this.items[i].setWidth(width);
41571             }
41572         }
41573     },
41574
41575     /**
41576      * Destroys this TabPanel
41577      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41578      */
41579     destroy : function(removeEl){
41580         Roo.EventManager.removeResizeListener(this.onResize, this);
41581         for(var i = 0, len = this.items.length; i < len; i++){
41582             this.items[i].purgeListeners();
41583         }
41584         if(removeEl === true){
41585             this.el.update("");
41586             this.el.remove();
41587         }
41588     },
41589     
41590     createStrip : function(container)
41591     {
41592         var strip = document.createElement("nav");
41593         strip.className = Roo.bootstrap.version == 4 ?
41594             "navbar-light bg-light" : 
41595             "navbar navbar-default"; //"x-tabs-wrap";
41596         container.appendChild(strip);
41597         return strip;
41598     },
41599     
41600     createStripList : function(strip)
41601     {
41602         // div wrapper for retard IE
41603         // returns the "tr" element.
41604         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41605         //'<div class="x-tabs-strip-wrap">'+
41606           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41607           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41608         return strip.firstChild; //.firstChild.firstChild.firstChild;
41609     },
41610     createBody : function(container)
41611     {
41612         var body = document.createElement("div");
41613         Roo.id(body, "tab-body");
41614         //Roo.fly(body).addClass("x-tabs-body");
41615         Roo.fly(body).addClass("tab-content");
41616         container.appendChild(body);
41617         return body;
41618     },
41619     createItemBody :function(bodyEl, id){
41620         var body = Roo.getDom(id);
41621         if(!body){
41622             body = document.createElement("div");
41623             body.id = id;
41624         }
41625         //Roo.fly(body).addClass("x-tabs-item-body");
41626         Roo.fly(body).addClass("tab-pane");
41627          bodyEl.insertBefore(body, bodyEl.firstChild);
41628         return body;
41629     },
41630     /** @private */
41631     createStripElements :  function(stripEl, text, closable, tpl)
41632     {
41633         var td = document.createElement("li"); // was td..
41634         td.className = 'nav-item';
41635         
41636         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41637         
41638         
41639         stripEl.appendChild(td);
41640         /*if(closable){
41641             td.className = "x-tabs-closable";
41642             if(!this.closeTpl){
41643                 this.closeTpl = new Roo.Template(
41644                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41645                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41646                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41647                 );
41648             }
41649             var el = this.closeTpl.overwrite(td, {"text": text});
41650             var close = el.getElementsByTagName("div")[0];
41651             var inner = el.getElementsByTagName("em")[0];
41652             return {"el": el, "close": close, "inner": inner};
41653         } else {
41654         */
41655         // not sure what this is..
41656 //            if(!this.tabTpl){
41657                 //this.tabTpl = new Roo.Template(
41658                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41659                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41660                 //);
41661 //                this.tabTpl = new Roo.Template(
41662 //                   '<a href="#">' +
41663 //                   '<span unselectable="on"' +
41664 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41665 //                            ' >{text}</span></a>'
41666 //                );
41667 //                
41668 //            }
41669
41670
41671             var template = tpl || this.tabTpl || false;
41672             
41673             if(!template){
41674                 template =  new Roo.Template(
41675                         Roo.bootstrap.version == 4 ? 
41676                             (
41677                                 '<a class="nav-link" href="#" unselectable="on"' +
41678                                      (this.disableTooltips ? '' : ' title="{text}"') +
41679                                      ' >{text}</a>'
41680                             ) : (
41681                                 '<a class="nav-link" href="#">' +
41682                                 '<span unselectable="on"' +
41683                                          (this.disableTooltips ? '' : ' title="{text}"') +
41684                                     ' >{text}</span></a>'
41685                             )
41686                 );
41687             }
41688             
41689             switch (typeof(template)) {
41690                 case 'object' :
41691                     break;
41692                 case 'string' :
41693                     template = new Roo.Template(template);
41694                     break;
41695                 default :
41696                     break;
41697             }
41698             
41699             var el = template.overwrite(td, {"text": text});
41700             
41701             var inner = el.getElementsByTagName("span")[0];
41702             
41703             return {"el": el, "inner": inner};
41704             
41705     }
41706         
41707     
41708 });
41709
41710 /**
41711  * @class Roo.TabPanelItem
41712  * @extends Roo.util.Observable
41713  * Represents an individual item (tab plus body) in a TabPanel.
41714  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41715  * @param {String} id The id of this TabPanelItem
41716  * @param {String} text The text for the tab of this TabPanelItem
41717  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41718  */
41719 Roo.bootstrap.panel.TabItem = function(config){
41720     /**
41721      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41722      * @type Roo.TabPanel
41723      */
41724     this.tabPanel = config.panel;
41725     /**
41726      * The id for this TabPanelItem
41727      * @type String
41728      */
41729     this.id = config.id;
41730     /** @private */
41731     this.disabled = false;
41732     /** @private */
41733     this.text = config.text;
41734     /** @private */
41735     this.loaded = false;
41736     this.closable = config.closable;
41737
41738     /**
41739      * The body element for this TabPanelItem.
41740      * @type Roo.Element
41741      */
41742     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41743     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41744     this.bodyEl.setStyle("display", "block");
41745     this.bodyEl.setStyle("zoom", "1");
41746     //this.hideAction();
41747
41748     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41749     /** @private */
41750     this.el = Roo.get(els.el);
41751     this.inner = Roo.get(els.inner, true);
41752      this.textEl = Roo.bootstrap.version == 4 ?
41753         this.el : Roo.get(this.el.dom.firstChild, true);
41754
41755     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41756     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41757
41758     
41759 //    this.el.on("mousedown", this.onTabMouseDown, this);
41760     this.el.on("click", this.onTabClick, this);
41761     /** @private */
41762     if(config.closable){
41763         var c = Roo.get(els.close, true);
41764         c.dom.title = this.closeText;
41765         c.addClassOnOver("close-over");
41766         c.on("click", this.closeClick, this);
41767      }
41768
41769     this.addEvents({
41770          /**
41771          * @event activate
41772          * Fires when this tab becomes the active tab.
41773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41774          * @param {Roo.TabPanelItem} this
41775          */
41776         "activate": true,
41777         /**
41778          * @event beforeclose
41779          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41780          * @param {Roo.TabPanelItem} this
41781          * @param {Object} e Set cancel to true on this object to cancel the close.
41782          */
41783         "beforeclose": true,
41784         /**
41785          * @event close
41786          * Fires when this tab is closed.
41787          * @param {Roo.TabPanelItem} this
41788          */
41789          "close": true,
41790         /**
41791          * @event deactivate
41792          * Fires when this tab is no longer the active tab.
41793          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41794          * @param {Roo.TabPanelItem} this
41795          */
41796          "deactivate" : true
41797     });
41798     this.hidden = false;
41799
41800     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41801 };
41802
41803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41804            {
41805     purgeListeners : function(){
41806        Roo.util.Observable.prototype.purgeListeners.call(this);
41807        this.el.removeAllListeners();
41808     },
41809     /**
41810      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41811      */
41812     show : function(){
41813         this.status_node.addClass("active");
41814         this.showAction();
41815         if(Roo.isOpera){
41816             this.tabPanel.stripWrap.repaint();
41817         }
41818         this.fireEvent("activate", this.tabPanel, this);
41819     },
41820
41821     /**
41822      * Returns true if this tab is the active tab.
41823      * @return {Boolean}
41824      */
41825     isActive : function(){
41826         return this.tabPanel.getActiveTab() == this;
41827     },
41828
41829     /**
41830      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41831      */
41832     hide : function(){
41833         this.status_node.removeClass("active");
41834         this.hideAction();
41835         this.fireEvent("deactivate", this.tabPanel, this);
41836     },
41837
41838     hideAction : function(){
41839         this.bodyEl.hide();
41840         this.bodyEl.setStyle("position", "absolute");
41841         this.bodyEl.setLeft("-20000px");
41842         this.bodyEl.setTop("-20000px");
41843     },
41844
41845     showAction : function(){
41846         this.bodyEl.setStyle("position", "relative");
41847         this.bodyEl.setTop("");
41848         this.bodyEl.setLeft("");
41849         this.bodyEl.show();
41850     },
41851
41852     /**
41853      * Set the tooltip for the tab.
41854      * @param {String} tooltip The tab's tooltip
41855      */
41856     setTooltip : function(text){
41857         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41858             this.textEl.dom.qtip = text;
41859             this.textEl.dom.removeAttribute('title');
41860         }else{
41861             this.textEl.dom.title = text;
41862         }
41863     },
41864
41865     onTabClick : function(e){
41866         e.preventDefault();
41867         this.tabPanel.activate(this.id);
41868     },
41869
41870     onTabMouseDown : function(e){
41871         e.preventDefault();
41872         this.tabPanel.activate(this.id);
41873     },
41874 /*
41875     getWidth : function(){
41876         return this.inner.getWidth();
41877     },
41878
41879     setWidth : function(width){
41880         var iwidth = width - this.linode.getPadding("lr");
41881         this.inner.setWidth(iwidth);
41882         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41883         this.linode.setWidth(width);
41884     },
41885 */
41886     /**
41887      * Show or hide the tab
41888      * @param {Boolean} hidden True to hide or false to show.
41889      */
41890     setHidden : function(hidden){
41891         this.hidden = hidden;
41892         this.linode.setStyle("display", hidden ? "none" : "");
41893     },
41894
41895     /**
41896      * Returns true if this tab is "hidden"
41897      * @return {Boolean}
41898      */
41899     isHidden : function(){
41900         return this.hidden;
41901     },
41902
41903     /**
41904      * Returns the text for this tab
41905      * @return {String}
41906      */
41907     getText : function(){
41908         return this.text;
41909     },
41910     /*
41911     autoSize : function(){
41912         //this.el.beginMeasure();
41913         this.textEl.setWidth(1);
41914         /*
41915          *  #2804 [new] Tabs in Roojs
41916          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41917          */
41918         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41919         //this.el.endMeasure();
41920     //},
41921
41922     /**
41923      * Sets the text for the tab (Note: this also sets the tooltip text)
41924      * @param {String} text The tab's text and tooltip
41925      */
41926     setText : function(text){
41927         this.text = text;
41928         this.textEl.update(text);
41929         this.setTooltip(text);
41930         //if(!this.tabPanel.resizeTabs){
41931         //    this.autoSize();
41932         //}
41933     },
41934     /**
41935      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41936      */
41937     activate : function(){
41938         this.tabPanel.activate(this.id);
41939     },
41940
41941     /**
41942      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41943      */
41944     disable : function(){
41945         if(this.tabPanel.active != this){
41946             this.disabled = true;
41947             this.status_node.addClass("disabled");
41948         }
41949     },
41950
41951     /**
41952      * Enables this TabPanelItem if it was previously disabled.
41953      */
41954     enable : function(){
41955         this.disabled = false;
41956         this.status_node.removeClass("disabled");
41957     },
41958
41959     /**
41960      * Sets the content for this TabPanelItem.
41961      * @param {String} content The content
41962      * @param {Boolean} loadScripts true to look for and load scripts
41963      */
41964     setContent : function(content, loadScripts){
41965         this.bodyEl.update(content, loadScripts);
41966     },
41967
41968     /**
41969      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41970      * @return {Roo.UpdateManager} The UpdateManager
41971      */
41972     getUpdateManager : function(){
41973         return this.bodyEl.getUpdateManager();
41974     },
41975
41976     /**
41977      * Set a URL to be used to load the content for this TabPanelItem.
41978      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41979      * @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)
41980      * @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)
41981      * @return {Roo.UpdateManager} The UpdateManager
41982      */
41983     setUrl : function(url, params, loadOnce){
41984         if(this.refreshDelegate){
41985             this.un('activate', this.refreshDelegate);
41986         }
41987         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41988         this.on("activate", this.refreshDelegate);
41989         return this.bodyEl.getUpdateManager();
41990     },
41991
41992     /** @private */
41993     _handleRefresh : function(url, params, loadOnce){
41994         if(!loadOnce || !this.loaded){
41995             var updater = this.bodyEl.getUpdateManager();
41996             updater.update(url, params, this._setLoaded.createDelegate(this));
41997         }
41998     },
41999
42000     /**
42001      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42002      *   Will fail silently if the setUrl method has not been called.
42003      *   This does not activate the panel, just updates its content.
42004      */
42005     refresh : function(){
42006         if(this.refreshDelegate){
42007            this.loaded = false;
42008            this.refreshDelegate();
42009         }
42010     },
42011
42012     /** @private */
42013     _setLoaded : function(){
42014         this.loaded = true;
42015     },
42016
42017     /** @private */
42018     closeClick : function(e){
42019         var o = {};
42020         e.stopEvent();
42021         this.fireEvent("beforeclose", this, o);
42022         if(o.cancel !== true){
42023             this.tabPanel.removeTab(this.id);
42024         }
42025     },
42026     /**
42027      * The text displayed in the tooltip for the close icon.
42028      * @type String
42029      */
42030     closeText : "Close this tab"
42031 });
42032 /**
42033 *    This script refer to:
42034 *    Title: International Telephone Input
42035 *    Author: Jack O'Connor
42036 *    Code version:  v12.1.12
42037 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42038 **/
42039
42040 Roo.bootstrap.form.PhoneInputData = function() {
42041     var d = [
42042       [
42043         "Afghanistan (‫افغانستان‬‎)",
42044         "af",
42045         "93"
42046       ],
42047       [
42048         "Albania (Shqipëri)",
42049         "al",
42050         "355"
42051       ],
42052       [
42053         "Algeria (‫الجزائر‬‎)",
42054         "dz",
42055         "213"
42056       ],
42057       [
42058         "American Samoa",
42059         "as",
42060         "1684"
42061       ],
42062       [
42063         "Andorra",
42064         "ad",
42065         "376"
42066       ],
42067       [
42068         "Angola",
42069         "ao",
42070         "244"
42071       ],
42072       [
42073         "Anguilla",
42074         "ai",
42075         "1264"
42076       ],
42077       [
42078         "Antigua and Barbuda",
42079         "ag",
42080         "1268"
42081       ],
42082       [
42083         "Argentina",
42084         "ar",
42085         "54"
42086       ],
42087       [
42088         "Armenia (Հայաստան)",
42089         "am",
42090         "374"
42091       ],
42092       [
42093         "Aruba",
42094         "aw",
42095         "297"
42096       ],
42097       [
42098         "Australia",
42099         "au",
42100         "61",
42101         0
42102       ],
42103       [
42104         "Austria (Österreich)",
42105         "at",
42106         "43"
42107       ],
42108       [
42109         "Azerbaijan (Azərbaycan)",
42110         "az",
42111         "994"
42112       ],
42113       [
42114         "Bahamas",
42115         "bs",
42116         "1242"
42117       ],
42118       [
42119         "Bahrain (‫البحرين‬‎)",
42120         "bh",
42121         "973"
42122       ],
42123       [
42124         "Bangladesh (বাংলাদেশ)",
42125         "bd",
42126         "880"
42127       ],
42128       [
42129         "Barbados",
42130         "bb",
42131         "1246"
42132       ],
42133       [
42134         "Belarus (Беларусь)",
42135         "by",
42136         "375"
42137       ],
42138       [
42139         "Belgium (België)",
42140         "be",
42141         "32"
42142       ],
42143       [
42144         "Belize",
42145         "bz",
42146         "501"
42147       ],
42148       [
42149         "Benin (Bénin)",
42150         "bj",
42151         "229"
42152       ],
42153       [
42154         "Bermuda",
42155         "bm",
42156         "1441"
42157       ],
42158       [
42159         "Bhutan (འབྲུག)",
42160         "bt",
42161         "975"
42162       ],
42163       [
42164         "Bolivia",
42165         "bo",
42166         "591"
42167       ],
42168       [
42169         "Bosnia and Herzegovina (Босна и Херцеговина)",
42170         "ba",
42171         "387"
42172       ],
42173       [
42174         "Botswana",
42175         "bw",
42176         "267"
42177       ],
42178       [
42179         "Brazil (Brasil)",
42180         "br",
42181         "55"
42182       ],
42183       [
42184         "British Indian Ocean Territory",
42185         "io",
42186         "246"
42187       ],
42188       [
42189         "British Virgin Islands",
42190         "vg",
42191         "1284"
42192       ],
42193       [
42194         "Brunei",
42195         "bn",
42196         "673"
42197       ],
42198       [
42199         "Bulgaria (България)",
42200         "bg",
42201         "359"
42202       ],
42203       [
42204         "Burkina Faso",
42205         "bf",
42206         "226"
42207       ],
42208       [
42209         "Burundi (Uburundi)",
42210         "bi",
42211         "257"
42212       ],
42213       [
42214         "Cambodia (កម្ពុជា)",
42215         "kh",
42216         "855"
42217       ],
42218       [
42219         "Cameroon (Cameroun)",
42220         "cm",
42221         "237"
42222       ],
42223       [
42224         "Canada",
42225         "ca",
42226         "1",
42227         1,
42228         ["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"]
42229       ],
42230       [
42231         "Cape Verde (Kabu Verdi)",
42232         "cv",
42233         "238"
42234       ],
42235       [
42236         "Caribbean Netherlands",
42237         "bq",
42238         "599",
42239         1
42240       ],
42241       [
42242         "Cayman Islands",
42243         "ky",
42244         "1345"
42245       ],
42246       [
42247         "Central African Republic (République centrafricaine)",
42248         "cf",
42249         "236"
42250       ],
42251       [
42252         "Chad (Tchad)",
42253         "td",
42254         "235"
42255       ],
42256       [
42257         "Chile",
42258         "cl",
42259         "56"
42260       ],
42261       [
42262         "China (中国)",
42263         "cn",
42264         "86"
42265       ],
42266       [
42267         "Christmas Island",
42268         "cx",
42269         "61",
42270         2
42271       ],
42272       [
42273         "Cocos (Keeling) Islands",
42274         "cc",
42275         "61",
42276         1
42277       ],
42278       [
42279         "Colombia",
42280         "co",
42281         "57"
42282       ],
42283       [
42284         "Comoros (‫جزر القمر‬‎)",
42285         "km",
42286         "269"
42287       ],
42288       [
42289         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42290         "cd",
42291         "243"
42292       ],
42293       [
42294         "Congo (Republic) (Congo-Brazzaville)",
42295         "cg",
42296         "242"
42297       ],
42298       [
42299         "Cook Islands",
42300         "ck",
42301         "682"
42302       ],
42303       [
42304         "Costa Rica",
42305         "cr",
42306         "506"
42307       ],
42308       [
42309         "Côte d’Ivoire",
42310         "ci",
42311         "225"
42312       ],
42313       [
42314         "Croatia (Hrvatska)",
42315         "hr",
42316         "385"
42317       ],
42318       [
42319         "Cuba",
42320         "cu",
42321         "53"
42322       ],
42323       [
42324         "Curaçao",
42325         "cw",
42326         "599",
42327         0
42328       ],
42329       [
42330         "Cyprus (Κύπρος)",
42331         "cy",
42332         "357"
42333       ],
42334       [
42335         "Czech Republic (Česká republika)",
42336         "cz",
42337         "420"
42338       ],
42339       [
42340         "Denmark (Danmark)",
42341         "dk",
42342         "45"
42343       ],
42344       [
42345         "Djibouti",
42346         "dj",
42347         "253"
42348       ],
42349       [
42350         "Dominica",
42351         "dm",
42352         "1767"
42353       ],
42354       [
42355         "Dominican Republic (República Dominicana)",
42356         "do",
42357         "1",
42358         2,
42359         ["809", "829", "849"]
42360       ],
42361       [
42362         "Ecuador",
42363         "ec",
42364         "593"
42365       ],
42366       [
42367         "Egypt (‫مصر‬‎)",
42368         "eg",
42369         "20"
42370       ],
42371       [
42372         "El Salvador",
42373         "sv",
42374         "503"
42375       ],
42376       [
42377         "Equatorial Guinea (Guinea Ecuatorial)",
42378         "gq",
42379         "240"
42380       ],
42381       [
42382         "Eritrea",
42383         "er",
42384         "291"
42385       ],
42386       [
42387         "Estonia (Eesti)",
42388         "ee",
42389         "372"
42390       ],
42391       [
42392         "Ethiopia",
42393         "et",
42394         "251"
42395       ],
42396       [
42397         "Falkland Islands (Islas Malvinas)",
42398         "fk",
42399         "500"
42400       ],
42401       [
42402         "Faroe Islands (Føroyar)",
42403         "fo",
42404         "298"
42405       ],
42406       [
42407         "Fiji",
42408         "fj",
42409         "679"
42410       ],
42411       [
42412         "Finland (Suomi)",
42413         "fi",
42414         "358",
42415         0
42416       ],
42417       [
42418         "France",
42419         "fr",
42420         "33"
42421       ],
42422       [
42423         "French Guiana (Guyane française)",
42424         "gf",
42425         "594"
42426       ],
42427       [
42428         "French Polynesia (Polynésie française)",
42429         "pf",
42430         "689"
42431       ],
42432       [
42433         "Gabon",
42434         "ga",
42435         "241"
42436       ],
42437       [
42438         "Gambia",
42439         "gm",
42440         "220"
42441       ],
42442       [
42443         "Georgia (საქართველო)",
42444         "ge",
42445         "995"
42446       ],
42447       [
42448         "Germany (Deutschland)",
42449         "de",
42450         "49"
42451       ],
42452       [
42453         "Ghana (Gaana)",
42454         "gh",
42455         "233"
42456       ],
42457       [
42458         "Gibraltar",
42459         "gi",
42460         "350"
42461       ],
42462       [
42463         "Greece (Ελλάδα)",
42464         "gr",
42465         "30"
42466       ],
42467       [
42468         "Greenland (Kalaallit Nunaat)",
42469         "gl",
42470         "299"
42471       ],
42472       [
42473         "Grenada",
42474         "gd",
42475         "1473"
42476       ],
42477       [
42478         "Guadeloupe",
42479         "gp",
42480         "590",
42481         0
42482       ],
42483       [
42484         "Guam",
42485         "gu",
42486         "1671"
42487       ],
42488       [
42489         "Guatemala",
42490         "gt",
42491         "502"
42492       ],
42493       [
42494         "Guernsey",
42495         "gg",
42496         "44",
42497         1
42498       ],
42499       [
42500         "Guinea (Guinée)",
42501         "gn",
42502         "224"
42503       ],
42504       [
42505         "Guinea-Bissau (Guiné Bissau)",
42506         "gw",
42507         "245"
42508       ],
42509       [
42510         "Guyana",
42511         "gy",
42512         "592"
42513       ],
42514       [
42515         "Haiti",
42516         "ht",
42517         "509"
42518       ],
42519       [
42520         "Honduras",
42521         "hn",
42522         "504"
42523       ],
42524       [
42525         "Hong Kong (香港)",
42526         "hk",
42527         "852"
42528       ],
42529       [
42530         "Hungary (Magyarország)",
42531         "hu",
42532         "36"
42533       ],
42534       [
42535         "Iceland (Ísland)",
42536         "is",
42537         "354"
42538       ],
42539       [
42540         "India (भारत)",
42541         "in",
42542         "91"
42543       ],
42544       [
42545         "Indonesia",
42546         "id",
42547         "62"
42548       ],
42549       [
42550         "Iran (‫ایران‬‎)",
42551         "ir",
42552         "98"
42553       ],
42554       [
42555         "Iraq (‫العراق‬‎)",
42556         "iq",
42557         "964"
42558       ],
42559       [
42560         "Ireland",
42561         "ie",
42562         "353"
42563       ],
42564       [
42565         "Isle of Man",
42566         "im",
42567         "44",
42568         2
42569       ],
42570       [
42571         "Israel (‫ישראל‬‎)",
42572         "il",
42573         "972"
42574       ],
42575       [
42576         "Italy (Italia)",
42577         "it",
42578         "39",
42579         0
42580       ],
42581       [
42582         "Jamaica",
42583         "jm",
42584         "1876"
42585       ],
42586       [
42587         "Japan (日本)",
42588         "jp",
42589         "81"
42590       ],
42591       [
42592         "Jersey",
42593         "je",
42594         "44",
42595         3
42596       ],
42597       [
42598         "Jordan (‫الأردن‬‎)",
42599         "jo",
42600         "962"
42601       ],
42602       [
42603         "Kazakhstan (Казахстан)",
42604         "kz",
42605         "7",
42606         1
42607       ],
42608       [
42609         "Kenya",
42610         "ke",
42611         "254"
42612       ],
42613       [
42614         "Kiribati",
42615         "ki",
42616         "686"
42617       ],
42618       [
42619         "Kosovo",
42620         "xk",
42621         "383"
42622       ],
42623       [
42624         "Kuwait (‫الكويت‬‎)",
42625         "kw",
42626         "965"
42627       ],
42628       [
42629         "Kyrgyzstan (Кыргызстан)",
42630         "kg",
42631         "996"
42632       ],
42633       [
42634         "Laos (ລາວ)",
42635         "la",
42636         "856"
42637       ],
42638       [
42639         "Latvia (Latvija)",
42640         "lv",
42641         "371"
42642       ],
42643       [
42644         "Lebanon (‫لبنان‬‎)",
42645         "lb",
42646         "961"
42647       ],
42648       [
42649         "Lesotho",
42650         "ls",
42651         "266"
42652       ],
42653       [
42654         "Liberia",
42655         "lr",
42656         "231"
42657       ],
42658       [
42659         "Libya (‫ليبيا‬‎)",
42660         "ly",
42661         "218"
42662       ],
42663       [
42664         "Liechtenstein",
42665         "li",
42666         "423"
42667       ],
42668       [
42669         "Lithuania (Lietuva)",
42670         "lt",
42671         "370"
42672       ],
42673       [
42674         "Luxembourg",
42675         "lu",
42676         "352"
42677       ],
42678       [
42679         "Macau (澳門)",
42680         "mo",
42681         "853"
42682       ],
42683       [
42684         "Macedonia (FYROM) (Македонија)",
42685         "mk",
42686         "389"
42687       ],
42688       [
42689         "Madagascar (Madagasikara)",
42690         "mg",
42691         "261"
42692       ],
42693       [
42694         "Malawi",
42695         "mw",
42696         "265"
42697       ],
42698       [
42699         "Malaysia",
42700         "my",
42701         "60"
42702       ],
42703       [
42704         "Maldives",
42705         "mv",
42706         "960"
42707       ],
42708       [
42709         "Mali",
42710         "ml",
42711         "223"
42712       ],
42713       [
42714         "Malta",
42715         "mt",
42716         "356"
42717       ],
42718       [
42719         "Marshall Islands",
42720         "mh",
42721         "692"
42722       ],
42723       [
42724         "Martinique",
42725         "mq",
42726         "596"
42727       ],
42728       [
42729         "Mauritania (‫موريتانيا‬‎)",
42730         "mr",
42731         "222"
42732       ],
42733       [
42734         "Mauritius (Moris)",
42735         "mu",
42736         "230"
42737       ],
42738       [
42739         "Mayotte",
42740         "yt",
42741         "262",
42742         1
42743       ],
42744       [
42745         "Mexico (México)",
42746         "mx",
42747         "52"
42748       ],
42749       [
42750         "Micronesia",
42751         "fm",
42752         "691"
42753       ],
42754       [
42755         "Moldova (Republica Moldova)",
42756         "md",
42757         "373"
42758       ],
42759       [
42760         "Monaco",
42761         "mc",
42762         "377"
42763       ],
42764       [
42765         "Mongolia (Монгол)",
42766         "mn",
42767         "976"
42768       ],
42769       [
42770         "Montenegro (Crna Gora)",
42771         "me",
42772         "382"
42773       ],
42774       [
42775         "Montserrat",
42776         "ms",
42777         "1664"
42778       ],
42779       [
42780         "Morocco (‫المغرب‬‎)",
42781         "ma",
42782         "212",
42783         0
42784       ],
42785       [
42786         "Mozambique (Moçambique)",
42787         "mz",
42788         "258"
42789       ],
42790       [
42791         "Myanmar (Burma) (မြန်မာ)",
42792         "mm",
42793         "95"
42794       ],
42795       [
42796         "Namibia (Namibië)",
42797         "na",
42798         "264"
42799       ],
42800       [
42801         "Nauru",
42802         "nr",
42803         "674"
42804       ],
42805       [
42806         "Nepal (नेपाल)",
42807         "np",
42808         "977"
42809       ],
42810       [
42811         "Netherlands (Nederland)",
42812         "nl",
42813         "31"
42814       ],
42815       [
42816         "New Caledonia (Nouvelle-Calédonie)",
42817         "nc",
42818         "687"
42819       ],
42820       [
42821         "New Zealand",
42822         "nz",
42823         "64"
42824       ],
42825       [
42826         "Nicaragua",
42827         "ni",
42828         "505"
42829       ],
42830       [
42831         "Niger (Nijar)",
42832         "ne",
42833         "227"
42834       ],
42835       [
42836         "Nigeria",
42837         "ng",
42838         "234"
42839       ],
42840       [
42841         "Niue",
42842         "nu",
42843         "683"
42844       ],
42845       [
42846         "Norfolk Island",
42847         "nf",
42848         "672"
42849       ],
42850       [
42851         "North Korea (조선 민주주의 인민 공화국)",
42852         "kp",
42853         "850"
42854       ],
42855       [
42856         "Northern Mariana Islands",
42857         "mp",
42858         "1670"
42859       ],
42860       [
42861         "Norway (Norge)",
42862         "no",
42863         "47",
42864         0
42865       ],
42866       [
42867         "Oman (‫عُمان‬‎)",
42868         "om",
42869         "968"
42870       ],
42871       [
42872         "Pakistan (‫پاکستان‬‎)",
42873         "pk",
42874         "92"
42875       ],
42876       [
42877         "Palau",
42878         "pw",
42879         "680"
42880       ],
42881       [
42882         "Palestine (‫فلسطين‬‎)",
42883         "ps",
42884         "970"
42885       ],
42886       [
42887         "Panama (Panamá)",
42888         "pa",
42889         "507"
42890       ],
42891       [
42892         "Papua New Guinea",
42893         "pg",
42894         "675"
42895       ],
42896       [
42897         "Paraguay",
42898         "py",
42899         "595"
42900       ],
42901       [
42902         "Peru (Perú)",
42903         "pe",
42904         "51"
42905       ],
42906       [
42907         "Philippines",
42908         "ph",
42909         "63"
42910       ],
42911       [
42912         "Poland (Polska)",
42913         "pl",
42914         "48"
42915       ],
42916       [
42917         "Portugal",
42918         "pt",
42919         "351"
42920       ],
42921       [
42922         "Puerto Rico",
42923         "pr",
42924         "1",
42925         3,
42926         ["787", "939"]
42927       ],
42928       [
42929         "Qatar (‫قطر‬‎)",
42930         "qa",
42931         "974"
42932       ],
42933       [
42934         "Réunion (La Réunion)",
42935         "re",
42936         "262",
42937         0
42938       ],
42939       [
42940         "Romania (România)",
42941         "ro",
42942         "40"
42943       ],
42944       [
42945         "Russia (Россия)",
42946         "ru",
42947         "7",
42948         0
42949       ],
42950       [
42951         "Rwanda",
42952         "rw",
42953         "250"
42954       ],
42955       [
42956         "Saint Barthélemy",
42957         "bl",
42958         "590",
42959         1
42960       ],
42961       [
42962         "Saint Helena",
42963         "sh",
42964         "290"
42965       ],
42966       [
42967         "Saint Kitts and Nevis",
42968         "kn",
42969         "1869"
42970       ],
42971       [
42972         "Saint Lucia",
42973         "lc",
42974         "1758"
42975       ],
42976       [
42977         "Saint Martin (Saint-Martin (partie française))",
42978         "mf",
42979         "590",
42980         2
42981       ],
42982       [
42983         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42984         "pm",
42985         "508"
42986       ],
42987       [
42988         "Saint Vincent and the Grenadines",
42989         "vc",
42990         "1784"
42991       ],
42992       [
42993         "Samoa",
42994         "ws",
42995         "685"
42996       ],
42997       [
42998         "San Marino",
42999         "sm",
43000         "378"
43001       ],
43002       [
43003         "São Tomé and Príncipe (São Tomé e Príncipe)",
43004         "st",
43005         "239"
43006       ],
43007       [
43008         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43009         "sa",
43010         "966"
43011       ],
43012       [
43013         "Senegal (Sénégal)",
43014         "sn",
43015         "221"
43016       ],
43017       [
43018         "Serbia (Србија)",
43019         "rs",
43020         "381"
43021       ],
43022       [
43023         "Seychelles",
43024         "sc",
43025         "248"
43026       ],
43027       [
43028         "Sierra Leone",
43029         "sl",
43030         "232"
43031       ],
43032       [
43033         "Singapore",
43034         "sg",
43035         "65"
43036       ],
43037       [
43038         "Sint Maarten",
43039         "sx",
43040         "1721"
43041       ],
43042       [
43043         "Slovakia (Slovensko)",
43044         "sk",
43045         "421"
43046       ],
43047       [
43048         "Slovenia (Slovenija)",
43049         "si",
43050         "386"
43051       ],
43052       [
43053         "Solomon Islands",
43054         "sb",
43055         "677"
43056       ],
43057       [
43058         "Somalia (Soomaaliya)",
43059         "so",
43060         "252"
43061       ],
43062       [
43063         "South Africa",
43064         "za",
43065         "27"
43066       ],
43067       [
43068         "South Korea (대한민국)",
43069         "kr",
43070         "82"
43071       ],
43072       [
43073         "South Sudan (‫جنوب السودان‬‎)",
43074         "ss",
43075         "211"
43076       ],
43077       [
43078         "Spain (España)",
43079         "es",
43080         "34"
43081       ],
43082       [
43083         "Sri Lanka (ශ්‍රී ලංකාව)",
43084         "lk",
43085         "94"
43086       ],
43087       [
43088         "Sudan (‫السودان‬‎)",
43089         "sd",
43090         "249"
43091       ],
43092       [
43093         "Suriname",
43094         "sr",
43095         "597"
43096       ],
43097       [
43098         "Svalbard and Jan Mayen",
43099         "sj",
43100         "47",
43101         1
43102       ],
43103       [
43104         "Swaziland",
43105         "sz",
43106         "268"
43107       ],
43108       [
43109         "Sweden (Sverige)",
43110         "se",
43111         "46"
43112       ],
43113       [
43114         "Switzerland (Schweiz)",
43115         "ch",
43116         "41"
43117       ],
43118       [
43119         "Syria (‫سوريا‬‎)",
43120         "sy",
43121         "963"
43122       ],
43123       [
43124         "Taiwan (台灣)",
43125         "tw",
43126         "886"
43127       ],
43128       [
43129         "Tajikistan",
43130         "tj",
43131         "992"
43132       ],
43133       [
43134         "Tanzania",
43135         "tz",
43136         "255"
43137       ],
43138       [
43139         "Thailand (ไทย)",
43140         "th",
43141         "66"
43142       ],
43143       [
43144         "Timor-Leste",
43145         "tl",
43146         "670"
43147       ],
43148       [
43149         "Togo",
43150         "tg",
43151         "228"
43152       ],
43153       [
43154         "Tokelau",
43155         "tk",
43156         "690"
43157       ],
43158       [
43159         "Tonga",
43160         "to",
43161         "676"
43162       ],
43163       [
43164         "Trinidad and Tobago",
43165         "tt",
43166         "1868"
43167       ],
43168       [
43169         "Tunisia (‫تونس‬‎)",
43170         "tn",
43171         "216"
43172       ],
43173       [
43174         "Turkey (Türkiye)",
43175         "tr",
43176         "90"
43177       ],
43178       [
43179         "Turkmenistan",
43180         "tm",
43181         "993"
43182       ],
43183       [
43184         "Turks and Caicos Islands",
43185         "tc",
43186         "1649"
43187       ],
43188       [
43189         "Tuvalu",
43190         "tv",
43191         "688"
43192       ],
43193       [
43194         "U.S. Virgin Islands",
43195         "vi",
43196         "1340"
43197       ],
43198       [
43199         "Uganda",
43200         "ug",
43201         "256"
43202       ],
43203       [
43204         "Ukraine (Україна)",
43205         "ua",
43206         "380"
43207       ],
43208       [
43209         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43210         "ae",
43211         "971"
43212       ],
43213       [
43214         "United Kingdom",
43215         "gb",
43216         "44",
43217         0
43218       ],
43219       [
43220         "United States",
43221         "us",
43222         "1",
43223         0
43224       ],
43225       [
43226         "Uruguay",
43227         "uy",
43228         "598"
43229       ],
43230       [
43231         "Uzbekistan (Oʻzbekiston)",
43232         "uz",
43233         "998"
43234       ],
43235       [
43236         "Vanuatu",
43237         "vu",
43238         "678"
43239       ],
43240       [
43241         "Vatican City (Città del Vaticano)",
43242         "va",
43243         "39",
43244         1
43245       ],
43246       [
43247         "Venezuela",
43248         "ve",
43249         "58"
43250       ],
43251       [
43252         "Vietnam (Việt Nam)",
43253         "vn",
43254         "84"
43255       ],
43256       [
43257         "Wallis and Futuna (Wallis-et-Futuna)",
43258         "wf",
43259         "681"
43260       ],
43261       [
43262         "Western Sahara (‫الصحراء الغربية‬‎)",
43263         "eh",
43264         "212",
43265         1
43266       ],
43267       [
43268         "Yemen (‫اليمن‬‎)",
43269         "ye",
43270         "967"
43271       ],
43272       [
43273         "Zambia",
43274         "zm",
43275         "260"
43276       ],
43277       [
43278         "Zimbabwe",
43279         "zw",
43280         "263"
43281       ],
43282       [
43283         "Åland Islands",
43284         "ax",
43285         "358",
43286         1
43287       ]
43288   ];
43289   
43290   return d;
43291 }/**
43292 *    This script refer to:
43293 *    Title: International Telephone Input
43294 *    Author: Jack O'Connor
43295 *    Code version:  v12.1.12
43296 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43297 **/
43298
43299 /**
43300  * @class Roo.bootstrap.form.PhoneInput
43301  * @extends Roo.bootstrap.form.TriggerField
43302  * An input with International dial-code selection
43303  
43304  * @cfg {String} defaultDialCode default '+852'
43305  * @cfg {Array} preferedCountries default []
43306   
43307  * @constructor
43308  * Create a new PhoneInput.
43309  * @param {Object} config Configuration options
43310  */
43311
43312 Roo.bootstrap.form.PhoneInput = function(config) {
43313     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43314 };
43315
43316 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43317         /**
43318         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43319         */
43320         listWidth: undefined,
43321         
43322         selectedClass: 'active',
43323         
43324         invalidClass : "has-warning",
43325         
43326         validClass: 'has-success',
43327         
43328         allowed: '0123456789',
43329         
43330         max_length: 15,
43331         
43332         /**
43333          * @cfg {String} defaultDialCode The default dial code when initializing the input
43334          */
43335         defaultDialCode: '+852',
43336         
43337         /**
43338          * @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
43339          */
43340         preferedCountries: false,
43341         
43342         getAutoCreate : function()
43343         {
43344             var data = Roo.bootstrap.form.PhoneInputData();
43345             var align = this.labelAlign || this.parentLabelAlign();
43346             var id = Roo.id();
43347             
43348             this.allCountries = [];
43349             this.dialCodeMapping = [];
43350             
43351             for (var i = 0; i < data.length; i++) {
43352               var c = data[i];
43353               this.allCountries[i] = {
43354                 name: c[0],
43355                 iso2: c[1],
43356                 dialCode: c[2],
43357                 priority: c[3] || 0,
43358                 areaCodes: c[4] || null
43359               };
43360               this.dialCodeMapping[c[2]] = {
43361                   name: c[0],
43362                   iso2: c[1],
43363                   priority: c[3] || 0,
43364                   areaCodes: c[4] || null
43365               };
43366             }
43367             
43368             var cfg = {
43369                 cls: 'form-group',
43370                 cn: []
43371             };
43372             
43373             var input =  {
43374                 tag: 'input',
43375                 id : id,
43376                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43377                 maxlength: this.max_length,
43378                 cls : 'form-control tel-input',
43379                 autocomplete: 'new-password'
43380             };
43381             
43382             var hiddenInput = {
43383                 tag: 'input',
43384                 type: 'hidden',
43385                 cls: 'hidden-tel-input'
43386             };
43387             
43388             if (this.name) {
43389                 hiddenInput.name = this.name;
43390             }
43391             
43392             if (this.disabled) {
43393                 input.disabled = true;
43394             }
43395             
43396             var flag_container = {
43397                 tag: 'div',
43398                 cls: 'flag-box',
43399                 cn: [
43400                     {
43401                         tag: 'div',
43402                         cls: 'flag'
43403                     },
43404                     {
43405                         tag: 'div',
43406                         cls: 'caret'
43407                     }
43408                 ]
43409             };
43410             
43411             var box = {
43412                 tag: 'div',
43413                 cls: this.hasFeedback ? 'has-feedback' : '',
43414                 cn: [
43415                     hiddenInput,
43416                     input,
43417                     {
43418                         tag: 'input',
43419                         cls: 'dial-code-holder',
43420                         disabled: true
43421                     }
43422                 ]
43423             };
43424             
43425             var container = {
43426                 cls: 'roo-select2-container input-group',
43427                 cn: [
43428                     flag_container,
43429                     box
43430                 ]
43431             };
43432             
43433             if (this.fieldLabel.length) {
43434                 var indicator = {
43435                     tag: 'i',
43436                     tooltip: 'This field is required'
43437                 };
43438                 
43439                 var label = {
43440                     tag: 'label',
43441                     'for':  id,
43442                     cls: 'control-label',
43443                     cn: []
43444                 };
43445                 
43446                 var label_text = {
43447                     tag: 'span',
43448                     html: this.fieldLabel
43449                 };
43450                 
43451                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43452                 label.cn = [
43453                     indicator,
43454                     label_text
43455                 ];
43456                 
43457                 if(this.indicatorpos == 'right') {
43458                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43459                     label.cn = [
43460                         label_text,
43461                         indicator
43462                     ];
43463                 }
43464                 
43465                 if(align == 'left') {
43466                     container = {
43467                         tag: 'div',
43468                         cn: [
43469                             container
43470                         ]
43471                     };
43472                     
43473                     if(this.labelWidth > 12){
43474                         label.style = "width: " + this.labelWidth + 'px';
43475                     }
43476                     if(this.labelWidth < 13 && this.labelmd == 0){
43477                         this.labelmd = this.labelWidth;
43478                     }
43479                     if(this.labellg > 0){
43480                         label.cls += ' col-lg-' + this.labellg;
43481                         input.cls += ' col-lg-' + (12 - this.labellg);
43482                     }
43483                     if(this.labelmd > 0){
43484                         label.cls += ' col-md-' + this.labelmd;
43485                         container.cls += ' col-md-' + (12 - this.labelmd);
43486                     }
43487                     if(this.labelsm > 0){
43488                         label.cls += ' col-sm-' + this.labelsm;
43489                         container.cls += ' col-sm-' + (12 - this.labelsm);
43490                     }
43491                     if(this.labelxs > 0){
43492                         label.cls += ' col-xs-' + this.labelxs;
43493                         container.cls += ' col-xs-' + (12 - this.labelxs);
43494                     }
43495                 }
43496             }
43497             
43498             cfg.cn = [
43499                 label,
43500                 container
43501             ];
43502             
43503             var settings = this;
43504             
43505             ['xs','sm','md','lg'].map(function(size){
43506                 if (settings[size]) {
43507                     cfg.cls += ' col-' + size + '-' + settings[size];
43508                 }
43509             });
43510             
43511             this.store = new Roo.data.Store({
43512                 proxy : new Roo.data.MemoryProxy({}),
43513                 reader : new Roo.data.JsonReader({
43514                     fields : [
43515                         {
43516                             'name' : 'name',
43517                             'type' : 'string'
43518                         },
43519                         {
43520                             'name' : 'iso2',
43521                             'type' : 'string'
43522                         },
43523                         {
43524                             'name' : 'dialCode',
43525                             'type' : 'string'
43526                         },
43527                         {
43528                             'name' : 'priority',
43529                             'type' : 'string'
43530                         },
43531                         {
43532                             'name' : 'areaCodes',
43533                             'type' : 'string'
43534                         }
43535                     ]
43536                 })
43537             });
43538             
43539             if(!this.preferedCountries) {
43540                 this.preferedCountries = [
43541                     'hk',
43542                     'gb',
43543                     'us'
43544                 ];
43545             }
43546             
43547             var p = this.preferedCountries.reverse();
43548             
43549             if(p) {
43550                 for (var i = 0; i < p.length; i++) {
43551                     for (var j = 0; j < this.allCountries.length; j++) {
43552                         if(this.allCountries[j].iso2 == p[i]) {
43553                             var t = this.allCountries[j];
43554                             this.allCountries.splice(j,1);
43555                             this.allCountries.unshift(t);
43556                         }
43557                     } 
43558                 }
43559             }
43560             
43561             this.store.proxy.data = {
43562                 success: true,
43563                 data: this.allCountries
43564             };
43565             
43566             return cfg;
43567         },
43568         
43569         initEvents : function()
43570         {
43571             this.createList();
43572             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43573             
43574             this.indicator = this.indicatorEl();
43575             this.flag = this.flagEl();
43576             this.dialCodeHolder = this.dialCodeHolderEl();
43577             
43578             this.trigger = this.el.select('div.flag-box',true).first();
43579             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43580             
43581             var _this = this;
43582             
43583             (function(){
43584                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43585                 _this.list.setWidth(lw);
43586             }).defer(100);
43587             
43588             this.list.on('mouseover', this.onViewOver, this);
43589             this.list.on('mousemove', this.onViewMove, this);
43590             this.inputEl().on("keyup", this.onKeyUp, this);
43591             this.inputEl().on("keypress", this.onKeyPress, this);
43592             
43593             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43594
43595             this.view = new Roo.View(this.list, this.tpl, {
43596                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43597             });
43598             
43599             this.view.on('click', this.onViewClick, this);
43600             this.setValue(this.defaultDialCode);
43601         },
43602         
43603         onTriggerClick : function(e)
43604         {
43605             Roo.log('trigger click');
43606             if(this.disabled){
43607                 return;
43608             }
43609             
43610             if(this.isExpanded()){
43611                 this.collapse();
43612                 this.hasFocus = false;
43613             }else {
43614                 this.store.load({});
43615                 this.hasFocus = true;
43616                 this.expand();
43617             }
43618         },
43619         
43620         isExpanded : function()
43621         {
43622             return this.list.isVisible();
43623         },
43624         
43625         collapse : function()
43626         {
43627             if(!this.isExpanded()){
43628                 return;
43629             }
43630             this.list.hide();
43631             Roo.get(document).un('mousedown', this.collapseIf, this);
43632             Roo.get(document).un('mousewheel', this.collapseIf, this);
43633             this.fireEvent('collapse', this);
43634             this.validate();
43635         },
43636         
43637         expand : function()
43638         {
43639             Roo.log('expand');
43640
43641             if(this.isExpanded() || !this.hasFocus){
43642                 return;
43643             }
43644             
43645             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43646             this.list.setWidth(lw);
43647             
43648             this.list.show();
43649             this.restrictHeight();
43650             
43651             Roo.get(document).on('mousedown', this.collapseIf, this);
43652             Roo.get(document).on('mousewheel', this.collapseIf, this);
43653             
43654             this.fireEvent('expand', this);
43655         },
43656         
43657         restrictHeight : function()
43658         {
43659             this.list.alignTo(this.inputEl(), this.listAlign);
43660             this.list.alignTo(this.inputEl(), this.listAlign);
43661         },
43662         
43663         onViewOver : function(e, t)
43664         {
43665             if(this.inKeyMode){
43666                 return;
43667             }
43668             var item = this.view.findItemFromChild(t);
43669             
43670             if(item){
43671                 var index = this.view.indexOf(item);
43672                 this.select(index, false);
43673             }
43674         },
43675
43676         // private
43677         onViewClick : function(view, doFocus, el, e)
43678         {
43679             var index = this.view.getSelectedIndexes()[0];
43680             
43681             var r = this.store.getAt(index);
43682             
43683             if(r){
43684                 this.onSelect(r, index);
43685             }
43686             if(doFocus !== false && !this.blockFocus){
43687                 this.inputEl().focus();
43688             }
43689         },
43690         
43691         onViewMove : function(e, t)
43692         {
43693             this.inKeyMode = false;
43694         },
43695         
43696         select : function(index, scrollIntoView)
43697         {
43698             this.selectedIndex = index;
43699             this.view.select(index);
43700             if(scrollIntoView !== false){
43701                 var el = this.view.getNode(index);
43702                 if(el){
43703                     this.list.scrollChildIntoView(el, false);
43704                 }
43705             }
43706         },
43707         
43708         createList : function()
43709         {
43710             this.list = Roo.get(document.body).createChild({
43711                 tag: 'ul',
43712                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43713                 style: 'display:none'
43714             });
43715             
43716             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43717         },
43718         
43719         collapseIf : function(e)
43720         {
43721             var in_combo  = e.within(this.el);
43722             var in_list =  e.within(this.list);
43723             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43724             
43725             if (in_combo || in_list || is_list) {
43726                 return;
43727             }
43728             this.collapse();
43729         },
43730         
43731         onSelect : function(record, index)
43732         {
43733             if(this.fireEvent('beforeselect', this, record, index) !== false){
43734                 
43735                 this.setFlagClass(record.data.iso2);
43736                 this.setDialCode(record.data.dialCode);
43737                 this.hasFocus = false;
43738                 this.collapse();
43739                 this.fireEvent('select', this, record, index);
43740             }
43741         },
43742         
43743         flagEl : function()
43744         {
43745             var flag = this.el.select('div.flag',true).first();
43746             if(!flag){
43747                 return false;
43748             }
43749             return flag;
43750         },
43751         
43752         dialCodeHolderEl : function()
43753         {
43754             var d = this.el.select('input.dial-code-holder',true).first();
43755             if(!d){
43756                 return false;
43757             }
43758             return d;
43759         },
43760         
43761         setDialCode : function(v)
43762         {
43763             this.dialCodeHolder.dom.value = '+'+v;
43764         },
43765         
43766         setFlagClass : function(n)
43767         {
43768             this.flag.dom.className = 'flag '+n;
43769         },
43770         
43771         getValue : function()
43772         {
43773             var v = this.inputEl().getValue();
43774             if(this.dialCodeHolder) {
43775                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43776             }
43777             return v;
43778         },
43779         
43780         setValue : function(v)
43781         {
43782             var d = this.getDialCode(v);
43783             
43784             //invalid dial code
43785             if(v.length == 0 || !d || d.length == 0) {
43786                 if(this.rendered){
43787                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43788                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43789                 }
43790                 return;
43791             }
43792             
43793             //valid dial code
43794             this.setFlagClass(this.dialCodeMapping[d].iso2);
43795             this.setDialCode(d);
43796             this.inputEl().dom.value = v.replace('+'+d,'');
43797             this.hiddenEl().dom.value = this.getValue();
43798             
43799             this.validate();
43800         },
43801         
43802         getDialCode : function(v)
43803         {
43804             v = v ||  '';
43805             
43806             if (v.length == 0) {
43807                 return this.dialCodeHolder.dom.value;
43808             }
43809             
43810             var dialCode = "";
43811             if (v.charAt(0) != "+") {
43812                 return false;
43813             }
43814             var numericChars = "";
43815             for (var i = 1; i < v.length; i++) {
43816               var c = v.charAt(i);
43817               if (!isNaN(c)) {
43818                 numericChars += c;
43819                 if (this.dialCodeMapping[numericChars]) {
43820                   dialCode = v.substr(1, i);
43821                 }
43822                 if (numericChars.length == 4) {
43823                   break;
43824                 }
43825               }
43826             }
43827             return dialCode;
43828         },
43829         
43830         reset : function()
43831         {
43832             this.setValue(this.defaultDialCode);
43833             this.validate();
43834         },
43835         
43836         hiddenEl : function()
43837         {
43838             return this.el.select('input.hidden-tel-input',true).first();
43839         },
43840         
43841         // after setting val
43842         onKeyUp : function(e){
43843             this.setValue(this.getValue());
43844         },
43845         
43846         onKeyPress : function(e){
43847             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43848                 e.stopEvent();
43849             }
43850         }
43851         
43852 });
43853 /**
43854  * @class Roo.bootstrap.form.MoneyField
43855  * @extends Roo.bootstrap.form.ComboBox
43856  * Bootstrap MoneyField class
43857  * 
43858  * @constructor
43859  * Create a new MoneyField.
43860  * @param {Object} config Configuration options
43861  */
43862
43863 Roo.bootstrap.form.MoneyField = function(config) {
43864     
43865     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43866     
43867 };
43868
43869 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43870     
43871     /**
43872      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43873      */
43874     allowDecimals : true,
43875     /**
43876      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43877      */
43878     decimalSeparator : ".",
43879     /**
43880      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43881      */
43882     decimalPrecision : 0,
43883     /**
43884      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43885      */
43886     allowNegative : true,
43887     /**
43888      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43889      */
43890     allowZero: true,
43891     /**
43892      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43893      */
43894     minValue : Number.NEGATIVE_INFINITY,
43895     /**
43896      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43897      */
43898     maxValue : Number.MAX_VALUE,
43899     /**
43900      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43901      */
43902     minText : "The minimum value for this field is {0}",
43903     /**
43904      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43905      */
43906     maxText : "The maximum value for this field is {0}",
43907     /**
43908      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43909      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43910      */
43911     nanText : "{0} is not a valid number",
43912     /**
43913      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43914      */
43915     castInt : true,
43916     /**
43917      * @cfg {String} defaults currency of the MoneyField
43918      * value should be in lkey
43919      */
43920     defaultCurrency : false,
43921     /**
43922      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43923      */
43924     thousandsDelimiter : false,
43925     /**
43926      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43927      */
43928     max_length: false,
43929     
43930     inputlg : 9,
43931     inputmd : 9,
43932     inputsm : 9,
43933     inputxs : 6,
43934      /**
43935      * @cfg {Roo.data.Store} store  Store to lookup currency??
43936      */
43937     store : false,
43938     
43939     getAutoCreate : function()
43940     {
43941         var align = this.labelAlign || this.parentLabelAlign();
43942         
43943         var id = Roo.id();
43944
43945         var cfg = {
43946             cls: 'form-group',
43947             cn: []
43948         };
43949
43950         var input =  {
43951             tag: 'input',
43952             id : id,
43953             cls : 'form-control roo-money-amount-input',
43954             autocomplete: 'new-password'
43955         };
43956         
43957         var hiddenInput = {
43958             tag: 'input',
43959             type: 'hidden',
43960             id: Roo.id(),
43961             cls: 'hidden-number-input'
43962         };
43963         
43964         if(this.max_length) {
43965             input.maxlength = this.max_length; 
43966         }
43967         
43968         if (this.name) {
43969             hiddenInput.name = this.name;
43970         }
43971
43972         if (this.disabled) {
43973             input.disabled = true;
43974         }
43975
43976         var clg = 12 - this.inputlg;
43977         var cmd = 12 - this.inputmd;
43978         var csm = 12 - this.inputsm;
43979         var cxs = 12 - this.inputxs;
43980         
43981         var container = {
43982             tag : 'div',
43983             cls : 'row roo-money-field',
43984             cn : [
43985                 {
43986                     tag : 'div',
43987                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43988                     cn : [
43989                         {
43990                             tag : 'div',
43991                             cls: 'roo-select2-container input-group',
43992                             cn: [
43993                                 {
43994                                     tag : 'input',
43995                                     cls : 'form-control roo-money-currency-input',
43996                                     autocomplete: 'new-password',
43997                                     readOnly : 1,
43998                                     name : this.currencyName
43999                                 },
44000                                 {
44001                                     tag :'span',
44002                                     cls : 'input-group-addon',
44003                                     cn : [
44004                                         {
44005                                             tag: 'span',
44006                                             cls: 'caret'
44007                                         }
44008                                     ]
44009                                 }
44010                             ]
44011                         }
44012                     ]
44013                 },
44014                 {
44015                     tag : 'div',
44016                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44017                     cn : [
44018                         {
44019                             tag: 'div',
44020                             cls: this.hasFeedback ? 'has-feedback' : '',
44021                             cn: [
44022                                 input
44023                             ]
44024                         }
44025                     ]
44026                 }
44027             ]
44028             
44029         };
44030         
44031         if (this.fieldLabel.length) {
44032             var indicator = {
44033                 tag: 'i',
44034                 tooltip: 'This field is required'
44035             };
44036
44037             var label = {
44038                 tag: 'label',
44039                 'for':  id,
44040                 cls: 'control-label',
44041                 cn: []
44042             };
44043
44044             var label_text = {
44045                 tag: 'span',
44046                 html: this.fieldLabel
44047             };
44048
44049             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44050             label.cn = [
44051                 indicator,
44052                 label_text
44053             ];
44054
44055             if(this.indicatorpos == 'right') {
44056                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44057                 label.cn = [
44058                     label_text,
44059                     indicator
44060                 ];
44061             }
44062
44063             if(align == 'left') {
44064                 container = {
44065                     tag: 'div',
44066                     cn: [
44067                         container
44068                     ]
44069                 };
44070
44071                 if(this.labelWidth > 12){
44072                     label.style = "width: " + this.labelWidth + 'px';
44073                 }
44074                 if(this.labelWidth < 13 && this.labelmd == 0){
44075                     this.labelmd = this.labelWidth;
44076                 }
44077                 if(this.labellg > 0){
44078                     label.cls += ' col-lg-' + this.labellg;
44079                     input.cls += ' col-lg-' + (12 - this.labellg);
44080                 }
44081                 if(this.labelmd > 0){
44082                     label.cls += ' col-md-' + this.labelmd;
44083                     container.cls += ' col-md-' + (12 - this.labelmd);
44084                 }
44085                 if(this.labelsm > 0){
44086                     label.cls += ' col-sm-' + this.labelsm;
44087                     container.cls += ' col-sm-' + (12 - this.labelsm);
44088                 }
44089                 if(this.labelxs > 0){
44090                     label.cls += ' col-xs-' + this.labelxs;
44091                     container.cls += ' col-xs-' + (12 - this.labelxs);
44092                 }
44093             }
44094         }
44095
44096         cfg.cn = [
44097             label,
44098             container,
44099             hiddenInput
44100         ];
44101         
44102         var settings = this;
44103
44104         ['xs','sm','md','lg'].map(function(size){
44105             if (settings[size]) {
44106                 cfg.cls += ' col-' + size + '-' + settings[size];
44107             }
44108         });
44109         
44110         return cfg;
44111     },
44112     
44113     initEvents : function()
44114     {
44115         this.indicator = this.indicatorEl();
44116         
44117         this.initCurrencyEvent();
44118         
44119         this.initNumberEvent();
44120     },
44121     
44122     initCurrencyEvent : function()
44123     {
44124         if (!this.store) {
44125             throw "can not find store for combo";
44126         }
44127         
44128         this.store = Roo.factory(this.store, Roo.data);
44129         this.store.parent = this;
44130         
44131         this.createList();
44132         
44133         this.triggerEl = this.el.select('.input-group-addon', true).first();
44134         
44135         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44136         
44137         var _this = this;
44138         
44139         (function(){
44140             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44141             _this.list.setWidth(lw);
44142         }).defer(100);
44143         
44144         this.list.on('mouseover', this.onViewOver, this);
44145         this.list.on('mousemove', this.onViewMove, this);
44146         this.list.on('scroll', this.onViewScroll, this);
44147         
44148         if(!this.tpl){
44149             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44150         }
44151         
44152         this.view = new Roo.View(this.list, this.tpl, {
44153             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44154         });
44155         
44156         this.view.on('click', this.onViewClick, this);
44157         
44158         this.store.on('beforeload', this.onBeforeLoad, this);
44159         this.store.on('load', this.onLoad, this);
44160         this.store.on('loadexception', this.onLoadException, this);
44161         
44162         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44163             "up" : function(e){
44164                 this.inKeyMode = true;
44165                 this.selectPrev();
44166             },
44167
44168             "down" : function(e){
44169                 if(!this.isExpanded()){
44170                     this.onTriggerClick();
44171                 }else{
44172                     this.inKeyMode = true;
44173                     this.selectNext();
44174                 }
44175             },
44176
44177             "enter" : function(e){
44178                 this.collapse();
44179                 
44180                 if(this.fireEvent("specialkey", this, e)){
44181                     this.onViewClick(false);
44182                 }
44183                 
44184                 return true;
44185             },
44186
44187             "esc" : function(e){
44188                 this.collapse();
44189             },
44190
44191             "tab" : function(e){
44192                 this.collapse();
44193                 
44194                 if(this.fireEvent("specialkey", this, e)){
44195                     this.onViewClick(false);
44196                 }
44197                 
44198                 return true;
44199             },
44200
44201             scope : this,
44202
44203             doRelay : function(foo, bar, hname){
44204                 if(hname == 'down' || this.scope.isExpanded()){
44205                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44206                 }
44207                 return true;
44208             },
44209
44210             forceKeyDown: true
44211         });
44212         
44213         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44214         
44215     },
44216     
44217     initNumberEvent : function(e)
44218     {
44219         this.inputEl().on("keydown" , this.fireKey,  this);
44220         this.inputEl().on("focus", this.onFocus,  this);
44221         this.inputEl().on("blur", this.onBlur,  this);
44222         
44223         this.inputEl().relayEvent('keyup', this);
44224         
44225         if(this.indicator){
44226             this.indicator.addClass('invisible');
44227         }
44228  
44229         this.originalValue = this.getValue();
44230         
44231         if(this.validationEvent == 'keyup'){
44232             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44233             this.inputEl().on('keyup', this.filterValidation, this);
44234         }
44235         else if(this.validationEvent !== false){
44236             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44237         }
44238         
44239         if(this.selectOnFocus){
44240             this.on("focus", this.preFocus, this);
44241             
44242         }
44243         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44244             this.inputEl().on("keypress", this.filterKeys, this);
44245         } else {
44246             this.inputEl().relayEvent('keypress', this);
44247         }
44248         
44249         var allowed = "0123456789";
44250         
44251         if(this.allowDecimals){
44252             allowed += this.decimalSeparator;
44253         }
44254         
44255         if(this.allowNegative){
44256             allowed += "-";
44257         }
44258         
44259         if(this.thousandsDelimiter) {
44260             allowed += ",";
44261         }
44262         
44263         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44264         
44265         var keyPress = function(e){
44266             
44267             var k = e.getKey();
44268             
44269             var c = e.getCharCode();
44270             
44271             if(
44272                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44273                     allowed.indexOf(String.fromCharCode(c)) === -1
44274             ){
44275                 e.stopEvent();
44276                 return;
44277             }
44278             
44279             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44280                 return;
44281             }
44282             
44283             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44284                 e.stopEvent();
44285             }
44286         };
44287         
44288         this.inputEl().on("keypress", keyPress, this);
44289         
44290     },
44291     
44292     onTriggerClick : function(e)
44293     {   
44294         if(this.disabled){
44295             return;
44296         }
44297         
44298         this.page = 0;
44299         this.loadNext = false;
44300         
44301         if(this.isExpanded()){
44302             this.collapse();
44303             return;
44304         }
44305         
44306         this.hasFocus = true;
44307         
44308         if(this.triggerAction == 'all') {
44309             this.doQuery(this.allQuery, true);
44310             return;
44311         }
44312         
44313         this.doQuery(this.getRawValue());
44314     },
44315     
44316     getCurrency : function()
44317     {   
44318         var v = this.currencyEl().getValue();
44319         
44320         return v;
44321     },
44322     
44323     restrictHeight : function()
44324     {
44325         this.list.alignTo(this.currencyEl(), this.listAlign);
44326         this.list.alignTo(this.currencyEl(), this.listAlign);
44327     },
44328     
44329     onViewClick : function(view, doFocus, el, e)
44330     {
44331         var index = this.view.getSelectedIndexes()[0];
44332         
44333         var r = this.store.getAt(index);
44334         
44335         if(r){
44336             this.onSelect(r, index);
44337         }
44338     },
44339     
44340     onSelect : function(record, index){
44341         
44342         if(this.fireEvent('beforeselect', this, record, index) !== false){
44343         
44344             this.setFromCurrencyData(index > -1 ? record.data : false);
44345             
44346             this.collapse();
44347             
44348             this.fireEvent('select', this, record, index);
44349         }
44350     },
44351     
44352     setFromCurrencyData : function(o)
44353     {
44354         var currency = '';
44355         
44356         this.lastCurrency = o;
44357         
44358         if (this.currencyField) {
44359             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44360         } else {
44361             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44362         }
44363         
44364         this.lastSelectionText = currency;
44365         
44366         //setting default currency
44367         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44368             this.setCurrency(this.defaultCurrency);
44369             return;
44370         }
44371         
44372         this.setCurrency(currency);
44373     },
44374     
44375     setFromData : function(o)
44376     {
44377         var c = {};
44378         
44379         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44380         
44381         this.setFromCurrencyData(c);
44382         
44383         var value = '';
44384         
44385         if (this.name) {
44386             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44387         } else {
44388             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44389         }
44390         
44391         this.setValue(value);
44392         
44393     },
44394     
44395     setCurrency : function(v)
44396     {   
44397         this.currencyValue = v;
44398         
44399         if(this.rendered){
44400             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44401             this.validate();
44402         }
44403     },
44404     
44405     setValue : function(v)
44406     {
44407         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44408         
44409         this.value = v;
44410         
44411         if(this.rendered){
44412             
44413             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44414             
44415             this.inputEl().dom.value = (v == '') ? '' :
44416                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44417             
44418             if(!this.allowZero && v === '0') {
44419                 this.hiddenEl().dom.value = '';
44420                 this.inputEl().dom.value = '';
44421             }
44422             
44423             this.validate();
44424         }
44425     },
44426     
44427     getRawValue : function()
44428     {
44429         var v = this.inputEl().getValue();
44430         
44431         return v;
44432     },
44433     
44434     getValue : function()
44435     {
44436         return this.fixPrecision(this.parseValue(this.getRawValue()));
44437     },
44438     
44439     parseValue : function(value)
44440     {
44441         if(this.thousandsDelimiter) {
44442             value += "";
44443             r = new RegExp(",", "g");
44444             value = value.replace(r, "");
44445         }
44446         
44447         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44448         return isNaN(value) ? '' : value;
44449         
44450     },
44451     
44452     fixPrecision : function(value)
44453     {
44454         if(this.thousandsDelimiter) {
44455             value += "";
44456             r = new RegExp(",", "g");
44457             value = value.replace(r, "");
44458         }
44459         
44460         var nan = isNaN(value);
44461         
44462         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44463             return nan ? '' : value;
44464         }
44465         return parseFloat(value).toFixed(this.decimalPrecision);
44466     },
44467     
44468     decimalPrecisionFcn : function(v)
44469     {
44470         return Math.floor(v);
44471     },
44472     
44473     validateValue : function(value)
44474     {
44475         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44476             return false;
44477         }
44478         
44479         var num = this.parseValue(value);
44480         
44481         if(isNaN(num)){
44482             this.markInvalid(String.format(this.nanText, value));
44483             return false;
44484         }
44485         
44486         if(num < this.minValue){
44487             this.markInvalid(String.format(this.minText, this.minValue));
44488             return false;
44489         }
44490         
44491         if(num > this.maxValue){
44492             this.markInvalid(String.format(this.maxText, this.maxValue));
44493             return false;
44494         }
44495         
44496         return true;
44497     },
44498     
44499     validate : function()
44500     {
44501         if(this.disabled || this.allowBlank){
44502             this.markValid();
44503             return true;
44504         }
44505         
44506         var currency = this.getCurrency();
44507         
44508         if(this.validateValue(this.getRawValue()) && currency.length){
44509             this.markValid();
44510             return true;
44511         }
44512         
44513         this.markInvalid();
44514         return false;
44515     },
44516     
44517     getName: function()
44518     {
44519         return this.name;
44520     },
44521     
44522     beforeBlur : function()
44523     {
44524         if(!this.castInt){
44525             return;
44526         }
44527         
44528         var v = this.parseValue(this.getRawValue());
44529         
44530         if(v || v == 0){
44531             this.setValue(v);
44532         }
44533     },
44534     
44535     onBlur : function()
44536     {
44537         this.beforeBlur();
44538         
44539         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44540             //this.el.removeClass(this.focusClass);
44541         }
44542         
44543         this.hasFocus = false;
44544         
44545         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44546             this.validate();
44547         }
44548         
44549         var v = this.getValue();
44550         
44551         if(String(v) !== String(this.startValue)){
44552             this.fireEvent('change', this, v, this.startValue);
44553         }
44554         
44555         this.fireEvent("blur", this);
44556     },
44557     
44558     inputEl : function()
44559     {
44560         return this.el.select('.roo-money-amount-input', true).first();
44561     },
44562     
44563     currencyEl : function()
44564     {
44565         return this.el.select('.roo-money-currency-input', true).first();
44566     },
44567     
44568     hiddenEl : function()
44569     {
44570         return this.el.select('input.hidden-number-input',true).first();
44571     }
44572     
44573 });/**
44574  * @class Roo.bootstrap.BezierSignature
44575  * @extends Roo.bootstrap.Component
44576  * Bootstrap BezierSignature class
44577  * This script refer to:
44578  *    Title: Signature Pad
44579  *    Author: szimek
44580  *    Availability: https://github.com/szimek/signature_pad
44581  *
44582  * @constructor
44583  * Create a new BezierSignature
44584  * @param {Object} config The config object
44585  */
44586
44587 Roo.bootstrap.BezierSignature = function(config){
44588     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44589     this.addEvents({
44590         "resize" : true
44591     });
44592 };
44593
44594 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44595 {
44596      
44597     curve_data: [],
44598     
44599     is_empty: true,
44600     
44601     mouse_btn_down: true,
44602     
44603     /**
44604      * @cfg {int} canvas height
44605      */
44606     canvas_height: '200px',
44607     
44608     /**
44609      * @cfg {float|function} Radius of a single dot.
44610      */ 
44611     dot_size: false,
44612     
44613     /**
44614      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44615      */
44616     min_width: 0.5,
44617     
44618     /**
44619      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44620      */
44621     max_width: 2.5,
44622     
44623     /**
44624      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44625      */
44626     throttle: 16,
44627     
44628     /**
44629      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44630      */
44631     min_distance: 5,
44632     
44633     /**
44634      * @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.
44635      */
44636     bg_color: 'rgba(0, 0, 0, 0)',
44637     
44638     /**
44639      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44640      */
44641     dot_color: 'black',
44642     
44643     /**
44644      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44645      */ 
44646     velocity_filter_weight: 0.7,
44647     
44648     /**
44649      * @cfg {function} Callback when stroke begin. 
44650      */
44651     onBegin: false,
44652     
44653     /**
44654      * @cfg {function} Callback when stroke end.
44655      */
44656     onEnd: false,
44657     
44658     getAutoCreate : function()
44659     {
44660         var cls = 'roo-signature column';
44661         
44662         if(this.cls){
44663             cls += ' ' + this.cls;
44664         }
44665         
44666         var col_sizes = [
44667             'lg',
44668             'md',
44669             'sm',
44670             'xs'
44671         ];
44672         
44673         for(var i = 0; i < col_sizes.length; i++) {
44674             if(this[col_sizes[i]]) {
44675                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44676             }
44677         }
44678         
44679         var cfg = {
44680             tag: 'div',
44681             cls: cls,
44682             cn: [
44683                 {
44684                     tag: 'div',
44685                     cls: 'roo-signature-body',
44686                     cn: [
44687                         {
44688                             tag: 'canvas',
44689                             cls: 'roo-signature-body-canvas',
44690                             height: this.canvas_height,
44691                             width: this.canvas_width
44692                         }
44693                     ]
44694                 },
44695                 {
44696                     tag: 'input',
44697                     type: 'file',
44698                     style: 'display: none'
44699                 }
44700             ]
44701         };
44702         
44703         return cfg;
44704     },
44705     
44706     initEvents: function() 
44707     {
44708         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44709         
44710         var canvas = this.canvasEl();
44711         
44712         // mouse && touch event swapping...
44713         canvas.dom.style.touchAction = 'none';
44714         canvas.dom.style.msTouchAction = 'none';
44715         
44716         this.mouse_btn_down = false;
44717         canvas.on('mousedown', this._handleMouseDown, this);
44718         canvas.on('mousemove', this._handleMouseMove, this);
44719         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44720         
44721         if (window.PointerEvent) {
44722             canvas.on('pointerdown', this._handleMouseDown, this);
44723             canvas.on('pointermove', this._handleMouseMove, this);
44724             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44725         }
44726         
44727         if ('ontouchstart' in window) {
44728             canvas.on('touchstart', this._handleTouchStart, this);
44729             canvas.on('touchmove', this._handleTouchMove, this);
44730             canvas.on('touchend', this._handleTouchEnd, this);
44731         }
44732         
44733         Roo.EventManager.onWindowResize(this.resize, this, true);
44734         
44735         // file input event
44736         this.fileEl().on('change', this.uploadImage, this);
44737         
44738         this.clear();
44739         
44740         this.resize();
44741     },
44742     
44743     resize: function(){
44744         
44745         var canvas = this.canvasEl().dom;
44746         var ctx = this.canvasElCtx();
44747         var img_data = false;
44748         
44749         if(canvas.width > 0) {
44750             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44751         }
44752         // setting canvas width will clean img data
44753         canvas.width = 0;
44754         
44755         var style = window.getComputedStyle ? 
44756             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44757             
44758         var padding_left = parseInt(style.paddingLeft) || 0;
44759         var padding_right = parseInt(style.paddingRight) || 0;
44760         
44761         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44762         
44763         if(img_data) {
44764             ctx.putImageData(img_data, 0, 0);
44765         }
44766     },
44767     
44768     _handleMouseDown: function(e)
44769     {
44770         if (e.browserEvent.which === 1) {
44771             this.mouse_btn_down = true;
44772             this.strokeBegin(e);
44773         }
44774     },
44775     
44776     _handleMouseMove: function (e)
44777     {
44778         if (this.mouse_btn_down) {
44779             this.strokeMoveUpdate(e);
44780         }
44781     },
44782     
44783     _handleMouseUp: function (e)
44784     {
44785         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44786             this.mouse_btn_down = false;
44787             this.strokeEnd(e);
44788         }
44789     },
44790     
44791     _handleTouchStart: function (e) {
44792         
44793         e.preventDefault();
44794         if (e.browserEvent.targetTouches.length === 1) {
44795             // var touch = e.browserEvent.changedTouches[0];
44796             // this.strokeBegin(touch);
44797             
44798              this.strokeBegin(e); // assume e catching the correct xy...
44799         }
44800     },
44801     
44802     _handleTouchMove: function (e) {
44803         e.preventDefault();
44804         // var touch = event.targetTouches[0];
44805         // _this._strokeMoveUpdate(touch);
44806         this.strokeMoveUpdate(e);
44807     },
44808     
44809     _handleTouchEnd: function (e) {
44810         var wasCanvasTouched = e.target === this.canvasEl().dom;
44811         if (wasCanvasTouched) {
44812             e.preventDefault();
44813             // var touch = event.changedTouches[0];
44814             // _this._strokeEnd(touch);
44815             this.strokeEnd(e);
44816         }
44817     },
44818     
44819     reset: function () {
44820         this._lastPoints = [];
44821         this._lastVelocity = 0;
44822         this._lastWidth = (this.min_width + this.max_width) / 2;
44823         this.canvasElCtx().fillStyle = this.dot_color;
44824     },
44825     
44826     strokeMoveUpdate: function(e)
44827     {
44828         this.strokeUpdate(e);
44829         
44830         if (this.throttle) {
44831             this.throttleStroke(this.strokeUpdate, this.throttle);
44832         }
44833         else {
44834             this.strokeUpdate(e);
44835         }
44836     },
44837     
44838     strokeBegin: function(e)
44839     {
44840         var newPointGroup = {
44841             color: this.dot_color,
44842             points: []
44843         };
44844         
44845         if (typeof this.onBegin === 'function') {
44846             this.onBegin(e);
44847         }
44848         
44849         this.curve_data.push(newPointGroup);
44850         this.reset();
44851         this.strokeUpdate(e);
44852     },
44853     
44854     strokeUpdate: function(e)
44855     {
44856         var rect = this.canvasEl().dom.getBoundingClientRect();
44857         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44858         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44859         var lastPoints = lastPointGroup.points;
44860         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44861         var isLastPointTooClose = lastPoint
44862             ? point.distanceTo(lastPoint) <= this.min_distance
44863             : false;
44864         var color = lastPointGroup.color;
44865         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44866             var curve = this.addPoint(point);
44867             if (!lastPoint) {
44868                 this.drawDot({color: color, point: point});
44869             }
44870             else if (curve) {
44871                 this.drawCurve({color: color, curve: curve});
44872             }
44873             lastPoints.push({
44874                 time: point.time,
44875                 x: point.x,
44876                 y: point.y
44877             });
44878         }
44879     },
44880     
44881     strokeEnd: function(e)
44882     {
44883         this.strokeUpdate(e);
44884         if (typeof this.onEnd === 'function') {
44885             this.onEnd(e);
44886         }
44887     },
44888     
44889     addPoint:  function (point) {
44890         var _lastPoints = this._lastPoints;
44891         _lastPoints.push(point);
44892         if (_lastPoints.length > 2) {
44893             if (_lastPoints.length === 3) {
44894                 _lastPoints.unshift(_lastPoints[0]);
44895             }
44896             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44897             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44898             _lastPoints.shift();
44899             return curve;
44900         }
44901         return null;
44902     },
44903     
44904     calculateCurveWidths: function (startPoint, endPoint) {
44905         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44906             (1 - this.velocity_filter_weight) * this._lastVelocity;
44907
44908         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44909         var widths = {
44910             end: newWidth,
44911             start: this._lastWidth
44912         };
44913         
44914         this._lastVelocity = velocity;
44915         this._lastWidth = newWidth;
44916         return widths;
44917     },
44918     
44919     drawDot: function (_a) {
44920         var color = _a.color, point = _a.point;
44921         var ctx = this.canvasElCtx();
44922         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44923         ctx.beginPath();
44924         this.drawCurveSegment(point.x, point.y, width);
44925         ctx.closePath();
44926         ctx.fillStyle = color;
44927         ctx.fill();
44928     },
44929     
44930     drawCurve: function (_a) {
44931         var color = _a.color, curve = _a.curve;
44932         var ctx = this.canvasElCtx();
44933         var widthDelta = curve.endWidth - curve.startWidth;
44934         var drawSteps = Math.floor(curve.length()) * 2;
44935         ctx.beginPath();
44936         ctx.fillStyle = color;
44937         for (var i = 0; i < drawSteps; i += 1) {
44938         var t = i / drawSteps;
44939         var tt = t * t;
44940         var ttt = tt * t;
44941         var u = 1 - t;
44942         var uu = u * u;
44943         var uuu = uu * u;
44944         var x = uuu * curve.startPoint.x;
44945         x += 3 * uu * t * curve.control1.x;
44946         x += 3 * u * tt * curve.control2.x;
44947         x += ttt * curve.endPoint.x;
44948         var y = uuu * curve.startPoint.y;
44949         y += 3 * uu * t * curve.control1.y;
44950         y += 3 * u * tt * curve.control2.y;
44951         y += ttt * curve.endPoint.y;
44952         var width = curve.startWidth + ttt * widthDelta;
44953         this.drawCurveSegment(x, y, width);
44954         }
44955         ctx.closePath();
44956         ctx.fill();
44957     },
44958     
44959     drawCurveSegment: function (x, y, width) {
44960         var ctx = this.canvasElCtx();
44961         ctx.moveTo(x, y);
44962         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44963         this.is_empty = false;
44964     },
44965     
44966     clear: function()
44967     {
44968         var ctx = this.canvasElCtx();
44969         var canvas = this.canvasEl().dom;
44970         ctx.fillStyle = this.bg_color;
44971         ctx.clearRect(0, 0, canvas.width, canvas.height);
44972         ctx.fillRect(0, 0, canvas.width, canvas.height);
44973         this.curve_data = [];
44974         this.reset();
44975         this.is_empty = true;
44976     },
44977     
44978     fileEl: function()
44979     {
44980         return  this.el.select('input',true).first();
44981     },
44982     
44983     canvasEl: function()
44984     {
44985         return this.el.select('canvas',true).first();
44986     },
44987     
44988     canvasElCtx: function()
44989     {
44990         return this.el.select('canvas',true).first().dom.getContext('2d');
44991     },
44992     
44993     getImage: function(type)
44994     {
44995         if(this.is_empty) {
44996             return false;
44997         }
44998         
44999         // encryption ?
45000         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45001     },
45002     
45003     drawFromImage: function(img_src)
45004     {
45005         var img = new Image();
45006         
45007         img.onload = function(){
45008             this.canvasElCtx().drawImage(img, 0, 0);
45009         }.bind(this);
45010         
45011         img.src = img_src;
45012         
45013         this.is_empty = false;
45014     },
45015     
45016     selectImage: function()
45017     {
45018         this.fileEl().dom.click();
45019     },
45020     
45021     uploadImage: function(e)
45022     {
45023         var reader = new FileReader();
45024         
45025         reader.onload = function(e){
45026             var img = new Image();
45027             img.onload = function(){
45028                 this.reset();
45029                 this.canvasElCtx().drawImage(img, 0, 0);
45030             }.bind(this);
45031             img.src = e.target.result;
45032         }.bind(this);
45033         
45034         reader.readAsDataURL(e.target.files[0]);
45035     },
45036     
45037     // Bezier Point Constructor
45038     Point: (function () {
45039         function Point(x, y, time) {
45040             this.x = x;
45041             this.y = y;
45042             this.time = time || Date.now();
45043         }
45044         Point.prototype.distanceTo = function (start) {
45045             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45046         };
45047         Point.prototype.equals = function (other) {
45048             return this.x === other.x && this.y === other.y && this.time === other.time;
45049         };
45050         Point.prototype.velocityFrom = function (start) {
45051             return this.time !== start.time
45052             ? this.distanceTo(start) / (this.time - start.time)
45053             : 0;
45054         };
45055         return Point;
45056     }()),
45057     
45058     
45059     // Bezier Constructor
45060     Bezier: (function () {
45061         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45062             this.startPoint = startPoint;
45063             this.control2 = control2;
45064             this.control1 = control1;
45065             this.endPoint = endPoint;
45066             this.startWidth = startWidth;
45067             this.endWidth = endWidth;
45068         }
45069         Bezier.fromPoints = function (points, widths, scope) {
45070             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45071             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45072             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45073         };
45074         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45075             var dx1 = s1.x - s2.x;
45076             var dy1 = s1.y - s2.y;
45077             var dx2 = s2.x - s3.x;
45078             var dy2 = s2.y - s3.y;
45079             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45080             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45081             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45082             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45083             var dxm = m1.x - m2.x;
45084             var dym = m1.y - m2.y;
45085             var k = l2 / (l1 + l2);
45086             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45087             var tx = s2.x - cm.x;
45088             var ty = s2.y - cm.y;
45089             return {
45090                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45091                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45092             };
45093         };
45094         Bezier.prototype.length = function () {
45095             var steps = 10;
45096             var length = 0;
45097             var px;
45098             var py;
45099             for (var i = 0; i <= steps; i += 1) {
45100                 var t = i / steps;
45101                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45102                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45103                 if (i > 0) {
45104                     var xdiff = cx - px;
45105                     var ydiff = cy - py;
45106                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45107                 }
45108                 px = cx;
45109                 py = cy;
45110             }
45111             return length;
45112         };
45113         Bezier.prototype.point = function (t, start, c1, c2, end) {
45114             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45115             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45116             + (3.0 * c2 * (1.0 - t) * t * t)
45117             + (end * t * t * t);
45118         };
45119         return Bezier;
45120     }()),
45121     
45122     throttleStroke: function(fn, wait) {
45123       if (wait === void 0) { wait = 250; }
45124       var previous = 0;
45125       var timeout = null;
45126       var result;
45127       var storedContext;
45128       var storedArgs;
45129       var later = function () {
45130           previous = Date.now();
45131           timeout = null;
45132           result = fn.apply(storedContext, storedArgs);
45133           if (!timeout) {
45134               storedContext = null;
45135               storedArgs = [];
45136           }
45137       };
45138       return function wrapper() {
45139           var args = [];
45140           for (var _i = 0; _i < arguments.length; _i++) {
45141               args[_i] = arguments[_i];
45142           }
45143           var now = Date.now();
45144           var remaining = wait - (now - previous);
45145           storedContext = this;
45146           storedArgs = args;
45147           if (remaining <= 0 || remaining > wait) {
45148               if (timeout) {
45149                   clearTimeout(timeout);
45150                   timeout = null;
45151               }
45152               previous = now;
45153               result = fn.apply(storedContext, storedArgs);
45154               if (!timeout) {
45155                   storedContext = null;
45156                   storedArgs = [];
45157               }
45158           }
45159           else if (!timeout) {
45160               timeout = window.setTimeout(later, remaining);
45161           }
45162           return result;
45163       };
45164   }
45165   
45166 });
45167
45168  
45169
45170  // old names for form elements
45171 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45172 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45173 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45174 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45175 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45176 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45177 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45178 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45179 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45180 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45181 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45182 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45183 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45184 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45185 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45186 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45187 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45188 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45189 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45190 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45191 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45192 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45193 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45194 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45195 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45196 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45197
45198 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45199 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45200
45201 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45202 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45203
45204 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45205 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45206 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45207 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45208